From 8264ac5484c0c7b5d47cb78ce60f5d6568e736d9 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 4 Jul 2020 17:25:10 -0500 Subject: redesigned editing menu, added link descriptions and popup on created --- src/client/views/nodes/DocumentLinksButton.tsx | 52 ++++++++++++------ src/client/views/nodes/DocumentView.tsx | 42 ++++++++++---- src/client/views/nodes/LinkCreatedBox.tsx | 8 +-- src/client/views/nodes/LinkDescriptionPopup.scss | 62 +++++++++++++++++++++ src/client/views/nodes/LinkDescriptionPopup.tsx | 70 ++++++++++++++++++++++++ 5 files changed, 200 insertions(+), 34 deletions(-) create mode 100644 src/client/views/nodes/LinkDescriptionPopup.scss create mode 100644 src/client/views/nodes/LinkDescriptionPopup.tsx (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index bfd860f65..5d1a68af5 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -11,6 +11,8 @@ import { DocUtils } from "../../documents/Documents"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { LinkDocPreview } from "./LinkDocPreview"; import { LinkCreatedBox } from "./LinkCreatedBox"; +import { LinkDescriptionPopup } from "./LinkDescriptionPopup"; +import { LinkManager } from "../../util/LinkManager"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -54,6 +56,7 @@ export class DocumentLinksButton extends React.Component { setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { if (doubleTap && this.props.InMenu) { @@ -87,15 +90,22 @@ export class DocumentLinksButton extends React.Component { - LinkCreatedBox.popupX = e.screenX; - LinkCreatedBox.popupY = e.screenY - 120; - LinkCreatedBox.linkCreated = true; - setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); - }); + + if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View) { + const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag"); + LinkManager.currentLink = linkDoc; + runInAction(() => { + LinkCreatedBox.popupX = e.screenX; + LinkCreatedBox.popupY = e.screenY - 33; + LinkCreatedBox.linkCreated = true; + + LinkDescriptionPopup.popupX = e.screenX; + LinkDescriptionPopup.popupY = e.screenY; + LinkDescriptionPopup.descriptionPopup = true; + + setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); + }); + } } } })); @@ -109,15 +119,21 @@ export class DocumentLinksButton extends React.Component { - LinkCreatedBox.popupX = e.screenX; - LinkCreatedBox.popupY = e.screenY - 120; - LinkCreatedBox.linkCreated = true; - setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); - }); + if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View) { + const linkDoc = DocUtils.MakeLink({ doc: DocumentLinksButton.StartLink.props.Document }, { doc: this.props.View.props.Document }, "long drag"); + LinkManager.currentLink = linkDoc; + runInAction(() => { + LinkCreatedBox.popupX = e.screenX; + LinkCreatedBox.popupY = e.screenY - 33; + LinkCreatedBox.linkCreated = true; + + LinkDescriptionPopup.popupX = e.screenX; + LinkDescriptionPopup.popupY = e.screenY; + LinkDescriptionPopup.descriptionPopup = true; + + setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); + }); + } } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b38db9a1e..9a4d9ac53 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -43,6 +43,8 @@ import React = require("react"); import { DocumentLinksButton } from './DocumentLinksButton'; import { MobileInterface } from '../../../mobile/MobileInterface'; import { LinkCreatedBox } from './LinkCreatedBox'; +import { LinkDescriptionPopup } from './LinkDescriptionPopup'; +import { LinkManager } from '../../util/LinkManager'; library.add(fa.faEdit, fa.faTrash, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faCompressArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faAlignCenter, fa.faCaretSquareRight, fa.faSquare, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faLink, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, fa.faLock, fa.faLaptopCode, fa.faMale, @@ -642,30 +644,46 @@ export class DocumentView extends DocComponent(Docu e.stopPropagation(); de.complete.annoDragData.linkedToDoc = true; + const linkDoc = DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link"); + LinkManager.currentLink = linkDoc; + runInAction(() => { LinkCreatedBox.popupX = de.x; - LinkCreatedBox.popupY = de.y; + LinkCreatedBox.popupY = de.y - 33; LinkCreatedBox.linkCreated = true; + + LinkDescriptionPopup.popupX = de.x; + LinkDescriptionPopup.popupY = de.y; + LinkDescriptionPopup.descriptionPopup = true; + setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); }); - - DocUtils.MakeLink({ doc: de.complete.annoDragData.annotationDocument }, { doc: this.props.Document }, "link"); } if (de.complete.linkDragData) { e.stopPropagation(); // const docs = await SearchUtil.Search(`data_l:"${destDoc[Id]}"`, true); // const views = docs.map(d => DocumentManager.Instance.getDocumentView(d)).filter(d => d).map(d => d as DocumentView); - runInAction(() => { - LinkCreatedBox.popupX = de.x; - LinkCreatedBox.popupY = de.y; - LinkCreatedBox.linkCreated = true; - setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); - }); + if (de.complete.linkDragData.linkSourceDocument !== this.props.Document) { + const linkDoc = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, + { doc: this.props.Document }, `link`); + LinkManager.currentLink = linkDoc; + + de.complete.linkDragData.linkSourceDocument !== this.props.Document && + (de.complete.linkDragData.linkDocument = linkDoc); // TODODO this is where in text links get passed + runInAction(() => { + LinkCreatedBox.popupX = de.x; + LinkCreatedBox.popupY = de.y - 33; + LinkCreatedBox.linkCreated = true; + + LinkDescriptionPopup.popupX = de.x; + LinkDescriptionPopup.popupY = de.y; + LinkDescriptionPopup.descriptionPopup = true; + + setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); + }); + } - de.complete.linkDragData.linkSourceDocument !== this.props.Document && - (de.complete.linkDragData.linkDocument = DocUtils.MakeLink({ doc: de.complete.linkDragData.linkSourceDocument }, - { doc: this.props.Document }, `link`)); // TODODO this is where in text links get passed } } diff --git a/src/client/views/nodes/LinkCreatedBox.tsx b/src/client/views/nodes/LinkCreatedBox.tsx index d157d3fca..648ae23c8 100644 --- a/src/client/views/nodes/LinkCreatedBox.tsx +++ b/src/client/views/nodes/LinkCreatedBox.tsx @@ -11,8 +11,8 @@ import { Fade } from "@material-ui/core"; export class LinkCreatedBox extends React.Component<{}> { @observable public static linkCreated: boolean = false; - @observable public static popupX: number = 600; - @observable public static popupY: number = 250; + @observable public static popupX: number = 500; + @observable public static popupY: number = 150; @action public static changeLinkCreated = () => { @@ -23,8 +23,8 @@ export class LinkCreatedBox extends React.Component<{}> { return
Link Created
; } diff --git a/src/client/views/nodes/LinkDescriptionPopup.scss b/src/client/views/nodes/LinkDescriptionPopup.scss new file mode 100644 index 000000000..474bd919b --- /dev/null +++ b/src/client/views/nodes/LinkDescriptionPopup.scss @@ -0,0 +1,62 @@ +.linkDescriptionPopup { + + display: flex; + + border: 1px solid rgb(100, 100, 100); + + width: auto; + position: absolute; + + height: auto; + z-index: 10000; + border-radius: 10px; + font-size: 12px; + //white-space: nowrap; + + background-color: rgba(250, 250, 250, 0.95); + padding-top: 9px; + padding-bottom: 9px; + padding-left: 9px; + padding-right: 9px; + + .linkDescriptionPopup-input { + float: left; + color: rgb(100, 100, 100); + } + + .linkDescriptionPopup-btn { + + float: right; + + + .linkDescriptionPopup-btn-dismiss { + background-color: white; + color: black; + display: inline; + right: 0; + border-radius: 10px; + border: 1px solid black; + padding: 3px; + font-size: 9px; + text-align: center; + position: relative; + transform: translateY(5px); + } + + .linkDescriptionPopup-btn-add { + background-color: black; + color: white; + display: inline; + right: 0; + border-radius: 10px; + border: 1px solid black; + padding: 3px; + font-size: 9px; + text-align: center; + position: relative; + transform: translateY(5px); + } + } + + +} \ No newline at end of file diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx new file mode 100644 index 000000000..078a738e7 --- /dev/null +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -0,0 +1,70 @@ +import React = require("react"); +import { observer } from "mobx-react"; +import "./LinkDescriptionPopup.scss"; +import { observable, action } from "mobx"; +import { EditableView } from "../EditableView"; +import { LinkManager } from "../../util/LinkManager"; + + +@observer +export class LinkDescriptionPopup extends React.Component<{}> { + + @observable public static descriptionPopup: boolean = false; + @observable public static popupX: number = 700; + @observable public static popupY: number = 350; + @observable description: string = ""; + @observable popupRef = React.createRef(); + + @action + descriptionChanged = (e: React.ChangeEvent) => { + this.description = e.currentTarget.value; + } + + @action + setDescription = () => { + if (LinkManager.currentLink) { + LinkManager.currentLink.description = this.description; + } + LinkDescriptionPopup.descriptionPopup = false; + } + + @action + onDismiss = () => { + LinkDescriptionPopup.descriptionPopup = false; + } + + @action + onClick = (e: PointerEvent) => { + if (this.popupRef && !!!this.popupRef.current?.contains(e.target as any)) { + LinkDescriptionPopup.descriptionPopup = false; + } + } + + @action + componentDidMount() { + document.addEventListener("pointerdown", this.onClick); + } + + componentWillUnmount() { + document.removeEventListener("pointerdown", this.onClick); + } + + render() { + return
+ this.descriptionChanged(e)}> + +
+
Dismiss
+
Add
+
+
; + } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 45fb8ba4be961f8b0b28cda05ceb5023f84cdb03 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 4 Jul 2020 23:51:30 -0500 Subject: descriptions toggle and UI cleanup --- src/client/views/collections/CollectionLinearView.scss | 12 ++++++++++++ src/client/views/collections/CollectionLinearView.tsx | 14 ++++++++++++++ src/client/views/nodes/DocumentLinksButton.tsx | 14 ++++++++------ src/client/views/nodes/LinkDescriptionPopup.scss | 13 ++++++++++--- src/client/views/nodes/LinkDescriptionPopup.tsx | 3 +++ 5 files changed, 47 insertions(+), 9 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss index 5ada79a28..b8b72e756 100644 --- a/src/client/views/collections/CollectionLinearView.scss +++ b/src/client/views/collections/CollectionLinearView.scss @@ -35,6 +35,18 @@ font-size: 12.5px; } + .bottomPopup-descriptions { + display: inline; + white-space: nowrap; + padding-left: 8px; + padding-right: 8px; + vertical-align: middle; + background-color: lightgrey; + border-radius: 5.5px; + color: black; + margin-right: 5px; + } + .bottomPopup-exit { display: inline; white-space: nowrap; diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx index 7cbe5c19d..35c28406a 100644 --- a/src/client/views/collections/CollectionLinearView.tsx +++ b/src/client/views/collections/CollectionLinearView.tsx @@ -15,6 +15,7 @@ import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { LinkDescriptionPopup } from '../nodes/LinkDescriptionPopup'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -89,6 +90,16 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { DocumentLinksButton.StartLink = undefined; } + @action + changeDescriptionSetting = () => { + if (LinkDescriptionPopup.showDescriptions === "ON") { + LinkDescriptionPopup.showDescriptions = "OFF"; + LinkDescriptionPopup.descriptionPopup = false; + } else { + LinkDescriptionPopup.showDescriptions = "ON"; + } + } + render() { const guid = Utils.GenerateGuid(); const flexDir: any = StrCast(this.Document.flexDirection); @@ -155,6 +166,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { onPointerDown={e => e.stopPropagation()} > Creating link from: {DocumentLinksButton.StartLink.title} + Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"} + Exit diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 5d1a68af5..544f5fd7f 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -96,11 +96,11 @@ export class DocumentLinksButton extends React.Component { LinkCreatedBox.popupX = e.screenX; - LinkCreatedBox.popupY = e.screenY - 33; + LinkCreatedBox.popupY = e.screenY - 133; LinkCreatedBox.linkCreated = true; LinkDescriptionPopup.popupX = e.screenX; - LinkDescriptionPopup.popupY = e.screenY; + LinkDescriptionPopup.popupY = e.screenY - 100; LinkDescriptionPopup.descriptionPopup = true; setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); @@ -124,12 +124,14 @@ export class DocumentLinksButton extends React.Component { LinkCreatedBox.popupX = e.screenX; - LinkCreatedBox.popupY = e.screenY - 33; + LinkCreatedBox.popupY = e.screenY - 133; LinkCreatedBox.linkCreated = true; - LinkDescriptionPopup.popupX = e.screenX; - LinkDescriptionPopup.popupY = e.screenY; - LinkDescriptionPopup.descriptionPopup = true; + if (LinkDescriptionPopup.showDescriptions === "ON" || !LinkDescriptionPopup.showDescriptions) { + LinkDescriptionPopup.popupX = e.screenX; + LinkDescriptionPopup.popupY = e.screenY - 100; + LinkDescriptionPopup.descriptionPopup = true; + } setTimeout(action(() => { LinkCreatedBox.linkCreated = false; }), 2500); }); diff --git a/src/client/views/nodes/LinkDescriptionPopup.scss b/src/client/views/nodes/LinkDescriptionPopup.scss index 474bd919b..54002fd1b 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.scss +++ b/src/client/views/nodes/LinkDescriptionPopup.scss @@ -2,7 +2,7 @@ display: flex; - border: 1px solid rgb(100, 100, 100); + border: 1px solid rgb(170, 26, 26); width: auto; position: absolute; @@ -21,13 +21,19 @@ .linkDescriptionPopup-input { float: left; + background-color: rgba(250, 250, 250, 0.95); color: rgb(100, 100, 100); + border: none; + min-width: 160px; } .linkDescriptionPopup-btn { float: right; + justify-content: center; + vertical-align: middle; + .linkDescriptionPopup-btn-dismiss { background-color: white; @@ -40,7 +46,8 @@ font-size: 9px; text-align: center; position: relative; - transform: translateY(5px); + margin-right: 4px; + justify-content: center; } .linkDescriptionPopup-btn-add { @@ -54,7 +61,7 @@ font-size: 9px; text-align: center; position: relative; - transform: translateY(5px); + justify-content: center; } } diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index 078a738e7..3bb52d9fb 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -4,12 +4,14 @@ import "./LinkDescriptionPopup.scss"; import { observable, action } from "mobx"; import { EditableView } from "../EditableView"; import { LinkManager } from "../../util/LinkManager"; +import { LinkCreatedBox } from "./LinkCreatedBox"; @observer export class LinkDescriptionPopup extends React.Component<{}> { @observable public static descriptionPopup: boolean = false; + @observable public static showDescriptions: string = "ON"; @observable public static popupX: number = 700; @observable public static popupY: number = 350; @observable description: string = ""; @@ -37,6 +39,7 @@ export class LinkDescriptionPopup extends React.Component<{}> { onClick = (e: PointerEvent) => { if (this.popupRef && !!!this.popupRef.current?.contains(e.target as any)) { LinkDescriptionPopup.descriptionPopup = false; + LinkCreatedBox.linkCreated = false; } } -- cgit v1.2.3-70-g09d2 From 0438137cd435c47ce334b15a4ad00cbd70d80662 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 7 Jul 2020 00:23:24 -0400 Subject: made docFilters apply to links. added hypothesis api token input --- .../apis/HypothesisAuthenticationManager.scss | 26 ++++ .../apis/HypothesisAuthenticationManager.tsx | 158 +++++++++++++++++++++ src/client/documents/Documents.ts | 41 ++++++ src/client/util/CurrentUserUtils.ts | 1 + src/client/views/GlobalKeyHandler.ts | 2 + src/client/views/MainView.tsx | 2 + src/client/views/collections/CollectionSubView.tsx | 43 +----- src/client/views/nodes/DocumentView.tsx | 2 +- src/server/ApiManagers/HypothesisManager.ts | 44 ++++++ src/server/ApiManagers/UploadManager.ts | 3 +- src/server/apis/google/GoogleApiServerUtils.ts | 3 +- src/server/index.ts | 3 +- 12 files changed, 285 insertions(+), 43 deletions(-) create mode 100644 src/client/apis/HypothesisAuthenticationManager.scss create mode 100644 src/client/apis/HypothesisAuthenticationManager.tsx create mode 100644 src/server/ApiManagers/HypothesisManager.ts (limited to 'src/client/views/nodes') diff --git a/src/client/apis/HypothesisAuthenticationManager.scss b/src/client/apis/HypothesisAuthenticationManager.scss new file mode 100644 index 000000000..bd30dd94f --- /dev/null +++ b/src/client/apis/HypothesisAuthenticationManager.scss @@ -0,0 +1,26 @@ +.authorize-container { + display: flex; + flex-direction: column; + align-items: center; + + .paste-target { + padding: 5px; + width: 100%; + } + + .avatar { + border-radius: 50%; + } + + .welcome { + font-style: italic; + margin-top: 15px; + } + + .disconnect { + font-size: 10px; + margin-top: 20px; + color: red; + cursor: grab; + } +} \ No newline at end of file diff --git a/src/client/apis/HypothesisAuthenticationManager.tsx b/src/client/apis/HypothesisAuthenticationManager.tsx new file mode 100644 index 000000000..cffb87227 --- /dev/null +++ b/src/client/apis/HypothesisAuthenticationManager.tsx @@ -0,0 +1,158 @@ +import { observable, action, reaction, runInAction, IReactionDisposer } from "mobx"; +import { observer } from "mobx-react"; +import * as React from "react"; +import MainViewModal from "../views/MainViewModal"; +import { Opt } from "../../fields/Doc"; +import { Networking } from "../Network"; +import "./HypothesisAuthenticationManager.scss"; +import { Scripting } from "../util/Scripting"; + +const prompt = "Paste authorization code here..."; + +@observer +export default class HypothesisAuthenticationManager extends React.Component<{}> { + public static Instance: HypothesisAuthenticationManager; + private authenticationLink: Opt = undefined; + @observable private openState = false; + @observable private authenticationCode: Opt = undefined; + @observable private showPasteTargetState = false; + @observable private success: Opt = undefined; + @observable private displayLauncher = true; + @observable private credentials: string; + private disposer: Opt; + + private set isOpen(value: boolean) { + runInAction(() => this.openState = value); + } + + private set shouldShowPasteTarget(value: boolean) { + runInAction(() => this.showPasteTargetState = value); + } + + public cancel() { + this.openState && this.resetState(0, 0); + } + + public fetchAccessToken = async (displayIfFound = false) => { + let response: any = await Networking.FetchFromServer("/readHypothesisAccessToken"); + // if this is an authentication url, activate the UI to register the new access token + if (!response) { // new RegExp(AuthenticationUrl).test(response)) { + this.isOpen = true; + this.authenticationLink = response; + return new Promise(async resolve => { + this.disposer?.(); + this.disposer = reaction( + () => this.authenticationCode, + async authenticationCode => { + if (authenticationCode) { + this.disposer?.(); + Networking.PostToServer("/writeHypothesisAccessToken", { authenticationCode }); + runInAction(() => { + this.success = true; + this.credentials = response; + }); + this.resetState(); + resolve(authenticationCode); + } + } + ); + }); + } + + if (displayIfFound) { + runInAction(() => { + this.success = true; + this.credentials = response; + }); + this.resetState(-1, -1); + this.isOpen = true; + } + return response.access_token; + } + + resetState = action((visibleForMS: number = 3000, fadesOutInMS: number = 500) => { + if (!visibleForMS && !fadesOutInMS) { + runInAction(() => { + this.isOpen = false; + this.success = undefined; + this.displayLauncher = true; + this.credentials = ""; + this.shouldShowPasteTarget = false; + this.authenticationCode = undefined; + }); + return; + } + this.authenticationCode = undefined; + this.displayLauncher = false; + this.shouldShowPasteTarget = false; + if (visibleForMS > 0 && fadesOutInMS > 0) { + setTimeout(action(() => { + this.isOpen = false; + setTimeout(action(() => { + this.success = undefined; + this.displayLauncher = true; + this.credentials = ""; + }), fadesOutInMS); + }), visibleForMS); + } + }); + + constructor(props: {}) { + super(props); + HypothesisAuthenticationManager.Instance = this; + } + + private get renderPrompt() { + return ( +
+ + {this.displayLauncher ? : (null)} + {this.showPasteTargetState ? this.authenticationCode = e.currentTarget.value)} + placeholder={prompt} + /> : (null)} + {this.credentials ? + <> + Welcome to Dash, {this.credentials} + +
{ + await Networking.FetchFromServer("/revokeHypothesisAccessToken"); + this.resetState(0, 0); + }} + >Disconnect Account
+ : (null)} +
+ ); + } + + private get dialogueBoxStyle() { + const borderColor = this.success === undefined ? "black" : this.success ? "green" : "red"; + return { borderColor, transition: "0.2s borderColor ease" }; + } + + render() { + return ( + + ); + } + +} + +Scripting.addGlobal("HypothesisAuthenticationManager", HypothesisAuthenticationManager); \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f81c25bab..3355c0091 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -829,6 +829,47 @@ export namespace Docs { } export namespace DocUtils { + export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField) { + const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; + + const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields + for (let i = 0; i < docFilters.length; i += 3) { + const [key, value, modifiers] = docFilters.slice(i, i + 3); + if (!filterFacets[key]) { + filterFacets[key] = {}; + } + filterFacets[key][value] = modifiers; + } + + const filteredDocs = docFilters.length ? childDocs.filter(d => { + for (const facetKey of Object.keys(filterFacets)) { + const facet = filterFacets[facetKey]; + const satisfiesFacet = Object.keys(facet).some(value => { + if (facet[value] === "match") { + return d[facetKey] === undefined || Field.toString(d[facetKey] as Field).includes(value); + } + return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value); + }); + if (!satisfiesFacet) { + return false; + } + } + return true; + }) : childDocs; + const rangeFilteredDocs = filteredDocs.filter(d => { + for (let i = 0; i < docRangeFilters.length; i += 3) { + const key = docRangeFilters[i]; + const min = Number(docRangeFilters[i + 1]); + const max = Number(docRangeFilters[i + 2]); + const val = Cast(d[key], "number", null); + if (val !== undefined && (val < min || val > max)) { + return false; + } + } + return true; + }); + return rangeFilteredDocs; + } export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) { targetID = targetID.replace(/^-/, "").replace(/\([0-9]*\)$/, ""); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 4276e04e4..9f04aab04 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -424,6 +424,7 @@ export class CurrentUserUtils { { title: "Drag a document previewer", label: "Prev", icon: "expand", click: 'openOnRight(getCopy(this.dragFactory, true))', drag: 'getCopy(this.dragFactory,true)', dragFactory: doc.emptyDocHolder as Doc }, { title: "Toggle a Calculator REPL", label: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, { title: "Connect a Google Account", label: "Google Account", icon: "external-link-alt", click: 'GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' }, + { title: "Connect a Hypothesis Account", label: "Hypothesis Account", icon: "houzz", click: 'HypothesisAuthenticationManager.Instance.fetchOrGenerateAccessToken(true)' }, ]; } diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts index 34f666f62..a3a023164 100644 --- a/src/client/views/GlobalKeyHandler.ts +++ b/src/client/views/GlobalKeyHandler.ts @@ -7,6 +7,7 @@ import { List } from "../../fields/List"; import { ScriptField } from "../../fields/ScriptField"; import { Cast, PromiseValue } from "../../fields/Types"; import GoogleAuthenticationManager from "../apis/GoogleAuthenticationManager"; +import HypothesisAuthenticationManager from "../apis/HypothesisAuthenticationManager"; import { DocServer } from "../DocServer"; import { DocumentType } from "../documents/DocumentTypes"; import { DictationManager } from "../util/DictationManager"; @@ -104,6 +105,7 @@ export default class KeyManager { DictationManager.Controls.stop(); // RecommendationsBox.Instance.closeMenu(); GoogleAuthenticationManager.Instance.cancel(); + HypothesisAuthenticationManager.Instance.cancel(); SharingManager.Instance.close(); break; case "delete": diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 15f818d1f..eba9bb344 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -61,6 +61,7 @@ import { LinkMenu } from './linking/LinkMenu'; import { LinkDocPreview } from './nodes/LinkDocPreview'; import { Fade } from '@material-ui/core'; import { LinkCreatedBox } from './nodes/LinkCreatedBox'; +import HypothesisAuthenticationManager from '../apis/HypothesisAuthenticationManager'; @observer export class MainView extends React.Component { @@ -601,6 +602,7 @@ export class MainView extends React.Component { + diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index ed8535ecb..ce6872695 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -106,16 +106,6 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: [...this.props.docFilters(), ...Cast(this.props.Document._docFilters, listSpec("string"), [])]; } @computed get childDocs() { - const docFilters = this.docFilters(); - const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - for (let i = 0; i < docFilters.length; i += 3) { - const [key, value, modifiers] = docFilters.slice(i, i + 3); - if (!filterFacets[key]) { - filterFacets[key] = {}; - } - filterFacets[key][value] = modifiers; - } let rawdocs: (Doc | Promise)[] = []; if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; @@ -128,38 +118,13 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: const rootDoc = Cast(this.props.Document.rootDocument, Doc, null); rawdocs = rootDoc && !this.props.annotationsKey ? [Doc.GetProto(rootDoc)] : []; } + const docs = rawdocs.filter(d => !(d instanceof Promise)).map(d => d as Doc); + const docFilters = this.docFilters(); const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); - const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; + const docRangeFilters = this.props.ignoreFields?.includes("_docRangeFilters") ? [] : Cast(this.props.Document._docRangeFilters, listSpec("string"), []); - const filteredDocs = docFilters.length && !this.props.dontRegisterView ? childDocs.filter(d => { - for (const facetKey of Object.keys(filterFacets)) { - const facet = filterFacets[facetKey]; - const satisfiesFacet = Object.keys(facet).some(value => { - if (facet[value] === "match") { - return d[facetKey] === undefined || Field.toString(d[facetKey] as Field).includes(value); - } - return (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value); - }); - if (!satisfiesFacet) { - return false; - } - } - return true; - }) : childDocs; - const rangeFilteredDocs = filteredDocs.filter(d => { - for (let i = 0; i < docRangeFilters.length; i += 3) { - const key = docRangeFilters[i]; - const min = Number(docRangeFilters[i + 1]); - const max = Number(docRangeFilters[i + 2]); - const val = Cast(d[key], "number", null); - if (val !== undefined && (val < min || val > max)) { - return false; - } - } - return true; - }); - return rangeFilteredDocs; + return this.props.Document.dontRegisterView ? docs : DocUtils.FilterDocs(docs, docFilters, docRangeFilters, viewSpecScript); } @action diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index b38db9a1e..3311bfa33 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1094,7 +1094,7 @@ export class DocumentView extends DocComponent(Docu return (this.props.treeViewDoc && this.props.LayoutTemplateString) || // render nothing for: tree view anchor dots this.layoutDoc.presBox || // presentationbox nodes this.props.dontRegisterView ? (null) : // view that are not registered - DocListCast(this.Document.links).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => + DocUtils.FilterDocs(DocListCast(this.Document.links), this.props.docFilters(), []).filter(d => !d.hidden && this.isNonTemporalLink).map((d, i) => { + if (existsSync(serverPathToFile(Directory.hypothesis, user.id))) { + const read = readFileSync(serverPathToFile(Directory.hypothesis, user.id), "base64") || ""; + console.log("READ = " + read); + res.send(read); + } else res.send(""); + } + }); + + register({ + method: Method.POST, + subscription: "/writeHypothesisAccessToken", + secureHandler: async ({ user, req, res }) => { + const write = req.body.authenticationCode; + console.log("WRITE = " + write); + res.send(await writeFile(serverPathToFile(Directory.hypothesis, user.id), write, "base64", () => { })); + } + }); + + register({ + method: Method.GET, + subscription: "/revokeHypothesisAccessToken", + secureHandler: async ({ user, res }) => { + await Database.Auxiliary.GoogleAccessToken.Revoke("dash-hyp-" + user.id); + res.send(); + } + }); + + } +} \ No newline at end of file diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index fe39b84e6..55ceab9fb 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -24,7 +24,8 @@ export enum Directory { pdfs = "pdfs", text = "text", pdf_thumbnails = "pdf_thumbnails", - audio = "audio" + audio = "audio", + hypothesis = "hypothesis" } export function serverPathToFile(directory: Directory, filename: string) { diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts index 20f96f432..b0157a85f 100644 --- a/src/server/apis/google/GoogleApiServerUtils.ts +++ b/src/server/apis/google/GoogleApiServerUtils.ts @@ -39,7 +39,8 @@ export namespace GoogleApiServerUtils { */ export enum Service { Documents = "Documents", - Slides = "Slides" + Slides = "Slides", + Hypothesis = "Hypothesis" } /** diff --git a/src/server/index.ts b/src/server/index.ts index 083173bb5..9af4b00bc 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -18,10 +18,10 @@ import PDFManager from "./ApiManagers/PDFManager"; import UploadManager from "./ApiManagers/UploadManager"; import { log_execution } from "./ActionUtilities"; import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager"; +import HypothesisManager from "./ApiManagers/HypothesisManager"; import GooglePhotosManager from "./ApiManagers/GooglePhotosManager"; import { Logger } from "./ProcessFactory"; import { yellow } from "colors"; -import { DashSessionAgent } from "./DashSession/DashSessionAgent"; import SessionManager from "./ApiManagers/SessionManager"; import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent"; @@ -72,6 +72,7 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: new DeleteManager(), new UtilManager(), new GeneralGoogleManager(), + new HypothesisManager(), new GooglePhotosManager(), ]; -- cgit v1.2.3-70-g09d2 From e7326c10d2f94c11dedec1d0a280579ba8fa7af0 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Mon, 6 Jul 2020 23:48:44 -0500 Subject: andy's UI changes --- src/client/views/linking/LinkMenu.scss | 4 +++- src/client/views/linking/LinkMenuItem.scss | 7 ++++--- src/client/views/linking/LinkMenuItem.tsx | 2 +- src/client/views/nodes/DocumentLinksButton.tsx | 8 ++++++-- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss index 4b9f5641a..b0c729cda 100644 --- a/src/client/views/linking/LinkMenu.scss +++ b/src/client/views/linking/LinkMenu.scss @@ -3,6 +3,7 @@ .linkMenu { width: 100%; height: auto; + border: 1px solid black; &:hover { width: calc(auto + 26px); @@ -10,11 +11,12 @@ } .linkMenu-list { + border: 1px solid black; max-height: 200px; overflow-y: scroll; position: absolute; z-index: 10; - background: $link-color; + background: white; min-width: 150px; border-radius: 5px; padding-top: 6.5px; diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss index 7fecc2820..67bf71fb9 100644 --- a/src/client/views/linking/LinkMenuItem.scss +++ b/src/client/views/linking/LinkMenuItem.scss @@ -16,7 +16,7 @@ .linkMenu-destination-title { text-decoration: none; - color: rgb(46, 82, 160); + color: rgb(85, 120, 196); font-size: 14px; padding-bottom: 2px; } @@ -62,8 +62,9 @@ .linkMenu-destination-title { text-decoration: underline; - color: rgb(15, 57, 148); - display: inline; + color: rgb(60, 90, 156); + //display: inline; + text-overflow: break; } &.expand-two p { diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 9f6b47375..bd4024902 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -201,7 +201,7 @@ export class LinkMenuItem extends React.Component { : <>}
-
+
{/*
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 544f5fd7f..7fb447cab 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -146,10 +146,14 @@ export class DocumentLinksButton extends React.Component +
Date: Tue, 7 Jul 2020 10:20:00 -0400 Subject: fixed potential infinite loop in richtextmenu --- src/client/views/nodes/formattedText/RichTextMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 9075a6486..f10c425d4 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -219,7 +219,7 @@ export default class RichTextMenu extends AntimodeMenu { getActiveAlignment() { if (this.view) { const path = (this.view.state.selection.$from as any).path; - for (let i = path.length - 3; i < path.length; i -= 3) { + for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) { if (path[i]?.type === this.view.state.schema.nodes.paragraph) { return path[i].attrs.align || "left"; } -- cgit v1.2.3-70-g09d2 From 3f704ee2941fef77dd0eafebeb0a6ffb0a946a8b Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Tue, 7 Jul 2020 12:04:21 -0400 Subject: fixes for audio document linkiing --- src/client/documents/Documents.ts | 2 +- src/client/views/nodes/AudioBox.scss | 1 + src/client/views/nodes/AudioBox.tsx | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ad62169ab..fa85d58f0 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -906,7 +906,7 @@ export namespace DocUtils { export let ActiveRecordings: Doc[] = []; export function MakeLinkToActiveAudio(doc: Doc) { - DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "", "audio timeline")); + DocUtils.ActiveRecordings.map(d => DocUtils.MakeLink({ doc: doc }, { doc: d }, "audio link", "audio timeline")); } export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string) { diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index c959b79f5..e9420a072 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -5,6 +5,7 @@ position: inherit; display: flex; pointer-events: all; + position: relative; cursor: default; .audiobox-buttons { diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index d5288fff6..5c921cea4 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -268,6 +268,7 @@ export class AudioBox extends ViewBoxBaseComponent -- cgit v1.2.3-70-g09d2