From 972f76a34e3c1a1aa5f0be59639fbd5763c9c16f Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Thu, 6 Jun 2019 17:45:26 -0400 Subject: links get saved to global table --- src/client/documents/Documents.ts | 30 +++++----- src/client/util/DocumentManager.ts | 54 ++++++++++++------ src/client/util/DragManager.ts | 19 +++++-- src/client/views/DocumentDecorations.tsx | 8 ++- .../CollectionFreeFormLinksView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/LinkBox.tsx | 17 +++--- src/client/views/nodes/LinkManager.tsx | 51 +++++++++++++++++ src/client/views/nodes/LinkMenu.scss | 65 +++++++++++++++++++++- src/client/views/nodes/LinkMenu.tsx | 51 ++++++++++++++--- 10 files changed, 247 insertions(+), 56 deletions(-) create mode 100644 src/client/views/nodes/LinkManager.tsx (limited to 'src') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ab61b915c..87b831663 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -35,6 +35,7 @@ import { dropActionType } from "../util/DragManager"; import { DateField } from "../../new_fields/DateField"; import { UndoManager } from "../util/UndoManager"; import { RouteStore } from "../../server/RouteStore"; +import { LinkManager } from "../views/nodes/LinkManager"; var requestImageSize = require('request-image-size'); var path = require('path'); @@ -70,12 +71,12 @@ const delegateKeys = ["x", "y", "width", "height", "panX", "panY"]; export namespace DocUtils { export function MakeLink(source: Doc, target: Doc) { - let protoSrc = source.proto ? source.proto : source; - let protoTarg = target.proto ? target.proto : target; + // let protoSrc = source.proto ? source.proto : source; + // let protoTarg = target.proto ? target.proto : target; UndoManager.RunInBatch(() => { let linkDoc = Docs.TextDocument({ width: 100, height: 30, borderRounding: -1 }); //let linkDoc = new Doc; - linkDoc.proto!.title = "-link name-"; + linkDoc.proto!.title = source.proto!.title + " and " + target.proto!.title; linkDoc.proto!.linkDescription = ""; linkDoc.proto!.linkTags = "Default"; @@ -84,17 +85,20 @@ export namespace DocUtils { linkDoc.proto!.linkedFrom = source; linkDoc.proto!.linkedFromPage = source.curPage; - let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); - if (!linkedFrom) { - protoTarg.linkedFromDocs = linkedFrom = new List(); - } - linkedFrom.push(linkDoc); + // let linkedFrom = Cast(protoTarg.linkedFromDocs, listSpec(Doc)); + // if (!linkedFrom) { + // protoTarg.linkedFromDocs = linkedFrom = new List(); + // } + // linkedFrom.push(linkDoc); + + // let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); + // if (!linkedTo) { + // protoSrc.linkedToDocs = linkedTo = new List(); + // } + // linkedTo.push(linkDoc); + + LinkManager.Instance.allLinks.push(linkDoc); - let linkedTo = Cast(protoSrc.linkedToDocs, listSpec(Doc)); - if (!linkedTo) { - protoSrc.linkedToDocs = linkedTo = new List(); - } - linkedTo.push(linkDoc); return linkDoc; }, "make link"); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 65c4b9e4b..712529745 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -9,6 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { CollectionPDFView } from '../views/collections/CollectionPDFView'; import { CollectionVideoView } from '../views/collections/CollectionVideoView'; import { Id } from '../../new_fields/FieldSymbols'; +import { LinkManager } from '../views/nodes/LinkManager'; export class DocumentManager { @@ -83,12 +84,16 @@ export class DocumentManager { @computed public get LinkedDocumentViews() { - return DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { - let linksList = DocListCast(dv.props.Document.linkedToDocs); + let linked = DocumentManager.Instance.DocumentViews.filter(dv => dv.isSelected() || BoolCast(dv.props.Document.libraryBrush, false)).reduce((pairs, dv) => { + + + let linksList = LinkManager.Instance.findAllRelatedLinks(dv.props.Document); if (linksList && linksList.length) { pairs.push(...linksList.reduce((pairs, link) => { if (link) { - let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); + let destination = (link["linkedTo"] === dv.props.Document) ? link["linkedFrom"] : link["linkedTo"]; + let linkToDoc = FieldValue(Cast(destination, Doc)); + // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); if (linkToDoc) { DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => pairs.push({ a: dv, b: docView1, l: link })); @@ -97,21 +102,38 @@ export class DocumentManager { return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); } - linksList = DocListCast(dv.props.Document.linkedFromDocs); - if (linksList && linksList.length) { - pairs.push(...linksList.reduce((pairs, link) => { - if (link) { - let linkFromDoc = FieldValue(Cast(link.linkedFrom, Doc)); - if (linkFromDoc) { - DocumentManager.Instance.getDocumentViews(linkFromDoc).map(docView1 => - pairs.push({ a: dv, b: docView1, l: link })); - } - } - return pairs; - }, pairs)); - } + + // let linksList = DocListCast(dv.props.Document.linkedToDocs); + // console.log("to links", linksList.length); + // if (linksList && linksList.length) { + // pairs.push(...linksList.reduce((pairs, link) => { + // if (link) { + // let linkToDoc = FieldValue(Cast(link.linkedTo, Doc)); + // if (linkToDoc) { + // DocumentManager.Instance.getDocumentViews(linkToDoc).map(docView1 => + // pairs.push({ a: dv, b: docView1, l: link })); + // } + // } + // return pairs; + // }, [] as { a: DocumentView, b: DocumentView, l: Doc }[])); + // } + // linksList = DocListCast(dv.props.Document.linkedFromDocs); + // console.log("from links", linksList.length); + // if (linksList && linksList.length) { + // pairs.push(...linksList.reduce((pairs, link) => { + // if (link) { + // let linkFromDoc = FieldValue(Cast(link.linkedFrom, Doc)); + // if (linkFromDoc) { + // DocumentManager.Instance.getDocumentViews(linkFromDoc).map(docView1 => + // pairs.push({ a: dv, b: docView1, l: link })); + // } + // } + // return pairs; + // }, pairs)); + // } return pairs; }, [] as { a: DocumentView, b: DocumentView, l: Doc }[]); + return linked; } @undoBatch diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 1e84a0db0..7854cc080 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -4,6 +4,7 @@ import { Cast } from "../../new_fields/Types"; import { emptyFunction } from "../../Utils"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import * as globalCssVariables from "../views/globalCssVariables.scss"; +import { LinkManager } from "../views/nodes/LinkManager"; export type dropActionType = "alias" | "copy" | undefined; export function SetupDrag(_reference: React.RefObject, docFunc: () => Doc | Promise, moveFunc?: DragManager.MoveFunction, dropAction?: dropActionType) { @@ -41,14 +42,20 @@ export function SetupDrag(_reference: React.RefObject, docFunc: () export async function DragLinksAsDocuments(dragEle: HTMLElement, x: number, y: number, sourceDoc: Doc) { let srcTarg = sourceDoc.proto; let draggedDocs: Doc[] = []; - let draggedFromDocs: Doc[] = []; + // let draggedFromDocs: Doc[] = []; if (srcTarg) { - let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); - let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); - if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); - if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); + // let linkToDocs = await DocListCastAsync(srcTarg.linkedToDocs); + // let linkFromDocs = await DocListCastAsync(srcTarg.linkedFromDocs); + let linkDocs = LinkManager.Instance.findAllRelatedLinks(srcTarg); + if (linkDocs) draggedDocs = linkDocs.map(link => { + return LinkManager.Instance.findOppositeAnchor(link, sourceDoc); + }); + + + // if (linkToDocs) draggedDocs = linkToDocs.map(linkDoc => Cast(linkDoc.linkedTo, Doc) as Doc); + // if (linkFromDocs) draggedFromDocs = linkFromDocs.map(linkDoc => Cast(linkDoc.linkedFrom, Doc) as Doc); } - draggedDocs.push(...draggedFromDocs); + // draggedDocs.push(...draggedFromDocs); if (draggedDocs.length) { let moddrag: Doc[] = []; for (const draggedDoc of draggedDocs) { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index da9b1253e..2aae9bce6 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -28,6 +28,7 @@ import { CollectionView } from "./collections/CollectionView"; import { DocumentManager } from "../util/DocumentManager"; import { FormattedTextBox } from "./nodes/FormattedTextBox"; import { FieldView } from "./nodes/FieldView"; +import { LinkManager } from "./nodes/LinkManager"; library.add(faLink); @@ -510,9 +511,10 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> let linkButton = null; if (SelectionManager.SelectedDocuments().length > 0) { let selFirst = SelectionManager.SelectedDocuments()[0]; - let linkToSize = Cast(selFirst.props.Document.linkedToDocs, listSpec(Doc), []).length; - let linkFromSize = Cast(selFirst.props.Document.linkedFromDocs, listSpec(Doc), []).length; - let linkCount = linkToSize + linkFromSize; + // let linkToSize = Cast(selFirst.props.Document.linkedToDocs, listSpec(Doc), []).length; + // let linkFromSize = Cast(selFirst.props.Document.linkedFromDocs, listSpec(Doc), []).length; + // let linkCount = linkToSize + linkFromSize; + let linkCount = LinkManager.Instance.findAllRelatedLinks(selFirst.props.Document).length; linkButton = ( { @undoBatch - onViewButtonPressed = async (e: React.PointerEvent): Promise => { + followLink = async (e: React.PointerEvent): Promise => { e.stopPropagation(); DocumentManager.Instance.jumpToDocument(this.props.pairedDoc, e.altKey); } @@ -62,8 +63,8 @@ export class LinkBox extends React.Component { return ( // -
-
+
+

{this.props.linkName}

@@ -72,13 +73,15 @@ export class LinkBox extends React.Component {
-
+
{/*
*/} +
+
-
+
-
+
); diff --git a/src/client/views/nodes/LinkManager.tsx b/src/client/views/nodes/LinkManager.tsx new file mode 100644 index 000000000..1064eaa22 --- /dev/null +++ b/src/client/views/nodes/LinkManager.tsx @@ -0,0 +1,51 @@ +import { observable, computed, action } from "mobx"; +import React = require("react"); +import { SelectionManager } from "../../util/SelectionManager"; +import { observer } from "mobx-react"; +import './LinkEditor.scss'; +import { props } from "bluebird"; +import { DocumentView } from "./DocumentView"; +import { link } from "fs"; +import { StrCast, Cast } from "../../../new_fields/Types"; +import { Doc } from "../../../new_fields/Doc"; +import { listSpec } from "../../../new_fields/Schema"; + + +export class LinkManager { + private static _instance: LinkManager; + public static get Instance(): LinkManager { + return this._instance || (this._instance = new this()); + } + private constructor() { + } + + @observable + public allLinks: Array = []; + + public findAllRelatedLinks(source: Doc): Array { + let related = LinkManager.Instance.allLinks.filter( + link => Doc.AreProtosEqual(source, Cast(link.linkedFrom, Doc, new Doc)) || Doc.AreProtosEqual(source, Cast(link.linkedTo, Doc, new Doc))); + return related; + } + + public findRelatedGroupedLinks(source: Doc): Map> { + let related = this.findAllRelatedLinks(source); + let categories = new Map(); + related.forEach(link => { + let group = categories.get(link.linkTags); + if (group) group.push(link); + else group = [link]; + categories.set(link.linkTags, group); + }) + return categories; + } + + public findOppositeAnchor(link: Doc, source: Doc): Doc { + if (Doc.AreProtosEqual(source, Cast(link.linkedFrom, Doc, new Doc))) { + return Cast(link.linkedTo, Doc, new Doc); + } else { + return Cast(link.linkedFrom, Doc, new Doc); + } + } + +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkMenu.scss b/src/client/views/nodes/LinkMenu.scss index dedcce6ef..860f31d8a 100644 --- a/src/client/views/nodes/LinkMenu.scss +++ b/src/client/views/nodes/LinkMenu.scss @@ -1,3 +1,5 @@ +@import "../globalCssVariables"; + #linkMenu-container { width: 100%; height: auto; @@ -18,4 +20,65 @@ width: 100%; height: 100px; overflow-y: scroll; -} \ No newline at end of file +} + +.link-menu-group { + .link-menu-item { + border-top: 0.5px solid $light-color-secondary; + padding: 6px; + position: relative; + display: flex; + + .link-menu-item-content { + width: 100%; + } + + &:last-child { + border-bottom: 0.5px solid $light-color-secondary; + } + &:hover .link-menu-item-buttons { + display: flex; + } + &:hover .link-menu-item-content { + width: calc(100% - 72px); + } + } + .link-menu-item-buttons { + display: none; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + + .button { + width: 20px; + height: 20px; + margin: 0; + margin-right: 6px; + border-radius: 50%; + cursor: pointer; + pointer-events: auto; + background-color: $dark-color; + color: $light-color; + font-size: 65%; + transition: transform 0.2s; + text-align: center; + position: relative; + + .fa-icon { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + + &:last-child { + margin-right: 0; + } + &:hover { + background: $main-accent; + } + } + } +} + diff --git a/src/client/views/nodes/LinkMenu.tsx b/src/client/views/nodes/LinkMenu.tsx index 3f09d6214..6dc5623d1 100644 --- a/src/client/views/nodes/LinkMenu.tsx +++ b/src/client/views/nodes/LinkMenu.tsx @@ -8,6 +8,7 @@ import React = require("react"); import { Doc, DocListCast } from "../../../new_fields/Doc"; import { Cast, FieldValue, StrCast } from "../../../new_fields/Types"; import { Id } from "../../../new_fields/FieldSymbols"; +import { LinkManager } from "./LinkManager"; interface Props { docView: DocumentView; @@ -19,26 +20,62 @@ export class LinkMenu extends React.Component { @observable private _editingLink?: Doc; - renderLinkItems(links: Doc[], key: string, type: string) { + // renderLinkItems(links: Doc[], key: string, type: string) { + // return links.map(link => { + // let doc = FieldValue(Cast(link[key], Doc)); + // if (doc) { + // return this._editingLink = link)} type={type} />; + // } + // }); + // } + + renderLinkGroup(links: Doc[]) { + console.log("render link group"); + let source = this.props.docView.Document; + console.log("num links", links.length, typeof links); return links.map(link => { - let doc = FieldValue(Cast(link[key], Doc)); + let destination = (link["linkedTo"] === source) ? link["linkedFrom"] : link["linkedTo"]; + let doc = FieldValue(Cast(destination, Doc)); if (doc) { - return this._editingLink = link)} type={type} />; + console.log(doc[Id] + source[Id], "source is", source[Id]); + return this._editingLink = link)} type={""} />; } }); } + renderLinkItems(links: Map>) { + console.log("render link items"); + + let linkItems: Array = []; + + links.forEach((links, group) => { + console.log("category is ", group); + linkItems.push( +
+

{group}:

+
+ {this.renderLinkGroup(links)} +
+
+ ) + }); + + return linkItems; + } + render() { //get list of links from document - let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); - let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs); + // let linkFrom = DocListCast(this.props.docView.props.Document.linkedFromDocs); + // let linkTo = DocListCast(this.props.docView.props.Document.linkedToDocs); + let related = LinkManager.Instance.findRelatedGroupedLinks(this.props.docView.props.Document); if (this._editingLink === undefined) { return (
{/* */}
- {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} - {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} + {/* {this.renderLinkItems(linkTo, "linkedTo", "Destination: ")} + {this.renderLinkItems(linkFrom, "linkedFrom", "Source: ")} */} + {this.renderLinkItems(related)}
); -- cgit v1.2.3-70-g09d2