aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/DocServer.ts12
-rw-r--r--src/client/apis/youtube/YoutubeBox.scss13
-rw-r--r--src/client/apis/youtube/YoutubeBox.tsx112
-rw-r--r--src/client/documents/Documents.ts16
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/nodes/DocumentContentsView.tsx3
6 files changed, 155 insertions, 5 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 6737657c8..1e376e92f 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,5 +1,5 @@
import * as OpenSocket from 'socket.io-client';
-import { MessageStore, Diff } from "./../server/Message";
+import { MessageStore, Diff, YoutubeQueryTypes } from "./../server/Message";
import { Opt } from '../new_fields/Doc';
import { Utils, emptyFunction } from '../Utils';
import { SerializationHelper } from './util/SerializationHelper';
@@ -167,6 +167,16 @@ export namespace DocServer {
return _GetRefField(id);
}
+ export async function getYoutubeChannels() {
+ let apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
+ return apiKey;
+ }
+
+ export function getYoutubeVideos(videoTitle: string, callBack: (videos: any[]) => void) {
+ Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.SearchVideo, userInput: videoTitle }, callBack);
+ }
+
+
/**
* Given a list of Doc GUIDs, this utility function will asynchronously attempt to each id's associated
* field, first looking in the RefField cache and then communicating with
diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss
new file mode 100644
index 000000000..e6ccfea90
--- /dev/null
+++ b/src/client/apis/youtube/YoutubeBox.scss
@@ -0,0 +1,13 @@
+ul {
+ list-style-type: none;
+}
+
+
+li {
+ margin: 4px;
+}
+
+li:hover {
+ cursor: pointer;
+ opacity: 0.8;
+} \ No newline at end of file
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
new file mode 100644
index 000000000..3e7e9e06d
--- /dev/null
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -0,0 +1,112 @@
+import "../../views/nodes/WebBox.scss";
+import React = require("react");
+import { FieldViewProps, FieldView } from "../../views/nodes/FieldView";
+import { HtmlField } from "../../../new_fields/HtmlField";
+import { WebField } from "../../../new_fields/URLField";
+import { observer } from "mobx-react";
+import { computed, reaction, IReactionDisposer, observable, action } from 'mobx';
+import { DocumentDecorations } from "../../views/DocumentDecorations";
+import { InkingControl } from "../../views/InkingControl";
+import { Utils } from "../../../Utils";
+import { DocServer } from "../../DocServer";
+import { NumCast } from "../../../new_fields/Types";
+import "./YoutubeBox.scss";
+
+
+@observer
+export class YoutubeBox extends React.Component<FieldViewProps> {
+
+ @observable YoutubeSearchElement: HTMLInputElement | undefined;
+ @observable searchResultsFound: boolean = false;
+ @observable searchResults: any[] = [];
+ @observable videoClicked: boolean = false;
+ @observable selectedVideoUrl: string = "";
+
+ public static LayoutString() { return FieldView.LayoutString(YoutubeBox); }
+
+ componentWillMount() {
+ DocServer.getYoutubeChannels();
+ }
+
+ _ignore = 0;
+ onPreWheel = (e: React.WheelEvent) => {
+ this._ignore = e.timeStamp;
+ }
+ onPrePointer = (e: React.PointerEvent) => {
+ this._ignore = e.timeStamp;
+ }
+ onPostPointer = (e: React.PointerEvent) => {
+ if (this._ignore !== e.timeStamp) {
+ e.stopPropagation();
+ }
+ }
+ onPostWheel = (e: React.WheelEvent) => {
+ if (this._ignore !== e.timeStamp) {
+ e.stopPropagation();
+ }
+ }
+
+ onEnterKeyDown = (e: React.KeyboardEvent) => {
+ if (e.keyCode === 13) {
+ let submittedTitle = this.YoutubeSearchElement!.value;
+ this.YoutubeSearchElement!.value = "";
+ this.YoutubeSearchElement!.blur();
+ DocServer.getYoutubeVideos(submittedTitle, this.processesVideoResults);
+
+ }
+ }
+
+ @action
+ processesVideoResults = (videos: any[]) => {
+ this.searchResults = videos;
+ if (this.searchResults.length > 0) {
+ this.searchResultsFound = true;
+ this.searchResults.forEach((video) => console.log("Image Url", video.snippet));
+ if (this.videoClicked) {
+ this.videoClicked = false;
+ }
+ }
+ }
+
+ renderSearchResultsOrVideo = () => {
+ if (this.searchResultsFound) {
+ return <ul>
+ {this.searchResults.map((video) => {
+ return <li onClick={() => this.embedVideoOnClick(video.id.videoId)} key={video.id.videoId}><img src={video.snippet.thumbnails.medium.url} /> {video.snippet.title}</li>;
+ })}
+ </ul>;
+ } else if (this.videoClicked) {
+ return <iframe src={this.selectedVideoUrl} height={NumCast(this.props.Document.height) - 40} width={NumCast(this.props.Document.width)}></iframe>;
+ } else {
+ return (null);
+ }
+ }
+
+ @action
+ embedVideoOnClick = (videoId: string) => {
+ let embeddedUrl = "https://www.youtube.com/embed/" + videoId;
+ this.selectedVideoUrl = embeddedUrl;
+ this.searchResultsFound = false;
+ this.videoClicked = true;
+ }
+
+ render() {
+ let field = this.props.Document[this.props.fieldKey];
+ let content =
+ <div style={{ width: "100%", height: "100%", position: "absolute" }} onWheel={this.onPostWheel} onPointerDown={this.onPostPointer} onPointerMove={this.onPostPointer} onPointerUp={this.onPostPointer}>
+ <input type="text" placeholder="Search for a video" onKeyDown={this.onEnterKeyDown} style={{ height: 40, width: "100%", border: "1px solid black", padding: 5, textAlign: "center" }} ref={(e) => this.YoutubeSearchElement = e!} />
+ {this.renderSearchResultsOrVideo()}
+ </div>;
+
+ let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
+
+ let classname = "webBox-cont" + (this.props.isSelected() && !InkingControl.Instance.selectedTool && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+ return (
+ <>
+ <div className={classname} >
+ {content}
+ </div>
+ {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
+ </>);
+ }
+} \ No newline at end of file
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index af2b95659..49ce8760f 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -22,7 +22,7 @@ import { MINIMIZED_ICON_SIZE } from "../views/globalCssVariables.scss";
import { IconBox } from "../views/nodes/IconBox";
import { Field, Doc, Opt } from "../../new_fields/Doc";
import { OmitKeys } from "../../Utils";
-import { ImageField, VideoField, AudioField, PdfField, WebField } from "../../new_fields/URLField";
+import { ImageField, VideoField, AudioField, PdfField, WebField, YoutubeField } from "../../new_fields/URLField";
import { HtmlField } from "../../new_fields/HtmlField";
import { List } from "../../new_fields/List";
import { Cast, NumCast, StrCast, ToConstructor, InterfaceValue, FieldValue } from "../../new_fields/Types";
@@ -33,6 +33,7 @@ import { dropActionType } from "../util/DragManager";
import { DateField } from "../../new_fields/DateField";
import { UndoManager } from "../util/UndoManager";
import { RouteStore } from "../../server/RouteStore";
+import { YoutubeBox } from "../apis/youtube/YoutubeBox";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { LinkManager } from "../util/LinkManager";
import { DocumentManager } from "../util/DocumentManager";
@@ -55,7 +56,8 @@ export enum DocumentType {
ICON = "icon",
IMPORT = "import",
LINK = "link",
- LINKDOC = "linkdoc"
+ LINKDOC = "linkdoc",
+ YOUTUBE = "youtube"
}
export interface DocumentOptions {
@@ -160,7 +162,11 @@ export namespace Docs {
data: new List<Doc>(),
layout: { view: EmptyBox },
options: {}
- }]
+ }],
+ [DocumentType.YOUTUBE, {
+ layout: { view: YoutubeBox }
+ }],
+
]);
// All document prototypes are initialized with at least these values
@@ -329,6 +335,10 @@ export namespace Docs {
return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(new URL(url)), options);
}
+ export function YoutubeDocument(url: string, options: DocumentOptions = {}) {
+ return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(new URL(url)), options);
+ }
+
export function AudioDocument(url: string, options: DocumentOptions = {}) {
return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(new URL(url)), options);
}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ce7369220..5a2e0c6c3 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -123,6 +123,7 @@ export class MainView extends React.Component {
library.add(faFilm);
library.add(faMusic);
library.add(faTree);
+ library.add(faPlay);
library.add(faClone);
library.add(faCut);
library.add(faCommentAlt);
@@ -379,11 +380,14 @@ export class MainView extends React.Component {
let addTreeNode = action(() => CurrentUserUtils.UserDocument);
let addImageNode = action(() => Docs.Create.ImageDocument(imgurl, { width: 200, title: "an image of a cat" }));
let addImportCollectionNode = action(() => Docs.Create.DirectoryImportDocument({ title: "Directory Import", width: 400, height: 400 }));
+ let youtubeurl = "https://www.youtube.com/embed/TqcApsGRzWw";
+ let addYoutubeSearcher = action(() => Docs.YoutubeDocument(youtubeurl, { width: 200, height: 200, title: "youtube node" }));
let btns: [React.RefObject<HTMLDivElement>, IconName, string, () => Doc][] = [
[React.createRef<HTMLDivElement>(), "object-group", "Add Collection", addColNode],
// [React.createRef<HTMLDivElement>(), "clone", "Add Docking Frame", addDockingNode],
[React.createRef<HTMLDivElement>(), "cloud-upload-alt", "Import Directory", addImportCollectionNode],
+ [React.createRef<HTMLDivElement>(), "play", "Add Youtube Searcher", addYoutubeSearcher]
];
if (!ClientUtils.RELEASE) btns.unshift([React.createRef<HTMLDivElement>(), "cat", "Add Cat Image", addImageNode]);
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index ed6b224a7..fa8d5dca3 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -17,6 +17,7 @@ import { PDFBox } from "./PDFBox";
import { VideoBox } from "./VideoBox";
import { FieldView } from "./FieldView";
import { WebBox } from "./WebBox";
+import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
import { HistogramBox } from "../../northstar/dash-nodes/HistogramBox";
import React = require("react");
import { FieldViewProps } from "./FieldView";
@@ -97,7 +98,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & {
if (this.props.renderDepth > 7) return (null);
if (!this.layout && (this.props.layoutKey !== "overlayLayout" || !this.templates.length)) return (null);
return <ObserverJsxParser
- components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox }}
+ components={{ FormattedTextBox, ImageBox, IconBox, DirectoryImportBox, FieldView, CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, CollectionPDFView, CollectionVideoView, WebBox, KeyValueBox, PDFBox, VideoBox, AudioBox, HistogramBox, YoutubeBox }}
bindings={this.CreateBindings()}
jsx={this.finalLayout}
showWarnings={true}