From 74e4909a77ac143ecdb1d038ad182aae9c710129 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Wed, 3 Jul 2019 22:15:25 -0400
Subject: implemented directory import routine
---
src/client/documents/Documents.ts | 57 ++++++++-
src/client/util/Import & Export/ImageImporter.tsx | 67 +++++++++++
src/client/util/Import & Export/ImportBox.tsx | 134 +++++++++++++++++++++
src/client/util/SelectionManager.ts | 2 +-
src/client/views/MainView.tsx | 3 +
src/client/views/collections/CollectionSubView.tsx | 46 +------
src/client/views/nodes/DocumentContentsView.tsx | 3 +-
7 files changed, 265 insertions(+), 47 deletions(-)
create mode 100644 src/client/util/Import & Export/ImageImporter.tsx
create mode 100644 src/client/util/Import & Export/ImportBox.tsx
(limited to 'src')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 7d7a1f02a..5d637dd3a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -36,6 +36,7 @@ import { UndoManager } from "../util/UndoManager";
import { RouteStore } from "../../server/RouteStore";
import { LinkManager } from "../util/LinkManager";
import { DocumentManager } from "../util/DocumentManager";
+import ImportBox from "../util/Import & Export/ImportBox";
var requestImageSize = require('../util/request-image-size');
var path = require('path');
@@ -51,7 +52,8 @@ export enum DocTypes {
KVP = "kvp",
VID = "video",
AUDIO = "audio",
- LINK = "link"
+ LINK = "link",
+ IMPORT = "import"
}
export interface DocumentOptions {
@@ -127,6 +129,7 @@ export namespace Docs {
let audioProto: Doc;
let pdfProto: Doc;
let iconProto: Doc;
+ let importProto: Doc;
// let linkProto: Doc;
const textProtoId = "textProto";
const histoProtoId = "histoProto";
@@ -138,6 +141,7 @@ export namespace Docs {
const videoProtoId = "videoProto";
const audioProtoId = "audioProto";
const iconProtoId = "iconProto";
+ const importProtoId = "importProto";
// const linkProtoId = "linkProto";
export function initProtos(): Promise {
@@ -152,6 +156,7 @@ export namespace Docs {
audioProto = fields[audioProtoId] as Doc || CreateAudioPrototype();
pdfProto = fields[pdfProtoId] as Doc || CreatePdfPrototype();
iconProto = fields[iconProtoId] as Doc || CreateIconPrototype();
+ importProto = fields[importProtoId] as Doc || CreateImportPrototype();
});
}
@@ -174,6 +179,11 @@ export namespace Docs {
return imageProto;
}
+ function CreateImportPrototype(): Doc {
+ let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
+ return importProto;
+ }
+
function CreateHistogramPrototype(): Doc {
let histoProto = setupPrototypeOptions(histoProtoId, "HISTO PROTO", CollectionView.LayoutString("annotations"),
{ x: 0, y: 0, width: 300, height: 300, backgroundColor: "black", backgroundLayout: HistogramBox.LayoutString(), type: DocTypes.HIST });
@@ -261,6 +271,10 @@ export namespace Docs {
return CreateInstance(audioProto, new AudioField(new URL(url)), options);
}
+ export function DirectoryImportDocument(options: DocumentOptions = {}) {
+ return CreateInstance(importProto, "", options);
+ }
+
export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) {
return CreateInstance(histoProto, new HistogramField(histoOp), options);
}
@@ -333,6 +347,47 @@ export namespace Docs {
return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
}
+ export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> {
+ let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined;
+ if (type.indexOf("image") !== -1) {
+ ctor = Docs.ImageDocument;
+ }
+ if (type.indexOf("video") !== -1) {
+ ctor = Docs.VideoDocument;
+ }
+ if (type.indexOf("audio") !== -1) {
+ ctor = Docs.AudioDocument;
+ }
+ if (type.indexOf("pdf") !== -1) {
+ ctor = Docs.PdfDocument;
+ options.nativeWidth = 1200;
+ }
+ if (type.indexOf("excel") !== -1) {
+ ctor = Docs.DBDocument;
+ options.dropAction = "copy";
+ }
+ if (type.indexOf("html") !== -1) {
+ if (path.includes(window.location.hostname)) {
+ let s = path.split('/');
+ let id = s[s.length - 1];
+ DocServer.GetRefField(id).then(field => {
+ if (field instanceof Doc) {
+ let alias = Doc.MakeAlias(field);
+ alias.x = options.x || 0;
+ alias.y = options.y || 0;
+ alias.width = options.width || 300;
+ alias.height = options.height || options.width || 300;
+ addDocument && addDocument(alias, false);
+ }
+ });
+ return undefined;
+ }
+ ctor = Docs.WebDocument;
+ options = { height: options.width, ...options, title: path, nativeWidth: undefined };
+ }
+ return ctor ? ctor(path, options) : undefined;
+ }
+
export function CaptionDocument(doc: Doc) {
const captionDoc = Doc.MakeAlias(doc);
captionDoc.overlayLayout = FixedCaption();
diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx
new file mode 100644
index 000000000..d664f6487
--- /dev/null
+++ b/src/client/util/Import & Export/ImageImporter.tsx
@@ -0,0 +1,67 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action } from "mobx";
+import { Docs } from "../../documents/Documents";
+import { FieldViewProps } from "../../views/nodes/FieldView";
+
+interface ImageImporterProps {
+ addSchema: (imageDocs: Doc[]) => void;
+}
+
+export default class BulkImporter extends React.Component {
+ private selector = React.createRef();
+
+ handleSelection = async (e: React.ChangeEvent) => {
+ let promises: Promise[] = [];
+ let docs: Doc[] = [];
+
+ let files = e.target.files;
+ if (!files) return;
+
+ for (let i = 0; i < files.length; i++) {
+ let target = files.item(i);
+
+ if (target === null) {
+ continue;
+ }
+
+ let type = target.type;
+ let formData = new FormData();
+ formData.append('file', target);
+ let dropFileName = target ? target.name : "-empty-";
+
+ let prom = fetch(DocServer.prepend(RouteStore.upload), {
+ method: 'POST',
+ body: formData
+ }).then(async (res: Response) => {
+ (await res.json()).map(action((file: any) => {
+ let path = window.location.origin + file;
+ let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+ docPromise.then(doc => doc && docs.push(doc));
+ }));
+ });
+ promises.push(prom);
+ }
+
+ await Promise.all(promises);
+
+ let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" });
+ }
+
+ componentDidMount() {
+ this.selector.current!.setAttribute("directory", "true");
+ this.selector.current!.setAttribute("webkitdirectory", "true");
+ }
+
+ render() {
+ return (
+
+
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx
new file mode 100644
index 000000000..630911710
--- /dev/null
+++ b/src/client/util/Import & Export/ImportBox.tsx
@@ -0,0 +1,134 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action, observable } from "mobx";
+import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
+import Measure, { ContentRect } from "react-measure";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowUp } from '@fortawesome/free-solid-svg-icons';
+import { Docs, DocumentOptions } from "../../documents/Documents";
+
+interface ImageImporterProps {
+ addSchema: (imageDocs: Doc[]) => void;
+}
+
+export default class ImportBox extends React.Component {
+ @observable private top = 0;
+ @observable private left = 0;
+ private dimensions = 50;
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ library.add(faArrowUp);
+ }
+
+ public static LayoutString() { return FieldView.LayoutString(ImportBox); }
+
+ private selector = React.createRef();
+
+ handleSelection = async (e: React.ChangeEvent) => {
+ let promises: Promise[] = [];
+ let docs: Doc[] = [];
+
+ let files = e.target.files;
+ if (!files || files.length === 0) return;
+
+ let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+
+ for (let i = 0; i < files.length; i++) {
+ let uploaded_file = files.item(i);
+
+ if (!uploaded_file) {
+ continue;
+ }
+
+ let formData = new FormData();
+ formData.append('file', uploaded_file);
+ let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
+ let type = uploaded_file.type;
+
+ let prom = fetch(DocServer.prepend(RouteStore.upload), {
+ method: 'POST',
+ body: formData
+ }).then(async (res: Response) => {
+ (await res.json()).map(action((file: any) => {
+ let path = DocServer.prepend(file);
+ let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+ docPromise.then(doc => doc && docs.push(doc));
+ }));
+ });
+ promises.push(prom);
+ }
+
+ await Promise.all(promises);
+
+ let doc = this.props.Document;
+ let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
+ let parent = this.props.ContainingCollectionView;
+ if (parent) {
+ let importContainer = Docs.StackingDocument(docs, options);
+ Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
+ this.props.removeDocument && this.props.removeDocument(doc);
+ }
+ }
+
+ componentDidMount() {
+ this.selector.current!.setAttribute("directory", "true");
+ this.selector.current!.setAttribute("webkitdirectory", "true");
+ }
+
+ @action
+ preserveCentering = (rect: ContentRect) => {
+ let bounds = rect.offset!;
+ if (bounds.width === 0 || bounds.height === 0) {
+ return;
+ }
+ let offset = this.dimensions / 2;
+ this.left = bounds.width / 2 - offset;
+ this.top = bounds.height / 2 - offset;
+ }
+
+ render() {
+ let dimensions = 50;
+ return (
+
+ {({ measureRef }) =>
+
+ }
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index 3c396362e..9efef888d 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -53,7 +53,7 @@ export namespace SelectionManager {
stored.length > 0 && (targetColor = stored);
}
InkingControl.Instance.updateSelectedColor(targetColor);
- });
+ }, { fireImmediately: true });
export function DeselectDoc(docView: DocumentView): void {
manager.DeselectDoc(docView);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ab9fe0118..e38fd3c8b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,6 +38,7 @@ import PDFMenu from './pdf/PDFMenu';
import { InkTool } from '../../new_fields/InkField';
import _ from "lodash";
import KeyManager from './GlobalKeyHandler';
+import BulkImporter from '../util/Import & Export/ImageImporter';
@observer
export class MainView extends React.Component {
@@ -351,11 +352,13 @@ export class MainView extends React.Component {
let addColNode = action(() => Docs.FreeformDocument([], { width: this.pwidth * .7, height: this.pheight, title: "a freeform collection" }));
let addTreeNode = action(() => CurrentUserUtils.UserDocument);
let addImageNode = action(() => Docs.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
+ let addImportCollectionNode = action(() => Docs.DirectoryImportDocument({ title: "Directory Import", width: 150, height: 150 }));
let btns: [React.RefObject, IconName, string, () => Doc][] = [
[React.createRef(), "image", "Add Image", addImageNode],
[React.createRef(), "object-group", "Add Collection", addColNode],
[React.createRef(), "tree", "Add Tree", addTreeNode],
+ [React.createRef(), "arrow-up", "Import Directory", addImportCollectionNode],
];
return < div id="add-nodes-menu" >
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 3b3bbdbd9..e886ad84a 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -102,47 +102,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
return false;
}
- protected async getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise> {
- let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined;
- if (type.indexOf("image") !== -1) {
- ctor = Docs.ImageDocument;
- }
- if (type.indexOf("video") !== -1) {
- ctor = Docs.VideoDocument;
- }
- if (type.indexOf("audio") !== -1) {
- ctor = Docs.AudioDocument;
- }
- if (type.indexOf("pdf") !== -1) {
- ctor = Docs.PdfDocument;
- options.nativeWidth = 1200;
- }
- if (type.indexOf("excel") !== -1) {
- ctor = Docs.DBDocument;
- options.dropAction = "copy";
- }
- if (type.indexOf("html") !== -1) {
- if (path.includes(window.location.hostname)) {
- let s = path.split('/');
- let id = s[s.length - 1];
- DocServer.GetRefField(id).then(field => {
- if (field instanceof Doc) {
- let alias = Doc.MakeAlias(field);
- alias.x = options.x || 0;
- alias.y = options.y || 0;
- alias.width = options.width || 300;
- alias.height = options.height || options.width || 300;
- this.props.addDocument(alias, false);
- }
- });
- return undefined;
- }
- ctor = Docs.WebDocument;
- options = { height: options.width, ...options, title: path, nativeWidth: undefined };
- }
- return ctor ? ctor(path, options) : undefined;
- }
-
@undoBatch
@action
protected onDrop(e: React.DragEvent, options: DocumentOptions): void {
@@ -223,7 +182,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
.then(result => {
let type = result["content-type"];
if (type) {
- this.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
+ Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }, this.props.addDocument)
.then(doc => doc && this.props.addDocument(doc, false));
}
});
@@ -245,8 +204,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
let path = window.location.origin + file;
- let docPromise = this.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
-
+ let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName }, this.props.addDocument);
docPromise.then(doc => doc && this.props.addDocument(doc));
}));
});
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 0da4888a1..e9fec1588 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,6 +24,7 @@ import { Without, OmitKeys } from "../../../Utils";
import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
import { Doc } from "../../../new_fields/Doc";
+import ImportBox from "../../util/Import & Export/ImportBox";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without;
@@ -106,7 +107,7 @@ export class DocumentContentsView extends React.Component 7) return (null);
if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return
Date: Fri, 5 Jul 2019 00:34:19 -0400
Subject: beginning implementation of key value tags for all imported documents
---
src/client/documents/Documents.ts | 12 +-
.../util/Import & Export/DirectoryImportBox.scss | 0
.../util/Import & Export/DirectoryImportBox.tsx | 181 +++++++++++++++++++++
src/client/util/Import & Export/ImageImporter.tsx | 67 --------
src/client/util/Import & Export/ImportBox.tsx | 134 ---------------
src/client/util/request-image-size.js | 6 +-
src/client/views/MainView.tsx | 1 -
src/client/views/collections/CollectionSubView.tsx | 4 +-
src/client/views/nodes/DocumentContentsView.tsx | 4 +-
src/client/views/nodes/ImageBox.tsx | 4 +-
10 files changed, 198 insertions(+), 215 deletions(-)
create mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss
create mode 100644 src/client/util/Import & Export/DirectoryImportBox.tsx
delete mode 100644 src/client/util/Import & Export/ImageImporter.tsx
delete mode 100644 src/client/util/Import & Export/ImportBox.tsx
(limited to 'src')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5d637dd3a..26b5498a2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -36,7 +36,7 @@ import { UndoManager } from "../util/UndoManager";
import { RouteStore } from "../../server/RouteStore";
import { LinkManager } from "../util/LinkManager";
import { DocumentManager } from "../util/DocumentManager";
-import ImportBox from "../util/Import & Export/ImportBox";
+import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
var requestImageSize = require('../util/request-image-size');
var path = require('path');
@@ -180,7 +180,7 @@ export namespace Docs {
}
function CreateImportPrototype(): Doc {
- let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", ImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
+ let importProto = setupPrototypeOptions(importProtoId, "IMPORT_PROTO", DirectoryImportBox.LayoutString(), { x: 0, y: 0, width: 600, height: 600, type: DocTypes.IMPORT });
return importProto;
}
@@ -347,7 +347,7 @@ export namespace Docs {
return CreateInstance(collProto, new List(documents), { ...options, viewType: CollectionViewType.Docking, dockingConfig: config }, id);
}
- export async function getDocumentFromType(type: string, path: string, options: DocumentOptions, addDocument?: (document: Doc, allowDuplicates?: boolean) => boolean): Promise> {
+ export async function getDocumentFromType(type: string, path: string, options: DocumentOptions): Promise> {
let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise)) | undefined = undefined;
if (type.indexOf("image") !== -1) {
ctor = Docs.ImageDocument;
@@ -370,17 +370,17 @@ export namespace Docs {
if (path.includes(window.location.hostname)) {
let s = path.split('/');
let id = s[s.length - 1];
- DocServer.GetRefField(id).then(field => {
+ return DocServer.GetRefField(id).then(field => {
if (field instanceof Doc) {
let alias = Doc.MakeAlias(field);
alias.x = options.x || 0;
alias.y = options.y || 0;
alias.width = options.width || 300;
alias.height = options.height || options.width || 300;
- addDocument && addDocument(alias, false);
+ return alias;
}
+ return undefined;
});
- return undefined;
}
ctor = Docs.WebDocument;
options = { height: options.width, ...options, title: path, nativeWidth: undefined };
diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
new file mode 100644
index 000000000..2d77f6ae6
--- /dev/null
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -0,0 +1,181 @@
+import "fs";
+import React = require("react");
+import { Doc } from "../../../new_fields/Doc";
+import { DocServer } from "../../DocServer";
+import { RouteStore } from "../../../server/RouteStore";
+import { action, observable, runInAction } from "mobx";
+import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
+import Measure, { ContentRect } from "react-measure";
+import { library } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons';
+import { Docs, DocumentOptions } from "../../documents/Documents";
+import { EditableView } from "../../views/EditableView";
+
+export default class DirectoryImportBox extends React.Component {
+ private selector = React.createRef();
+ @observable private top = 0;
+ @observable private left = 0;
+ private dimensions = 50;
+
+ @observable private key = "Key";
+ @observable private value = "Value";
+
+ public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
+
+ constructor(props: FieldViewProps) {
+ super(props);
+ library.add(faArrowUp, faTag);
+ }
+
+ updateKey = (newKey: string) => {
+ runInAction(() => this.key = newKey);
+ console.log("KEY ", this.key);
+ return true;
+ }
+
+ updateValue = (newValue: string) => {
+ runInAction(() => this.value = newValue);
+ console.log("VALUE ", this.value);
+ return true;
+ }
+
+ handleSelection = async (e: React.ChangeEvent) => {
+ let promises: Promise[] = [];
+ let docs: Doc[] = [];
+
+ let files = e.target.files;
+ if (!files || files.length === 0) return;
+
+ let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+
+ for (let i = 0; i < files.length; i++) {
+ let uploaded_file = files.item(i);
+
+ if (!uploaded_file) {
+ continue;
+ }
+
+ let formData = new FormData();
+ formData.append('file', uploaded_file);
+ let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
+ let type = uploaded_file.type;
+
+ let prom = fetch(DocServer.prepend(RouteStore.upload), {
+ method: 'POST',
+ body: formData
+ }).then(async (res: Response) => {
+ (await res.json()).map(action((file: any) => {
+ let path = DocServer.prepend(file);
+ console.log(path);
+ let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
+ docPromise.then(doc => doc && docs.push(doc));
+ }));
+ });
+ promises.push(prom);
+ }
+
+ await Promise.all(promises);
+
+ let doc = this.props.Document;
+ let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
+ let parent = this.props.ContainingCollectionView;
+ if (parent) {
+ let importContainer = Docs.StackingDocument(docs, options);
+ Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
+ this.props.removeDocument && this.props.removeDocument(doc);
+ }
+ }
+
+ componentDidMount() {
+ this.selector.current!.setAttribute("directory", "");
+ this.selector.current!.setAttribute("webkitdirectory", "");
+ }
+
+ @action
+ preserveCentering = (rect: ContentRect) => {
+ let bounds = rect.offset!;
+ if (bounds.width === 0 || bounds.height === 0) {
+ return;
+ }
+ let offset = this.dimensions / 2;
+ this.left = bounds.width / 2 - offset;
+ this.top = bounds.height / 2 - offset;
+ }
+
+ render() {
+ let dimensions = 50;
+ let keyValueStyle = { paddingLeft: 5, width: "50%" };
+ return (
+
+ {({ measureRef }) =>
+
+
+
+
+
+
+
+
+
+ this.key}
+ oneLine={true}
+ />
+
+
+ this.value}
+ oneLine={true}
+ />
+
+
+
+ }
+
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImageImporter.tsx b/src/client/util/Import & Export/ImageImporter.tsx
deleted file mode 100644
index d664f6487..000000000
--- a/src/client/util/Import & Export/ImageImporter.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import "fs";
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
-import { RouteStore } from "../../../server/RouteStore";
-import { action } from "mobx";
-import { Docs } from "../../documents/Documents";
-import { FieldViewProps } from "../../views/nodes/FieldView";
-
-interface ImageImporterProps {
- addSchema: (imageDocs: Doc[]) => void;
-}
-
-export default class BulkImporter extends React.Component {
- private selector = React.createRef();
-
- handleSelection = async (e: React.ChangeEvent) => {
- let promises: Promise[] = [];
- let docs: Doc[] = [];
-
- let files = e.target.files;
- if (!files) return;
-
- for (let i = 0; i < files.length; i++) {
- let target = files.item(i);
-
- if (target === null) {
- continue;
- }
-
- let type = target.type;
- let formData = new FormData();
- formData.append('file', target);
- let dropFileName = target ? target.name : "-empty-";
-
- let prom = fetch(DocServer.prepend(RouteStore.upload), {
- method: 'POST',
- body: formData
- }).then(async (res: Response) => {
- (await res.json()).map(action((file: any) => {
- let path = window.location.origin + file;
- let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
- docPromise.then(doc => doc && docs.push(doc));
- }));
- });
- promises.push(prom);
- }
-
- await Promise.all(promises);
-
- let parent = Docs.SchemaDocument(["title", "data"], docs, { width: 300, height: 300, title: "Bulk Import from Directory" });
- }
-
- componentDidMount() {
- this.selector.current!.setAttribute("directory", "true");
- this.selector.current!.setAttribute("webkitdirectory", "true");
- }
-
- render() {
- return (
-
-
-
- );
- }
-
-}
\ No newline at end of file
diff --git a/src/client/util/Import & Export/ImportBox.tsx b/src/client/util/Import & Export/ImportBox.tsx
deleted file mode 100644
index 630911710..000000000
--- a/src/client/util/Import & Export/ImportBox.tsx
+++ /dev/null
@@ -1,134 +0,0 @@
-import "fs";
-import React = require("react");
-import { Doc } from "../../../new_fields/Doc";
-import { DocServer } from "../../DocServer";
-import { RouteStore } from "../../../server/RouteStore";
-import { action, observable } from "mobx";
-import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
-import Measure, { ContentRect } from "react-measure";
-import { library } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faArrowUp } from '@fortawesome/free-solid-svg-icons';
-import { Docs, DocumentOptions } from "../../documents/Documents";
-
-interface ImageImporterProps {
- addSchema: (imageDocs: Doc[]) => void;
-}
-
-export default class ImportBox extends React.Component {
- @observable private top = 0;
- @observable private left = 0;
- private dimensions = 50;
-
- constructor(props: FieldViewProps) {
- super(props);
- library.add(faArrowUp);
- }
-
- public static LayoutString() { return FieldView.LayoutString(ImportBox); }
-
- private selector = React.createRef();
-
- handleSelection = async (e: React.ChangeEvent) => {
- let promises: Promise[] = [];
- let docs: Doc[] = [];
-
- let files = e.target.files;
- if (!files || files.length === 0) return;
-
- let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
-
- for (let i = 0; i < files.length; i++) {
- let uploaded_file = files.item(i);
-
- if (!uploaded_file) {
- continue;
- }
-
- let formData = new FormData();
- formData.append('file', uploaded_file);
- let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
- let type = uploaded_file.type;
-
- let prom = fetch(DocServer.prepend(RouteStore.upload), {
- method: 'POST',
- body: formData
- }).then(async (res: Response) => {
- (await res.json()).map(action((file: any) => {
- let path = DocServer.prepend(file);
- let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
- docPromise.then(doc => doc && docs.push(doc));
- }));
- });
- promises.push(prom);
- }
-
- await Promise.all(promises);
-
- let doc = this.props.Document;
- let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
- let parent = this.props.ContainingCollectionView;
- if (parent) {
- let importContainer = Docs.StackingDocument(docs, options);
- Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
- this.props.removeDocument && this.props.removeDocument(doc);
- }
- }
-
- componentDidMount() {
- this.selector.current!.setAttribute("directory", "true");
- this.selector.current!.setAttribute("webkitdirectory", "true");
- }
-
- @action
- preserveCentering = (rect: ContentRect) => {
- let bounds = rect.offset!;
- if (bounds.width === 0 || bounds.height === 0) {
- return;
- }
- let offset = this.dimensions / 2;
- this.left = bounds.width / 2 - offset;
- this.top = bounds.height / 2 - offset;
- }
-
- render() {
- let dimensions = 50;
- return (
-
- {({ measureRef }) =>
-
- }
-
- );
- }
-
-}
\ No newline at end of file
diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js
index 0f9328872..27605d167 100644
--- a/src/client/util/request-image-size.js
+++ b/src/client/util/request-image-size.js
@@ -21,7 +21,9 @@ module.exports = function requestImageSize(options) {
if (options && typeof options === 'object') {
opts = Object.assign(options, opts);
} else if (options && typeof options === 'string') {
- opts = Object.assign({ uri: options }, opts);
+ opts = Object.assign({
+ uri: options
+ }, opts);
} else {
return Promise.reject(new Error('You should provide an URI string or a "request" options object.'));
}
@@ -70,4 +72,4 @@ module.exports = function requestImageSize(options) {
req.on('error', err => reject(err));
});
-};
+};
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index e38fd3c8b..3d059b2f3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -38,7 +38,6 @@ import PDFMenu from './pdf/PDFMenu';
import { InkTool } from '../../new_fields/InkField';
import _ from "lodash";
import KeyManager from './GlobalKeyHandler';
-import BulkImporter from '../util/Import & Export/ImageImporter';
@observer
export class MainView extends React.Component {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index e886ad84a..8cb2e1beb 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -182,7 +182,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
.then(result => {
let type = result["content-type"];
if (type) {
- Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 }, this.props.addDocument)
+ Docs.getDocumentFromType(type, str, { ...options, width: 300, nativeWidth: 300 })
.then(doc => doc && this.props.addDocument(doc, false));
}
});
@@ -204,7 +204,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
let path = window.location.origin + file;
- let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName }, this.props.addDocument);
+ let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
docPromise.then(doc => doc && this.props.addDocument(doc));
}));
});
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index e9fec1588..0bfb2954f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -24,7 +24,7 @@ import { Without, OmitKeys } from "../../../Utils";
import { Cast, StrCast, NumCast } from "../../../new_fields/Types";
import { List } from "../../../new_fields/List";
import { Doc } from "../../../new_fields/Doc";
-import ImportBox from "../../util/Import & Export/ImportBox";
+import DirectoryImportBox from "../../util/Import & Export/DirectoryImportBox";
const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
type BindingProps = Without;
@@ -107,7 +107,7 @@ export class DocumentContentsView extends React.Component 7) return (null);
if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return (ImageD
}), 0);
}
})
- .catch((err: any) => console.log(err));
+ .catch((err: any) => {
+ console.log(err);
+ });
}
render() {
--
cgit v1.2.3-70-g09d2
From 12788cbe0586d0349f70b73d1b6f6a481b4bf2cf Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Fri, 5 Jul 2019 17:31:20 -0400
Subject: first pass at implementation of directory import
---
deploy/assets/loading.gif | Bin 0 -> 114208 bytes
.../util/Import & Export/DirectoryImportBox.scss | 0
.../util/Import & Export/DirectoryImportBox.tsx | 194 +++++++++++++++------
src/client/util/Import & Export/KeyValue.tsx | 79 +++++++++
src/client/views/MainView.tsx | 2 +-
5 files changed, 217 insertions(+), 58 deletions(-)
create mode 100644 deploy/assets/loading.gif
delete mode 100644 src/client/util/Import & Export/DirectoryImportBox.scss
create mode 100644 src/client/util/Import & Export/KeyValue.tsx
(limited to 'src')
diff --git a/deploy/assets/loading.gif b/deploy/assets/loading.gif
new file mode 100644
index 000000000..0652bf021
Binary files /dev/null and b/deploy/assets/loading.gif differ
diff --git a/src/client/util/Import & Export/DirectoryImportBox.scss b/src/client/util/Import & Export/DirectoryImportBox.scss
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 2d77f6ae6..cbc8c6fdc 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -3,44 +3,46 @@ import React = require("react");
import { Doc } from "../../../new_fields/Doc";
import { DocServer } from "../../DocServer";
import { RouteStore } from "../../../server/RouteStore";
-import { action, observable, runInAction } from "mobx";
+import { action, observable, autorun, runInAction } from "mobx";
import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
import Measure, { ContentRect } from "react-measure";
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { faArrowUp, faTag, faFileExcel } from '@fortawesome/free-solid-svg-icons';
+import { faArrowUp, faTag, faPlus } from '@fortawesome/free-solid-svg-icons';
import { Docs, DocumentOptions } from "../../documents/Documents";
-import { EditableView } from "../../views/EditableView";
+import { observer } from "mobx-react";
+import KeyValue from "./KeyValue";
+import { Utils } from "../../../Utils";
+import { doesNotReject } from "assert";
+import { remove } from "typescript-collections/dist/lib/arrays";
+@observer
export default class DirectoryImportBox extends React.Component {
private selector = React.createRef();
@observable private top = 0;
@observable private left = 0;
private dimensions = 50;
- @observable private key = "Key";
- @observable private value = "Value";
+ @observable private editingMetadata = false;
+ @observable private metadata_guids: string[] = [];
+ @observable private entries: KeyValue[] = [];
+
+ @observable private quota = 1;
+ @observable private remaining = 1;
+
+ @observable private uploadBegun = false;
public static LayoutString() { return FieldView.LayoutString(DirectoryImportBox); }
constructor(props: FieldViewProps) {
super(props);
- library.add(faArrowUp, faTag);
- }
-
- updateKey = (newKey: string) => {
- runInAction(() => this.key = newKey);
- console.log("KEY ", this.key);
- return true;
- }
-
- updateValue = (newValue: string) => {
- runInAction(() => this.value = newValue);
- console.log("VALUE ", this.value);
- return true;
+ library.add(faArrowUp, faTag, faPlus);
}
+ @action
handleSelection = async (e: React.ChangeEvent) => {
+ this.uploadBegun = true;
+
let promises: Promise[] = [];
let docs: Doc[] = [];
@@ -49,9 +51,15 @@ export default class DirectoryImportBox extends React.Component
let directory = (files.item(0) as any).webkitRelativePath.split("/", 1);
+ let validated: File[] = [];
for (let i = 0; i < files.length; i++) {
- let uploaded_file = files.item(i);
+ let file = files.item(i);
+ file && validated.push(file);
+ }
+
+ this.quota = validated.length;
+ for (let uploaded_file of validated) {
if (!uploaded_file) {
continue;
}
@@ -61,15 +69,18 @@ export default class DirectoryImportBox extends React.Component
let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
let type = uploaded_file.type;
+ this.remaining++;
+
let prom = fetch(DocServer.prepend(RouteStore.upload), {
method: 'POST',
body: formData
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
let path = DocServer.prepend(file);
- console.log(path);
let docPromise = Docs.getDocumentFromType(type, path, { nativeWidth: 300, width: 300, title: dropFileName });
- docPromise.then(doc => doc && docs.push(doc));
+ docPromise.then(doc => {
+ doc && docs.push(doc) && runInAction(() => this.remaining--);
+ });
}));
});
promises.push(prom);
@@ -77,11 +88,14 @@ export default class DirectoryImportBox extends React.Component
await Promise.all(promises);
+ docs.forEach(doc => this.entries.forEach(entry => doc[entry.key] = entry.value));
+
let doc = this.props.Document;
let options: DocumentOptions = { title: `Import of ${directory}`, width: 500, height: 500, x: Doc.GetT(doc, "x", "number"), y: Doc.GetT(doc, "y", "number") };
let parent = this.props.ContainingCollectionView;
if (parent) {
let importContainer = Docs.StackingDocument(docs, options);
+ importContainer.singleColumn = false;
Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
this.props.removeDocument && this.props.removeDocument(doc);
}
@@ -103,9 +117,29 @@ export default class DirectoryImportBox extends React.Component
this.top = bounds.height / 2 - offset;
}
+ @action
+ addMetadataEntry = () => {
+ this.metadata_guids.push(Utils.GenerateGuid());
+ }
+
+ @action
+ remove = (entry: KeyValue) => {
+ let index = this.entries.indexOf(entry);
+ let key = entry.key;
+ this.entries.splice(index, 1);
+ this.metadata_guids.splice(this.metadata_guids.indexOf(key), 1);
+ }
+
render() {
let dimensions = 50;
- let keyValueStyle = { paddingLeft: 5, width: "50%" };
+ let guids = this.metadata_guids.map(el => el);
+ let isEditing = this.editingMetadata;
+ let remaining = this.remaining;
+ let quota = this.quota;
+ let percent = `${100 - (remaining / quota * 100)}`;
+ let uploadBegun = this.uploadBegun;
+ percent = percent.split(".")[0];
+ percent = percent.startsWith("100") ? "99" : percent;
return (
{({ measureRef }) =>
@@ -113,14 +147,20 @@ export default class DirectoryImportBox extends React.Component
-
+ }}>Template will be {persistent ? "kept" : "removed"} after upload
{percent}%
borderRadius: "50%",
width: 25,
height: 25,
- background: "black"
+ background: "black",
+ pointerEvents: uploading ? "none" : "all",
+ opacity: uploading ? 0 : 1,
+ transition: "0.4s opacity ease"
}}
title={isEditing ? "Back to Upload" : "Add Metadata"}
onClick={action(() => this.editingMetadata = !this.editingMetadata)}
@@ -263,7 +307,9 @@ export default class DirectoryImportBox extends React.Component
pointerEvents: "none",
position: "absolute",
right: isEditing ? 16.3 : 14.5,
- top: isEditing ? 15.4 : 16
+ top: isEditing ? 15.4 : 16,
+ opacity: uploading ? 0 : 1,
+ transition: "0.4s opacity ease"
}}
icon={isEditing ? faArrowUp : faTag}
color="#FFFFFF"
@@ -304,9 +350,10 @@ export default class DirectoryImportBox extends React.Component
Add metadata to your import...
- {guids.map(guid =>
+ {entries.map(doc =>
{ if (el) this.entries.push(el); }}
next={this.addMetadataEntry}
diff --git a/src/client/util/Import & Export/ImportMetadataEntry.tsx b/src/client/util/Import & Export/ImportMetadataEntry.tsx
index 3b2a6ebb5..f5198c39b 100644
--- a/src/client/util/Import & Export/ImportMetadataEntry.tsx
+++ b/src/client/util/Import & Export/ImportMetadataEntry.tsx
@@ -5,20 +5,20 @@ import { observable, action, computed } from "mobx";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { library } from '@fortawesome/fontawesome-svg-core';
-import { Opt } from "../../../new_fields/Doc";
+import { Opt, Doc } from "../../../new_fields/Doc";
+import { StrCast, BoolCast } from "../../../new_fields/Types";
interface KeyValueProps {
+ Document: Doc;
remove: (self: ImportMetadataEntry) => void;
next: () => void;
}
-const keyPlaceholder = "Key";
-const valuePlaceholder = "Value";
+export const keyPlaceholder = "Key";
+export const valuePlaceholder = "Value";
@observer
export default class ImportMetadataEntry extends React.Component {
- @observable public key = keyPlaceholder;
- @observable public value = valuePlaceholder;
private keyRef = React.createRef();
private valueRef = React.createRef();
@@ -34,8 +34,36 @@ export default class ImportMetadataEntry extends React.Component
return (this.key.length > 0 && this.key !== keyPlaceholder) && (this.value.length > 0 && this.value !== valuePlaceholder);
}
+ @computed
+ private get backing() {
+ return this.props.Document;
+ }
+
+ @computed
public get onDataDoc() {
- return this.checkRef.current && this.checkRef.current.checked;
+ return BoolCast(this.backing.checked);
+ }
+
+ public set onDataDoc(value: boolean) {
+ this.backing.checked = value;
+ }
+
+ @computed
+ public get key() {
+ return StrCast(this.backing.key);
+ }
+
+ public set key(value: string) {
+ this.backing.key = value;
+ }
+
+ @computed
+ public get value() {
+ return StrCast(this.backing.value);
+ }
+
+ public set value(value: string) {
+ this.backing.value = value;
}
@action
@@ -75,10 +103,12 @@ export default class ImportMetadataEntry extends React.Component
}}
>
this.onDataDoc = e.target.checked}
ref={this.checkRef}
style={{ margin: "0 10px 0 15px" }}
type="checkbox"
title={"Add to Data Document?"}
+ checked={this.onDataDoc}
/>
, document.getElementById('root'));
-})();
+})();
\ No newline at end of file
--
cgit v1.2.3-70-g09d2
From a1bbdbbcfb9ad704c42fae48bfe88a720803f756 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Sat, 6 Jul 2019 12:03:25 -0400
Subject: added embedding of size and modified time
---
.../util/Import & Export/DirectoryImportBox.tsx | 23 +++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
(limited to 'src')
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index b75944745..093cee168 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -19,6 +19,8 @@ import { List } from "../../../new_fields/List";
import { Cast, BoolCast, NumCast } from "../../../new_fields/Types";
import { listSpec } from "../../../new_fields/Schema";
+const unsupported = ["text/html", "text/plain"];
+
@observer
export default class DirectoryImportBox extends React.Component {
private selector = React.createRef();
@@ -77,21 +79,24 @@ export default class DirectoryImportBox extends React.Component
let validated: File[] = [];
for (let i = 0; i < files.length; i++) {
let file = files.item(i);
- file && validated.push(file);
+ console.log(file);
+ file && !unsupported.includes(file.type) && validated.push(file);
}
runInAction(() => this.quota = validated.length);
- for (let uploaded_file of validated) {
- if (!uploaded_file) {
- continue;
- }
+ let sizes = [];
+ let modifiedDates = [];
+ for (let uploaded_file of validated) {
let formData = new FormData();
formData.append('file', uploaded_file);
let dropFileName = uploaded_file ? uploaded_file.name : "-empty-";
let type = uploaded_file.type;
+ sizes.push(uploaded_file.size);
+ modifiedDates.push(uploaded_file.lastModified);
+
runInAction(() => this.remaining++);
let prom = fetch(DocServer.prepend(RouteStore.upload), {
@@ -99,6 +104,7 @@ export default class DirectoryImportBox extends React.Component
body: formData
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
+ console.log(file);
let docPromise = Docs.getDocumentFromType(type, DocServer.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
docPromise.then(doc => {
doc && docs.push(doc) && runInAction(() => this.remaining--);
@@ -110,12 +116,15 @@ export default class DirectoryImportBox extends React.Component
await Promise.all(promises);
- docs.forEach(doc => {
+ for (let i = 0; i < docs.length; i++) {
+ let doc = docs[i];
+ doc.size = sizes[i];
+ doc.modified = modifiedDates[i];
this.entries.forEach(entry => {
let target = entry.onDataDoc ? Doc.GetProto(doc) : doc;
target[entry.key] = entry.value;
});
- });
+ }
let doc = this.props.Document;
let height: number = NumCast(doc.height) || 0;
--
cgit v1.2.3-70-g09d2
From 5ce2d59b4544a7473a910c121daf0c34d1546214 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Sat, 6 Jul 2019 12:20:58 -0400
Subject: removed console.log
---
src/client/util/Import & Export/DirectoryImportBox.tsx | 1 -
1 file changed, 1 deletion(-)
(limited to 'src')
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 093cee168..1a939882c 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -104,7 +104,6 @@ export default class DirectoryImportBox extends React.Component
body: formData
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
- console.log(file);
let docPromise = Docs.getDocumentFromType(type, DocServer.prepend(file), { nativeWidth: 300, width: 300, title: dropFileName });
docPromise.then(doc => {
doc && docs.push(doc) && runInAction(() => this.remaining--);
--
cgit v1.2.3-70-g09d2
From 2066873ce1095c85013be880822d25df44e2e6b5 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Mon, 8 Jul 2019 11:18:27 -0400
Subject: import proto tweak
---
src/client/documents/Documents.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'src')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 26a6f202c..07812432c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -273,7 +273,7 @@ export namespace Docs {
}
export function DirectoryImportDocument(options: DocumentOptions = {}) {
- return CreateInstance(importProto, "", options);
+ return CreateInstance(importProto, new List(), options);
}
export function HistogramDocument(histoOp: HistogramOperation, options: DocumentOptions = {}) {
--
cgit v1.2.3-70-g09d2
From 04887c8a578147015421d3909bd100c82ac5e31d Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Mon, 8 Jul 2019 19:55:15 -0400
Subject: fixed readonly and hopefully cursor proto bugs
---
src/client/DocServer.ts | 4 +-
.../util/Import & Export/DirectoryImportBox.tsx | 1 -
src/client/views/collections/CollectionSubView.tsx | 9 ++-
src/client/views/nodes/KeyValueBox.tsx | 81 +++++++++++++++-------
4 files changed, 66 insertions(+), 29 deletions(-)
(limited to 'src')
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index cbcf751ee..652a9b701 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -12,7 +12,9 @@ export namespace DocServer {
const GUID: string = Utils.GenerateGuid();
export function makeReadOnly() {
- _CreateField = emptyFunction;
+ _CreateField = field => {
+ _cache[field[Id]] = field;
+ };
_UpdateField = emptyFunction;
_respondToUpdate = emptyFunction;
}
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 1a939882c..ce95ba90e 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx
@@ -79,7 +79,6 @@ export default class DirectoryImportBox extends React.Component
let validated: File[] = [];
for (let i = 0; i < files.length; i++) {
let file = files.item(i);
- console.log(file);
file && !unsupported.includes(file.type) && validated.push(file);
}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index bf17088ae..a8810f336 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -19,6 +19,7 @@ import { CollectionPDFView } from "./CollectionPDFView";
import { CollectionVideoView } from "./CollectionVideoView";
import { CollectionView } from "./CollectionView";
import React = require("react");
+import { MainView } from "../MainView";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -67,10 +68,16 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
let email = CurrentUserUtils.email;
let pos = { x: position[0], y: position[1] };
if (id && email) {
- const proto = await doc.proto;
+ const proto = Doc.GetProto(doc);
if (!proto) {
return;
}
+ if (proto[Id] === "collectionProto") {
+ alert("COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...");
+ console.log(doc);
+ console.log(proto);
+ throw new Error(`AHA! You were trying to set a cursor on a collection's proto, which is the original collection proto! Look at the two previously printed lines for document values!`);
+ }
let cursors = Cast(proto.cursors, listSpec(CursorField));
if (!cursors) {
proto.cursors = cursors = new List();
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 9407d742c..fbabe224e 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -18,6 +18,7 @@ import { List } from "../../../new_fields/List";
import { TextField } from "../../util/ProsemirrorCopy/prompt";
import { RichTextField } from "../../../new_fields/RichTextField";
import { ImageField } from "../../../new_fields/URLField";
+import { SelectionManager } from "../../util/SelectionManager";
@observer
export class KeyValueBox extends React.Component {
@@ -167,44 +168,72 @@ export class KeyValueBox extends React.Component {
return parent;
}
- createTemplateField = async (parent: Doc, row: KeyValuePair) => {
- let collectionKeyProp = `fieldKey={"data"}`;
- let metaKey = row.props.keyName;
- let metaKeyProp = `fieldKey={"${metaKey}"}`;
+ createTemplateField = async (parentStackingDoc: Doc, row: KeyValuePair) => {
+ // let collectionKeyProp = `fieldKey={"data"}`;
+ // let metaKey = row.props.keyName;
+ // let metaKeyProp = `fieldKey={"${metaKey}"}`;
+
+ // let sourceDoc = await Cast(this.props.Document.data, Doc);
+ // if (!sourceDoc) {
+ // return;
+ // }
+ // let target = this.inferType(sourceDoc[metaKey], metaKey);
+ // let template = Doc.MakeAlias(target);
+ // template.proto = parent;
+ // template.title = metaKey;
+ // template.nativeWidth = 0;
+ // template.nativeHeight = 0;
+ // template.embed = true;
+ // template.isTemplate = true;
+ // template.templates = new List([Templates.TitleBar(metaKey)]);
+ // if (target.backgroundLayout) {
+ // let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`;
+ // let collectionAnoKeyProp = `fieldKey={"annotations"}`;
+ // template.layout = StrCast(target.layout).replace(collectionAnoKeyProp, metaAnoKeyProp);
+ // template.backgroundLayout = StrCast(target.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
+ // } else {
+ // template.layout = StrCast(target.layout).replace(collectionKeyProp, metaKeyProp);
+ // }
+
+ let metaKey = row.props.keyName;
let sourceDoc = await Cast(this.props.Document.data, Doc);
if (!sourceDoc) {
return;
}
- let target = this.inferType(sourceDoc[metaKey], metaKey);
-
- let template = Doc.MakeAlias(target);
- template.proto = parent;
- template.title = metaKey;
- template.nativeWidth = 0;
- template.nativeHeight = 0;
- template.embed = true;
- template.isTemplate = true;
- template.templates = new List([Templates.TitleBar(metaKey)]);
- if (target.backgroundLayout) {
- let metaAnoKeyProp = `fieldKey={"${metaKey}"} fieldExt={"annotations"}`;
- let collectionAnoKeyProp = `fieldKey={"annotations"}`;
- template.layout = StrCast(target.layout).replace(collectionAnoKeyProp, metaAnoKeyProp);
- template.backgroundLayout = StrCast(target.backgroundLayout).replace(collectionKeyProp, metaKeyProp);
- } else {
- template.layout = StrCast(target.layout).replace(collectionKeyProp, metaKeyProp);
+ let fieldTemplate = this.inferType(sourceDoc[metaKey], metaKey);
+
+ // move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
+ let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
+ let layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
+ if (backgroundLayout) {
+ layout = StrCast(fieldTemplate.layout).replace(/fieldKey={"annotations"}/, `fieldKey={"${metaKey}"} fieldExt={"annotations"}`);
+ backgroundLayout = backgroundLayout.replace(/fieldKey={"[^"]*"}/, `fieldKey={"${metaKey}"}`);
}
- Doc.AddDocToList(parent, "data", template);
+ let nw = NumCast(fieldTemplate.nativeWidth);
+ let nh = NumCast(fieldTemplate.nativeHeight);
+
+ fieldTemplate.title = metaKey;
+ fieldTemplate.layout = layout;
+ fieldTemplate.backgroundLayout = backgroundLayout;
+ fieldTemplate.nativeWidth = nw;
+ fieldTemplate.nativeHeight = nh;
+ fieldTemplate.embed = true;
+ fieldTemplate.isTemplate = true;
+ fieldTemplate.templates = new List([Templates.TitleBar(metaKey)]);
+ fieldTemplate.proto = Doc.GetProto(parentStackingDoc);
+
+ Doc.AddDocToList(parentStackingDoc, "data", fieldTemplate);
row.uncheck();
}
- inferType = (field: FieldResult, metaKey: string) => {
+ inferType = (data: FieldResult, metaKey: string) => {
let options = { width: 300, height: 300, title: metaKey };
- if (field instanceof RichTextField || typeof field === "string" || typeof field === "number") {
+ if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") {
return Docs.TextDocument(options);
- } else if (field instanceof List) {
+ } else if (data instanceof List) {
return Docs.StackingDocument([], options);
- } else if (field instanceof ImageField) {
+ } else if (data instanceof ImageField) {
return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options);
}
return new Doc;
--
cgit v1.2.3-70-g09d2
From 7fdb33cafaa3e8593d648ddba994356a9625ff56 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 13:52:35 -0400
Subject: string list to list of text doc map in scraper and cleaned up kv pane
templating interaction code
---
src/client/views/nodes/DocumentContentsView.tsx | 7 +--
src/client/views/nodes/KeyValueBox.tsx | 20 ++++++--
src/scraping/buxton/scraper.py | 61 +++++++++++++++++++++++--
3 files changed, 75 insertions(+), 13 deletions(-)
(limited to 'src')
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index b4c0e844f..eb786d537 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -69,7 +69,7 @@ export class DocumentContentsView extends React.Component 7) return (null);
if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return >>>>>> b49fdb1c42b9758e006521e0f404634ba396a2ac
jsx={this.finalLayout}
showWarnings={true}
onError={(test: any) => { console.log(test); }}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index e27ab1589..e8619584e 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -161,7 +161,7 @@ export class KeyValueBox extends React.Component {
getTemplate = async () => {
let parent = Docs.StackingDocument([], { width: 800, height: 800, title: "Template" });
parent.singleColumn = false;
- parent.columnWidth = 50;
+ parent.columnWidth = 100;
for (let row of this.rows.filter(row => row.isChecked)) {
await this.createTemplateField(parent, row);
row.uncheck();
@@ -175,7 +175,7 @@ export class KeyValueBox extends React.Component {
if (!sourceDoc) {
return;
}
- let fieldTemplate = this.inferType(sourceDoc[metaKey], metaKey);
+ let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
// move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
@@ -200,12 +200,24 @@ export class KeyValueBox extends React.Component {
Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
}
- inferType = (data: FieldResult, metaKey: string) => {
+ inferType = async (data: FieldResult, metaKey: string) => {
let options = { width: 300, height: 300, title: metaKey };
if (data instanceof RichTextField || typeof data === "string" || typeof data === "number") {
return Docs.TextDocument(options);
} else if (data instanceof List) {
- return Docs.StackingDocument([], options);
+ if (data.length === 0) {
+ return Docs.StackingDocument([], options);
+ }
+ let first = await Cast(data[0], Doc);
+ if (!first) {
+ return Docs.StackingDocument([], options);
+ }
+ switch (first.type) {
+ case "image":
+ return Docs.StackingDocument([], options);
+ case "text":
+ return Docs.TreeDocument([], options);
+ }
} else if (data instanceof ImageField) {
return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options);
}
diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py
index 02c6d8b74..700269727 100644
--- a/src/scraping/buxton/scraper.py
+++ b/src/scraping/buxton/scraper.py
@@ -26,7 +26,7 @@ def extract_links(fileName):
item = rels[rel]
if item.reltype == RT.HYPERLINK and ".aspx" not in item._target:
links.append(item._target)
- return listify(links)
+ return text_doc_map(links)
def extract_value(kv_string):
@@ -60,6 +60,12 @@ def protofy(fieldId):
}
+def text_doc_map(string_list):
+ def guid_map(caption):
+ return write_text_doc(caption)
+ return listify(proxify_guids(list(map(guid_map, string_list))))
+
+
def write_schema(parse_results, display_fields, storage_key):
view_guids = parse_results["child_guids"]
@@ -110,6 +116,54 @@ def write_schema(parse_results, display_fields, storage_key):
return view_doc_guid
+def write_text_doc(content):
+ data_doc_guid = guid()
+ view_doc_guid = guid()
+
+ view_doc = {
+ "_id": view_doc_guid,
+ "fields": {
+ "proto": protofy(data_doc_guid),
+ "x": 10,
+ "y": 10,
+ "width": 400,
+ "zIndex": 2,
+ "libraryBrush": False
+ },
+ "__type": "Doc"
+ }
+
+ data_doc = {
+ "_id": data_doc_guid,
+ "fields": {
+ "proto": protofy("textProto"),
+ "data": {
+ "Data": '{"doc":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"' + content + '"}]}]},"selection":{"type":"text","anchor":1,"head":1}' + '}',
+ "__type": "RichTextField"
+ },
+ "title": content,
+ "nativeWidth": 200,
+ "author": "Bill Buxton",
+ "creationDate": {
+ "date": datetime.datetime.utcnow().microsecond,
+ "__type": "date"
+ },
+ "isPrototype": True,
+ "autoHeight": True,
+ "page": -1,
+ "nativeHeight": 200,
+ "height": 200,
+ "data_text": content
+ },
+ "__type": "Doc"
+ }
+
+ db.newDocuments.insert_one(view_doc)
+ db.newDocuments.insert_one(data_doc)
+
+ return view_doc_guid
+
+
def write_image(folder, name):
path = f"http://localhost:1050/files/{folder}/{name}"
@@ -253,7 +307,7 @@ def parse_document(file_name: str):
while lines[cur] != "Image":
link_descriptions.append(lines[cur].strip())
cur += 1
- result["link_descriptions"] = listify(link_descriptions)
+ result["link_descriptions"] = text_doc_map(link_descriptions)
result["hyperlinks"] = extract_links(source + "/" + file_name)
@@ -265,7 +319,8 @@ def parse_document(file_name: str):
captions.append(lines[cur + 1])
cur += 2
result["images"] = listify(images)
- result["captions"] = listify(captions)
+
+ result["captions"] = text_doc_map(captions)
notes = []
if (cur < len(lines) and lines[cur] == "NOTES:"):
--
cgit v1.2.3-70-g09d2
From 1d00e2e05acabe938ee496b6bf4bea55e82056e7 Mon Sep 17 00:00:00 2001
From: bob
Date: Tue, 9 Jul 2019 14:18:12 -0400
Subject: fixed ink marqueeing. fixed link following on clicks. commented out
link displays.
---
src/client/documents/Documents.ts | 1 -
.../collections/collectionFreeForm/MarqueeView.tsx | 26 +++++++++++-----
src/client/views/nodes/DocumentView.tsx | 36 ++++++++--------------
src/new_fields/Doc.ts | 2 +-
4 files changed, 31 insertions(+), 34 deletions(-)
(limited to 'src')
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2bddf053a..109afeeb0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -59,7 +59,6 @@ export interface DocumentOptions {
x?: number;
y?: number;
type?: string;
- ink?: InkField;
width?: number;
height?: number;
nativeWidth?: number;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index c83e1e3f5..8a619bfae 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,7 +1,7 @@
import * as htmlToImage from "html-to-image";
import { action, computed, observable } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../../new_fields/Doc";
+import { Doc, FieldResult } from "../../../../new_fields/Doc";
import { Id } from "../../../../new_fields/FieldSymbols";
import { InkField, StrokeData } from "../../../../new_fields/InkField";
import { List } from "../../../../new_fields/List";
@@ -224,6 +224,18 @@ export class MarqueeView extends React.Component
return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
}
+ get ink() {
+ let container = this.props.container.Document;
+ let containerKey = this.props.container.props.fieldKey;
+ return Cast(container[containerKey + "_ink"], InkField);
+ }
+
+ set ink(value: InkField | undefined) {
+ let container = Doc.GetProto(this.props.container.Document);
+ let containerKey = this.props.container.props.fieldKey;
+ container[containerKey + "_ink"] = value;
+ }
+
@undoBatch
@action
marqueeCommand = async (e: KeyboardEvent) => {
@@ -235,9 +247,8 @@ export class MarqueeView extends React.Component
e.stopPropagation();
(e as any).propagationIsStopped = true;
this.marqueeSelect().map(d => this.props.removeDocument(d));
- let ink = Cast(this.props.container.props.Document.ink, InkField);
- if (ink) {
- this.marqueeInkDelete(ink.inkData);
+ if (this.ink) {
+ this.marqueeInkDelete(this.ink.inkData);
}
SelectionManager.DeselectAll();
this.cleanupInteractions(false);
@@ -259,8 +270,7 @@ export class MarqueeView extends React.Component
return d;
});
}
- let ink = Cast(this.props.container.props.Document.ink, InkField);
- let inkData = ink ? ink.inkData : undefined;
+ let inkData = this.ink ? this.ink.inkData : undefined;
let newCollection = Docs.FreeformDocument(selected, {
x: bounds.left,
y: bounds.top,
@@ -269,9 +279,9 @@ export class MarqueeView extends React.Component
backgroundColor: this.props.container.isAnnotationOverlay ? undefined : "white",
width: bounds.width,
height: bounds.height,
- ink: inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined,
title: e.key === "s" || e.key === "S" ? "-summary-" : "a nested collection",
});
+ newCollection.data_ink = inkData ? new InkField(this.marqueeInkSelect(inkData)) : undefined;
this.marqueeInkDelete(inkData);
if (e.key === "s") {
@@ -348,7 +358,7 @@ export class MarqueeView extends React.Component
let idata = new Map();
ink.forEach((value: StrokeData, key: string, map: any) =>
!InkingCanvas.IntersectStrokeRect(value, this.Bounds) && idata.set(key, value));
- Doc.SetOnPrototype(this.props.container.props.Document, "ink", new InkField(idata));
+ this.ink = new InkField(idata);
}
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 47ec3b5a1..430409ee3 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -339,30 +339,18 @@ export class DocumentView extends DocComponent(Docu
}
}
else if (linkedDocs.length) {
- let linkedDoc = linkedDocs.length ? linkedDocs[0] : expandedDocs[0];
- let linkedPages = [linkedDocs.length ? NumCast(linkedDocs[0].anchor1Page, undefined) : NumCast(linkedDocs[0].anchor2Page, undefined),
- linkedDocs.length ? NumCast(linkedDocs[0].anchor2Page, undefined) : NumCast(linkedDocs[0].anchor1Page, undefined)];
- let maxLocation = StrCast(linkedDoc.maximizeLocation, "inTab");
- DocumentManager.Instance.jumpToDocument(linkedDoc, ctrlKey, false, document => this.props.addDocTab(document, undefined, maxLocation), linkedPages[altKey ? 1 : 0]);
-
- // else if (linkedToDocs.length || linkedFromDocs.length) {
- // let linkedFwdDocs = [
- // linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : expandedDocs[0],
- // linkedFromDocs.length ? linkedFromDocs[0].linkedFrom as Doc : linkedToDocs.length ? linkedToDocs[0].linkedTo as Doc : expandedDocs[0]];
-
- // let linkedFwdContextDocs = [
- // linkedToDocs.length ? await (linkedToDocs[0].linkedToContext) as Doc : linkedFromDocs.length ? await PromiseValue(linkedFromDocs[0].linkedFromContext) as Doc : undefined,
- // linkedFromDocs.length ? await (linkedFromDocs[0].linkedFromContext) as Doc : linkedToDocs.length ? await PromiseValue(linkedToDocs[0].linkedToContext) as Doc : undefined];
-
- // let linkedFwdPage = [
- // linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : undefined,
- // linkedFromDocs.length ? NumCast(linkedFromDocs[0].linkedFromPage, undefined) : linkedToDocs.length ? NumCast(linkedToDocs[0].linkedToPage, undefined) : undefined];
-
- // if (!linkedFwdDocs.some(l => l instanceof Promise)) {
- // let maxLocation = StrCast(linkedFwdDocs[altKey ? 1 : 0].maximizeLocation, "inTab");
- // let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
- // DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, undefined, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
- // }
+ let first = linkedDocs.filter(d => Doc.AreProtosEqual(d.anchor1 as Doc, this.props.Document));
+ let linkedFwdDocs = first.length ? [first[0].anchor2 as Doc, first[0].anchor1 as Doc] : [expandedDocs[0], expandedDocs[0]];
+
+ let linkedFwdContextDocs = [first.length ? await (first[0].context) as Doc : undefined, undefined];
+
+ let linkedFwdPage = [first.length ? NumCast(first[0].linkedToPage, undefined) : undefined, undefined];
+
+ if (!linkedFwdDocs.some(l => l instanceof Promise)) {
+ let maxLocation = StrCast(linkedFwdDocs[0].maximizeLocation, "inTab");
+ let targetContext = !Doc.AreProtosEqual(linkedFwdContextDocs[altKey ? 1 : 0], this.props.ContainingCollectionView && this.props.ContainingCollectionView.props.Document) ? linkedFwdContextDocs[altKey ? 1 : 0] : undefined;
+ DocumentManager.Instance.jumpToDocument(linkedFwdDocs[altKey ? 1 : 0], ctrlKey, false, document => this.props.addDocTab(document, undefined, maxLocation), linkedFwdPage[altKey ? 1 : 0], targetContext);
+ }
}
}
}
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index aa13b1d7a..a07f56ca4 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -277,7 +277,7 @@ export namespace Doc {
}
//
- // Resolves a reference to a field by returning 'doc' if o field extension is specified,
+ // Resolves a reference to a field by returning 'doc' if field extension is specified,
// otherwise, it returns the extension document stored in doc._ext.
// This mechanism allows any fields to be extended with an extension document that can
// be used to capture field-specific metadata. For example, an image field can be extended
--
cgit v1.2.3-70-g09d2
From 724d03b8d05b5ef7ef944c648aae8f43dde625be Mon Sep 17 00:00:00 2001
From: bob
Date: Tue, 9 Jul 2019 14:18:26 -0400
Subject: from last
---
.../CollectionFreeFormLinksView.tsx | 111 ++++++++++-----------
1 file changed, 53 insertions(+), 58 deletions(-)
(limited to 'src')
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index ebeb1fcee..2d94f1b8e 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -17,58 +17,56 @@ export class CollectionFreeFormLinksView extends React.Component {
- let doclist = DocListCast(this.props.Document[this.props.fieldKey]);
- return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) };
- },
- () => {
- let doclist = DocListCast(this.props.Document[this.props.fieldKey]);
- let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : [];
- views.forEach((dstDoc, i) => {
- views.forEach((srcDoc, j) => {
- let dstTarg = dstDoc;
- let srcTarg = srcDoc;
- let x1 = NumCast(srcDoc.x);
- let x2 = NumCast(dstDoc.x);
- let x1w = NumCast(srcDoc.width, -1) / NumCast(srcDoc.zoomBasis, 1);
- let x2w = NumCast(dstDoc.width, -1) / NumCast(srcDoc.zoomBasis, 1);
- if (x1w < 0 || x2w < 0 || i === j) { }
- else {
- let findBrush = (field: (Doc | Promise)[]) => field.findIndex(brush => {
- let bdocs = brush instanceof Doc ? Cast(brush.brushingDocs, listSpec(Doc), []) : undefined;
- return bdocs && bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false;
- });
- let brushAction = (field: (Doc | Promise)[]) => {
- let found = findBrush(field);
- if (found !== -1) {
- console.log("REMOVE BRUSH " + srcTarg.title + " " + dstTarg.title);
- field.splice(found, 1);
- }
- };
- if (Math.abs(x1 + x1w - x2) < 20) {
- let linkDoc: Doc = new Doc();
- linkDoc.title = "Histogram Brush";
- linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title);
- linkDoc.brushingDocs = new List([dstTarg, srcTarg]);
+ // this._brushReactionDisposer = reaction(
+ // () => {
+ // let doclist = DocListCast(this.props.Document[this.props.fieldKey]);
+ // return { doclist: doclist ? doclist : [], xs: doclist.map(d => d.x) };
+ // },
+ // () => {
+ // let doclist = DocListCast(this.props.Document[this.props.fieldKey]);
+ // let views = doclist ? doclist.filter(doc => StrCast(doc.backgroundLayout).indexOf("istogram") !== -1) : [];
+ // views.forEach((dstDoc, i) => {
+ // views.forEach((srcDoc, j) => {
+ // let dstTarg = dstDoc;
+ // let srcTarg = srcDoc;
+ // let x1 = NumCast(srcDoc.x);
+ // let x2 = NumCast(dstDoc.x);
+ // let x1w = NumCast(srcDoc.width, -1) / NumCast(srcDoc.zoomBasis, 1);
+ // let x2w = NumCast(dstDoc.width, -1) / NumCast(srcDoc.zoomBasis, 1);
+ // if (x1w < 0 || x2w < 0 || i === j) { }
+ // else {
+ // let findBrush = (field: (Doc | Promise)[]) => field.findIndex(brush => {
+ // let bdocs = brush instanceof Doc ? Cast(brush.brushingDocs, listSpec(Doc), []) : undefined;
+ // return bdocs && bdocs.length && ((bdocs[0] === dstTarg && bdocs[1] === srcTarg)) ? true : false;
+ // });
+ // let brushAction = (field: (Doc | Promise)[]) => {
+ // let found = findBrush(field);
+ // if (found !== -1) {
+ // field.splice(found, 1);
+ // }
+ // };
+ // if (Math.abs(x1 + x1w - x2) < 20) {
+ // let linkDoc: Doc = new Doc();
+ // linkDoc.title = "Histogram Brush";
+ // linkDoc.linkDescription = "Brush between " + StrCast(srcTarg.title) + " and " + StrCast(dstTarg.Title);
+ // linkDoc.brushingDocs = new List([dstTarg, srcTarg]);
- brushAction = (field: (Doc | Promise)[]) => {
- if (findBrush(field) === -1) {
- console.log("ADD BRUSH " + srcTarg.title + " " + dstTarg.title);
- field.push(linkDoc);
- }
- };
- }
- if (dstTarg.brushingDocs === undefined) dstTarg.brushingDocs = new List();
- if (srcTarg.brushingDocs === undefined) srcTarg.brushingDocs = new List();
- let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []);
- let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []);
- brushAction(dstBrushDocs);
- brushAction(srcBrushDocs);
- }
- });
- });
- });
+ // brushAction = (field: (Doc | Promise)[]) => {
+ // if (findBrush(field) === -1) {
+ // field.push(linkDoc);
+ // }
+ // };
+ // }
+ // if (dstTarg.brushingDocs === undefined) dstTarg.brushingDocs = new List();
+ // if (srcTarg.brushingDocs === undefined) srcTarg.brushingDocs = new List();
+ // let dstBrushDocs = Cast(dstTarg.brushingDocs, listSpec(Doc), []);
+ // let srcBrushDocs = Cast(srcTarg.brushingDocs, listSpec(Doc), []);
+ // brushAction(dstBrushDocs);
+ // brushAction(srcBrushDocs);
+ // }
+ // });
+ // });
+ // });
}
componentWillUnmount() {
if (this._brushReactionDisposer) {
@@ -115,19 +113,16 @@ export class CollectionFreeFormLinksView extends React.Component {
- let x = c.l.reduce((p, l) => p + l[Id], "");
- return ;
- });
+ return connections.map(c => p + l[Id], "")} A={c.a} B={c.b} LinkDocs={c.l}
+ removeDocument={this.props.removeDocument} addDocument={this.props.addDocument} />);
}
render() {
return (
- */}
{this.props.children}
);
--
cgit v1.2.3-70-g09d2
From 9d9c16939bb296a9deb38bc34cbea9870aee4f76 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 14:33:56 -0400
Subject: collection view fix
---
src/client/views/nodes/KeyValueBox.tsx | 5 +++++
1 file changed, 5 insertions(+)
(limited to 'src')
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index e8619584e..b5c47f138 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -175,7 +175,9 @@ export class KeyValueBox extends React.Component {
if (!sourceDoc) {
return;
}
+
let fieldTemplate = await this.inferType(sourceDoc[metaKey], metaKey);
+ let previousViewType = fieldTemplate.viewType;
// move data doc fields to layout doc as needed (nativeWidth/nativeHeight, data, ??)
let backgroundLayout = StrCast(fieldTemplate.backgroundLayout);
@@ -196,6 +198,7 @@ export class KeyValueBox extends React.Component {
fieldTemplate.isTemplate = true;
fieldTemplate.templates = new List([Templates.TitleBar(metaKey)]);
fieldTemplate.proto = Doc.GetProto(parentStackingDoc);
+ previousViewType && (fieldTemplate.viewType = previousViewType);
Cast(parentStackingDoc.data, listSpec(Doc))!.push(fieldTemplate);
}
@@ -214,8 +217,10 @@ export class KeyValueBox extends React.Component {
}
switch (first.type) {
case "image":
+ console.log("STACKING VIEW CREATED for ", data);
return Docs.StackingDocument([], options);
case "text":
+ console.log("TREE VIEW CREATED for ", data);
return Docs.TreeDocument([], options);
}
} else if (data instanceof ImageField) {
--
cgit v1.2.3-70-g09d2
From 7a33f0b56f1182c19cc7ea1b276da3a674438957 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 14:39:23 -0400
Subject: cleanup
---
src/client/views/nodes/KeyValueBox.tsx | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
(limited to 'src')
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index b5c47f138..2f5a0f963 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -217,14 +217,12 @@ export class KeyValueBox extends React.Component {
}
switch (first.type) {
case "image":
- console.log("STACKING VIEW CREATED for ", data);
return Docs.StackingDocument([], options);
case "text":
- console.log("TREE VIEW CREATED for ", data);
return Docs.TreeDocument([], options);
}
} else if (data instanceof ImageField) {
- return Docs.ImageDocument("https://www.freepik.com/free-icon/picture-frame-with-mountain-image_748687.htm", options);
+ return Docs.ImageDocument("https://image.flaticon.com/icons/png/512/23/23765.png", options);
}
return new Doc;
}
--
cgit v1.2.3-70-g09d2
From 146521beef8ef2ca836a0b4a63a66bdf48485098 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 14:47:30 -0400
Subject: preparing for merging into master
---
src/client/views/collections/CollectionSubView.tsx | 7 ++++---
src/new_fields/Doc.ts | 1 -
2 files changed, 4 insertions(+), 4 deletions(-)
(limited to 'src')
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index a8810f336..873fb518c 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -72,6 +72,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
if (!proto) {
return;
}
+ // The following conditional detects a recurring bug we've seen on the server
if (proto[Id] === "collectionProto") {
alert("COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...");
console.log(doc);
@@ -216,9 +217,9 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) {
body: formData
}).then(async (res: Response) => {
(await res.json()).map(action((file: any) => {
- let path = window.location.origin + file;
- let docPromise = Docs.getDocumentFromType(type, path, { ...options, nativeWidth: 300, width: 300, title: dropFileName });
- docPromise.then(doc => doc && this.props.addDocument(doc));
+ let full = { ...options, nativeWidth: 300, width: 300, title: dropFileName };
+ let path = DocServer.prepend(file);
+ Docs.getDocumentFromType(type, path, full).then(doc => doc && this.props.addDocument(doc));
}));
});
promises.push(prom);
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts
index d180f8b01..a07f56ca4 100644
--- a/src/new_fields/Doc.ts
+++ b/src/new_fields/Doc.ts
@@ -251,7 +251,6 @@ export namespace Doc {
if (allowDuplicates !== true) {
let pind = list.reduce((l, d, i) => d instanceof Doc && Doc.AreProtosEqual(d, doc) ? i : l, -1);
if (pind !== -1) {
- console.log("SPLICING DUPLICATE");
list.splice(pind, 1);
}
}
--
cgit v1.2.3-70-g09d2
From 15914700e307a372a1e304955a5244184108a6fa Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 19:52:09 -0400
Subject: logging in now directs you to original target url and restored logout
button functionality
---
src/client/views/MainView.tsx | 2 +-
.../authentication/controllers/user_controller.ts | 22 +++++++++++++---------
src/server/index.ts | 10 +++++++---
3 files changed, 21 insertions(+), 13 deletions(-)
(limited to 'src')
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b8fc3f47b..b37ba1cb0 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -413,7 +413,7 @@ export class MainView extends React.Component {
return [
this.isSearchVisible ?
: null,
-
+
];
}
diff --git a/src/server/authentication/controllers/user_controller.ts b/src/server/authentication/controllers/user_controller.ts
index 1dacdf3fa..ca4fc171c 100644
--- a/src/server/authentication/controllers/user_controller.ts
+++ b/src/server/authentication/controllers/user_controller.ts
@@ -42,10 +42,6 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
const errors = req.validationErrors();
if (errors) {
- res.render("signup.pug", {
- title: "Sign Up",
- user: req.user,
- });
return res.redirect(RouteStore.signup);
}
@@ -66,16 +62,23 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
user.save((err) => {
if (err) { return next(err); }
req.logIn(user, (err) => {
- if (err) {
- return next(err);
- }
- res.redirect(RouteStore.home);
+ if (err) { return next(err); }
+ tryRedirectToTarget(req, res);
});
});
});
};
+let tryRedirectToTarget = (req: Request, res: Response) => {
+ if (req.session && req.session.target) {
+ res.redirect(req.session.target);
+ req.session.target = undefined;
+ } else {
+ res.redirect(RouteStore.home);
+ }
+};
+
/**
* GET /login
@@ -83,6 +86,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
*/
export let getLogin = (req: Request, res: Response) => {
if (req.user) {
+ req.session!.target = undefined;
return res.redirect(RouteStore.home);
}
res.render("login.pug", {
@@ -115,7 +119,7 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
}
req.logIn(user, (err) => {
if (err) { next(err); return; }
- res.redirect(RouteStore.home);
+ tryRedirectToTarget(req, res);
});
})(req, res, next);
};
diff --git a/src/server/index.ts b/src/server/index.ts
index c9ec11bfa..5ecbfa41a 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -40,6 +40,7 @@ import { Search } from './Search';
import { debug } from 'util';
import _ = require('lodash');
import { Response } from 'express-serve-static-core';
+import { DocServer } from '../client/DocServer';
const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
const probe = require("probe-image-size");
@@ -69,7 +70,7 @@ app.use(session({
secret: "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc",
resave: true,
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
- saveUninitialized: true,
+ saveUninitialized: false,
store: new MongoStore({ url: 'mongodb://localhost:27017/Dash' })
}));
@@ -82,6 +83,7 @@ app.use(passport.initialize());
app.use(passport.session());
app.use((req, res, next) => {
res.locals.user = req.user;
+ // res.locals.target = req.session!.target;
next();
});
@@ -103,14 +105,16 @@ enum Method {
*/
function addSecureRoute(method: Method,
handler: (user: DashUserModel, res: express.Response, req: express.Request) => void,
- onRejection: (res: express.Response) => any = (res) => res.redirect(RouteStore.logout),
+ onRejection: (res: express.Response, req: express.Request) => any = res => res.redirect(RouteStore.login),
...subscribers: string[]
) {
let abstracted = (req: express.Request, res: express.Response) => {
if (req.user) {
handler(req.user, res, req);
} else {
- onRejection(res);
+ let target = `http://localhost:${port}${req.originalUrl}`;
+ req.session!.target = target;
+ onRejection(res, req);
}
};
subscribers.forEach(route => {
--
cgit v1.2.3-70-g09d2
From e9189e389adbbbc6c13281b4fa4ea2a5c0076275 Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 19:57:29 -0400
Subject: clean up
---
src/server/index.ts | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
(limited to 'src')
diff --git a/src/server/index.ts b/src/server/index.ts
index 5ecbfa41a..554bdad17 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -70,7 +70,7 @@ app.use(session({
secret: "64d6866242d3b5a5503c675b32c9605e4e90478e9b77bcf2bc",
resave: true,
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
- saveUninitialized: false,
+ saveUninitialized: true,
store: new MongoStore({ url: 'mongodb://localhost:27017/Dash' })
}));
@@ -112,8 +112,7 @@ function addSecureRoute(method: Method,
if (req.user) {
handler(req.user, res, req);
} else {
- let target = `http://localhost:${port}${req.originalUrl}`;
- req.session!.target = target;
+ req.session!.target = `http://localhost:${port}${req.originalUrl}`;
onRejection(res, req);
}
};
--
cgit v1.2.3-70-g09d2
From dc807afc7b2b0bbeb22378b4e1603eaec688615e Mon Sep 17 00:00:00 2001
From: Sam Wilkins
Date: Tue, 9 Jul 2019 19:58:55 -0400
Subject: last cleanup
---
src/server/index.ts | 2 --
1 file changed, 2 deletions(-)
(limited to 'src')
diff --git a/src/server/index.ts b/src/server/index.ts
index 554bdad17..2073046ce 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -40,7 +40,6 @@ import { Search } from './Search';
import { debug } from 'util';
import _ = require('lodash');
import { Response } from 'express-serve-static-core';
-import { DocServer } from '../client/DocServer';
const MongoStore = require('connect-mongo')(session);
const mongoose = require('mongoose');
const probe = require("probe-image-size");
@@ -83,7 +82,6 @@ app.use(passport.initialize());
app.use(passport.session());
app.use((req, res, next) => {
res.locals.user = req.user;
- // res.locals.target = req.session!.target;
next();
});
--
cgit v1.2.3-70-g09d2