From b285803c4e8c37302f6e02624a6127667d628305 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 25 Jun 2019 19:49:54 -0400 Subject: Youtube Api Exploration --- src/client/apis/youtube/YoutubeBox.tsx | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/client/apis/youtube/YoutubeBox.tsx (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx new file mode 100644 index 000000000..ee190750f --- /dev/null +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -0,0 +1,72 @@ +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 } from 'mobx'; +import { DocumentDecorations } from "../../views/DocumentDecorations"; +import { InkingControl } from "../../views/InkingControl"; +import * as YoutubeApi from "./youtubeApiSample"; +import { Utils } from "../../../Utils"; +import { DocServer } from "../../DocServer"; + + +@observer +export class YoutubeBox extends React.Component { + + private youtubeApiKey: string = ""; + + public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } + + async componentWillMount() { + let apiKey = await DocServer.getYoutubeApiKey(); + this.youtubeApiKey = apiKey; + YoutubeApi.authorizedGetChannel(this.youtubeApiKey); + } + + _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(); + } + } + render() { + let field = this.props.Document[this.props.fieldKey]; + let view; + YoutubeApi.readFsFile(); + if (field instanceof HtmlField) { + view = ; + } else if (field instanceof WebField) { + view = ; } 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 =
- this.YoutubeSearchElement = e!} /> - {this.renderSearchResults()} + this.YoutubeSearchElement = e!} /> + {this.renderSearchResultsOrVideo()}
; let frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting; -- cgit v1.2.3-70-g09d2 From 49edd4e6071d0ea84cd0a652d69acb826866c99b Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Wed, 17 Jul 2019 17:50:45 -0400 Subject: New VideoBox spawns when a youtube video search result is clicked --- src/client/apis/youtube/YoutubeBox.tsx | 31 ++++++++++++++++++++++++------- src/server/youtubeApi/youtubeApiSample.js | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 3e7e9e06d..e7913da9e 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -11,6 +11,7 @@ import { Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; import { NumCast } from "../../../new_fields/Types"; import "./YoutubeBox.scss"; +import { Docs } from "../../documents/Documents"; @observer @@ -25,7 +26,7 @@ export class YoutubeBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } componentWillMount() { - DocServer.getYoutubeChannels(); + //DocServer.getYoutubeChannels(); } _ignore = 0; @@ -59,34 +60,50 @@ export class YoutubeBox extends React.Component { @action processesVideoResults = (videos: any[]) => { this.searchResults = videos; + console.log("Results: ", this.searchResults); if (this.searchResults.length > 0) { this.searchResultsFound = true; - this.searchResults.forEach((video) => console.log("Image Url", video.snippet)); if (this.videoClicked) { this.videoClicked = false; } } } + filterYoutubeTitleResult = (resultTitle: string) => { + let processedTitle: string = resultTitle.ReplaceAll("&", "&"); + processedTitle = processedTitle.ReplaceAll("'", "'"); + processedTitle = processedTitle.ReplaceAll(""", "\""); + return processedTitle; + } + renderSearchResultsOrVideo = () => { if (this.searchResultsFound) { return
    {this.searchResults.map((video) => { - return
  • this.embedVideoOnClick(video.id.videoId)} key={video.id.videoId}> {video.snippet.title}
  • ; + let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); + return
  • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
  • ; })}
; - } else if (this.videoClicked) { - return ; + // } else if (this.videoClicked) { + // return ; + // } } else { return (null); } } @action - embedVideoOnClick = (videoId: string) => { + embedVideoOnClick = (videoId: string, filteredTitle: string) => { let embeddedUrl = "https://www.youtube.com/embed/" + videoId; this.selectedVideoUrl = embeddedUrl; - this.searchResultsFound = false; + let addFunction = this.props.addDocument!; + let newVideoX = NumCast(this.props.Document.x) + NumCast(this.props.Document.width); + let newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height); + + addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, width: 400, height: 315, x: newVideoX, y: newVideoY })); + + //this.props.addDocument(Docs.Create.VideoDocument(embeddedUrl, { title: embeddedUrl, width: 400, height: 315 })); + //this.searchResultsFound = false; this.videoClicked = true; } diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js index e95f99015..f875812d5 100644 --- a/src/server/youtubeApi/youtubeApiSample.js +++ b/src/server/youtubeApi/youtubeApiSample.js @@ -153,7 +153,7 @@ function getSampleVideos(auth, args) { return; } let videos = response.data.items; - console.log('Videos found: ' + videos[0].id.videoId, " ", videos[0].snippet.title); + console.log('Videos found: ' + videos[0].id.videoId, " ", unescape(videos[0].snippet.title)); args.callBack(videos); }); } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 1f1f857847cd9ffa0fdd5001c0dd72f06ba903c0 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Wed, 17 Jul 2019 20:26:00 -0400 Subject: Titles alligned --- src/client/apis/youtube/YoutubeBox.scss | 6 ++++++ src/client/apis/youtube/YoutubeBox.tsx | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss index e6ccfea90..23f264b95 100644 --- a/src/client/apis/youtube/YoutubeBox.scss +++ b/src/client/apis/youtube/YoutubeBox.scss @@ -5,9 +5,15 @@ ul { li { margin: 4px; + display: inline-flex; } li:hover { cursor: pointer; opacity: 0.8; +} + +.videoTitle { + margin-left: 4px; + font-family: Arial, Helvetica, sans-serif; } \ No newline at end of file diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index e7913da9e..d94f3785c 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -81,7 +81,7 @@ export class YoutubeBox extends React.Component { return
    {this.searchResults.map((video) => { let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); - return
  • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
  • ; + return
  • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
  • ; })}
; // } else if (this.videoClicked) { -- cgit v1.2.3-70-g09d2 From 808443664d66dc5009aaace48420659cf98c6c47 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Thu, 18 Jul 2019 13:31:17 -0400 Subject: Youtube Search Results Backed Up, Key error is present --- src/client/apis/youtube/YoutubeBox.tsx | 112 +++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 12 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index d94f3785c..fa2d3fb53 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -4,14 +4,17 @@ 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 { computed, reaction, IReactionDisposer, observable, action, runInAction } 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 { NumCast, Cast, StrCast } from "../../../new_fields/Types"; import "./YoutubeBox.scss"; import { Docs } from "../../documents/Documents"; +import { Doc } from "../../../new_fields/Doc"; +import { listSpec } from "../../../new_fields/Schema"; +import { List } from "../../../new_fields/List"; @observer @@ -22,11 +25,43 @@ export class YoutubeBox extends React.Component { @observable searchResults: any[] = []; @observable videoClicked: boolean = false; @observable selectedVideoUrl: string = ""; + // @observable cachedResults: List | undefined; + @observable lisOfBackUp: JSX.Element[] = []; + public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } - componentWillMount() { + async componentWillMount() { //DocServer.getYoutubeChannels(); + let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); + if (!castedBackUpDocs) { + this.props.Document.cachedSearch = castedBackUpDocs = new List(); + } + if (castedBackUpDocs.length !== 0) { + //let awaitedRes = await castedBackUpDocs; + + this.searchResultsFound = true; + + for (let videoBackUp of castedBackUpDocs) { + let curBackUp = await videoBackUp; + let videoId = StrCast(curBackUp.videoId); + let videoTitle = StrCast(curBackUp.videoTitle); + let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); + runInAction(() => this.lisOfBackUp.push(( +
  • this.embedVideoOnClick(videoId, videoTitle)} + key={videoId} + > + + {videoTitle} +
  • ) + )); + } + + + } + + } _ignore = 0; @@ -63,12 +98,24 @@ export class YoutubeBox extends React.Component { console.log("Results: ", this.searchResults); if (this.searchResults.length > 0) { this.searchResultsFound = true; + this.backUpSearchResults(videos); if (this.videoClicked) { this.videoClicked = false; } } } + backUpSearchResults = (videos: any[]) => { + let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); + videos.forEach((video) => { + let videoBackUp = new Doc(); + videoBackUp.videoId = video.id.videoId; + videoBackUp.videoTitle = this.filterYoutubeTitleResult(video.snippet.title); + videoBackUp.thumbnailUrl = video.snippet.thumbnails.medium.url; + castedBackUpDocs!.push(videoBackUp); + }); + } + filterYoutubeTitleResult = (resultTitle: string) => { let processedTitle: string = resultTitle.ReplaceAll("&", "&"); processedTitle = processedTitle.ReplaceAll("'", "'"); @@ -76,17 +123,56 @@ export class YoutubeBox extends React.Component { return processedTitle; } + // mapSearchResults = () => { + // if (this.searchResults.length !== 0) { + // console.log("Entered here"); + // return
      { + // this.searchResults.map((video) => { + // let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); + // return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
    • ; + // })} + //
    ; + // } else if (this.cachedResults!.length !== 0) { + // return
      { + // this.cachedResults!.map(async (videoBackUp) => { + // let curBackUp = await videoBackUp; + // let videoId = StrCast(curBackUp.videoId); + // let videoTitle = StrCast(curBackUp.videoTitle); + // let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); + // return
    • this.embedVideoOnClick(videoTitle, videoTitle)} key={videoId}> {videoTitle}
    • ; + // })} + //
    ; + // } + // } + renderSearchResultsOrVideo = () => { if (this.searchResultsFound) { - return
      - {this.searchResults.map((video) => { - let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); - return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
    • ; - })} -
    ; - // } else if (this.videoClicked) { - // return ; - // } + if (this.searchResults.length !== 0) { + return
      + {this.searchResults.map((video) => { + let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); + return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
    • ; + })} +
    ; + } else if (this.lisOfBackUp.length !== 0) { + // let lis: JSX.Element[] = []; + // for (let videoBackUp of this.cachedResults!) { + // let curBackUp = await videoBackUp; + // let videoId = StrCast(curBackUp.videoId); + // let videoTitle = StrCast(curBackUp.videoTitle); + // let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); + // lis.push(( + //
  • this.embedVideoOnClick(videoTitle, videoTitle)} + // key={videoId} + // > + // + // {videoTitle} + //
  • ) + // ); + // } + return
      {this.lisOfBackUp}
    ; + } } else { return (null); } @@ -95,6 +181,7 @@ export class YoutubeBox extends React.Component { @action embedVideoOnClick = (videoId: string, filteredTitle: string) => { let embeddedUrl = "https://www.youtube.com/embed/" + videoId; + console.log("EmbeddedUrl: ", embeddedUrl); this.selectedVideoUrl = embeddedUrl; let addFunction = this.props.addDocument!; let newVideoX = NumCast(this.props.Document.x) + NumCast(this.props.Document.width); @@ -109,6 +196,7 @@ export class YoutubeBox extends React.Component { render() { let field = this.props.Document[this.props.fieldKey]; + //let results = this.renderSearchResultsOrVideo(); let content =
    this.YoutubeSearchElement = e!} /> -- cgit v1.2.3-70-g09d2 From 421311806a686dd0de2bbdd7d8aa7dbb5c9fe9d5 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Thu, 18 Jul 2019 14:14:43 -0400 Subject: Overriding allowed, keys problem still exist --- src/client/apis/youtube/YoutubeBox.tsx | 54 ++++------------------------------ 1 file changed, 5 insertions(+), 49 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index fa2d3fb53..33d989b6a 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -25,7 +25,6 @@ export class YoutubeBox extends React.Component { @observable searchResults: any[] = []; @observable videoClicked: boolean = false; @observable selectedVideoUrl: string = ""; - // @observable cachedResults: List | undefined; @observable lisOfBackUp: JSX.Element[] = []; @@ -38,7 +37,6 @@ export class YoutubeBox extends React.Component { this.props.Document.cachedSearch = castedBackUpDocs = new List(); } if (castedBackUpDocs.length !== 0) { - //let awaitedRes = await castedBackUpDocs; this.searchResultsFound = true; @@ -50,7 +48,7 @@ export class YoutubeBox extends React.Component { runInAction(() => this.lisOfBackUp.push((
  • this.embedVideoOnClick(videoId, videoTitle)} - key={videoId} + key={Utils.GenerateGuid()} > {videoTitle} @@ -95,7 +93,6 @@ export class YoutubeBox extends React.Component { @action processesVideoResults = (videos: any[]) => { this.searchResults = videos; - console.log("Results: ", this.searchResults); if (this.searchResults.length > 0) { this.searchResultsFound = true; this.backUpSearchResults(videos); @@ -106,13 +103,14 @@ export class YoutubeBox extends React.Component { } backUpSearchResults = (videos: any[]) => { - let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); + let newCachedList = new List(); + this.props.Document.cachedSearch = newCachedList; videos.forEach((video) => { let videoBackUp = new Doc(); videoBackUp.videoId = video.id.videoId; videoBackUp.videoTitle = this.filterYoutubeTitleResult(video.snippet.title); videoBackUp.thumbnailUrl = video.snippet.thumbnails.medium.url; - castedBackUpDocs!.push(videoBackUp); + newCachedList.push(videoBackUp); }); } @@ -123,54 +121,16 @@ export class YoutubeBox extends React.Component { return processedTitle; } - // mapSearchResults = () => { - // if (this.searchResults.length !== 0) { - // console.log("Entered here"); - // return
      { - // this.searchResults.map((video) => { - // let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); - // return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
    • ; - // })} - //
    ; - // } else if (this.cachedResults!.length !== 0) { - // return
      { - // this.cachedResults!.map(async (videoBackUp) => { - // let curBackUp = await videoBackUp; - // let videoId = StrCast(curBackUp.videoId); - // let videoTitle = StrCast(curBackUp.videoTitle); - // let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); - // return
    • this.embedVideoOnClick(videoTitle, videoTitle)} key={videoId}> {videoTitle}
    • ; - // })} - //
    ; - // } - // } - renderSearchResultsOrVideo = () => { if (this.searchResultsFound) { if (this.searchResults.length !== 0) { return
      {this.searchResults.map((video) => { let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); - return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={video.id.videoId}> {filteredTitle}
    • ; + return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}> {filteredTitle}
    • ; })}
    ; } else if (this.lisOfBackUp.length !== 0) { - // let lis: JSX.Element[] = []; - // for (let videoBackUp of this.cachedResults!) { - // let curBackUp = await videoBackUp; - // let videoId = StrCast(curBackUp.videoId); - // let videoTitle = StrCast(curBackUp.videoTitle); - // let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); - // lis.push(( - //
  • this.embedVideoOnClick(videoTitle, videoTitle)} - // key={videoId} - // > - // - // {videoTitle} - //
  • ) - // ); - // } return
      {this.lisOfBackUp}
    ; } } else { @@ -188,15 +148,11 @@ export class YoutubeBox extends React.Component { let newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height); addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, width: 400, height: 315, x: newVideoX, y: newVideoY })); - - //this.props.addDocument(Docs.Create.VideoDocument(embeddedUrl, { title: embeddedUrl, width: 400, height: 315 })); - //this.searchResultsFound = false; this.videoClicked = true; } render() { let field = this.props.Document[this.props.fieldKey]; - //let results = this.renderSearchResultsOrVideo(); let content =
    this.YoutubeSearchElement = e!} /> -- cgit v1.2.3-70-g09d2 From 82a9c1f854fad05db0878717aa82572ffc1290c1 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Thu, 18 Jul 2019 18:08:07 -0400 Subject: Update on coordinates --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 33d989b6a..da3c4b851 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -144,7 +144,7 @@ export class YoutubeBox extends React.Component { console.log("EmbeddedUrl: ", embeddedUrl); this.selectedVideoUrl = embeddedUrl; let addFunction = this.props.addDocument!; - let newVideoX = NumCast(this.props.Document.x) + NumCast(this.props.Document.width); + let newVideoX = NumCast(this.props.Document.x); let newVideoY = NumCast(this.props.Document.y) + NumCast(this.props.Document.height); addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, width: 400, height: 315, x: newVideoX, y: newVideoY })); -- cgit v1.2.3-70-g09d2 From 157060f7e6029c76765aa20d8fdbe325401a3880 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Fri, 19 Jul 2019 18:29:03 -0400 Subject: Youtube Search UI imitated mostly --- src/client/apis/youtube/YoutubeBox.scss | 74 +++++++++++++++++++++++++-- src/client/apis/youtube/YoutubeBox.tsx | 89 ++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 4 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss index 23f264b95..5b539b463 100644 --- a/src/client/apis/youtube/YoutubeBox.scss +++ b/src/client/apis/youtube/YoutubeBox.scss @@ -1,5 +1,6 @@ ul { list-style-type: none; + padding-inline-start: 10px; } @@ -13,7 +14,74 @@ li:hover { opacity: 0.8; } -.videoTitle { - margin-left: 4px; - font-family: Arial, Helvetica, sans-serif; +.search_wrapper { + width: 100%; + display: inline-flex; + height: 175px; + + .textual_info { + font-family: Arial, Helvetica, sans-serif; + + .videoTitle { + margin-left: 4px; + // display: inline-block; + color: #0D0D0D; + -webkit-line-clamp: 2; + display: block; + max-height: 4.8rem; + overflow: hidden; + font-size: 1.8rem; + font-weight: 400; + line-height: 2.4rem; + -webkit-box-orient: vertical; + text-overflow: ellipsis; + white-space: normal; + display: -webkit-box; + } + + .channelName { + color:#606060; + margin-left: 4px; + font-size: 1.3rem; + font-weight: 400; + line-height: 1.8rem; + text-transform: none; + margin-top: 0px; + display: inline-block; + } + + .video_description { + margin-left: 4px; + // font-size: 12px; + color: #606060; + padding-top: 8px; + margin-bottom: 8px; + display: block; + line-height: 1.8rem; + max-height: 4.2rem; + overflow: hidden; + font-size: 1.3rem; + font-weight: 400; + text-transform: none; + } + + .publish_time { + //display: inline-block; + margin-left: 8px; + padding: 0; + border: 0; + background: transparent; + color: #606060; + max-width: 100%; + line-height: 1.8rem; + max-height: 3.6rem; + overflow: hidden; + font-size: 1.3rem; + font-weight: 400; + text-transform: none; + } + + + + } } \ No newline at end of file diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index da3c4b851..7ac8d06f6 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -121,13 +121,100 @@ export class YoutubeBox extends React.Component { return processedTitle; } + roundPublishTime = (publishTime: string) => { + let date = new Date(publishTime); + let curDate = new Date(); + let videoYearDif = curDate.getFullYear() - date.getFullYear(); + let videoMonthDif = curDate.getMonth() - date.getMonth(); + let videoDayDif = curDate.getDay() - date.getDay(); + console.log("video day dif: ", videoDayDif, " first day: ", curDate.getDay(), " second day: ", date.getDay()); + let videoHoursDif = curDate.getHours() - date.getHours(); + let videoMinutesDif = curDate.getMinutes() - date.getMinutes(); + let videoSecondsDif = curDate.getSeconds() - date.getSeconds(); + if (videoYearDif !== 0) { + return videoYearDif + " years ago"; + } else if (videoMonthDif !== 0) { + return videoMonthDif + " months ago"; + } else if (videoDayDif !== 0) { + return videoDayDif + " days ago"; + } else if (videoHoursDif !== 0) { + return videoHoursDif + " hours ago"; + } else if (videoMinutesDif) { + return videoMinutesDif + " minutes ago"; + } else if (videoSecondsDif) { + return videoSecondsDif + " seconds ago"; + } + + console.log("Date : ", date); + } + + roundPublishTime2 = (publishTime: string) => { + let date = new Date(publishTime).getTime(); + let curDate = new Date().getTime(); + let timeDif = curDate - date; + let totalSeconds = timeDif / 1000; + let totalMin = totalSeconds / 60; + let totalHours = totalMin / 60; + let totalDays = totalHours / 24; + let totalMonths = totalDays / 30.417; + let totalYears = totalMonths / 12; + + + let truncYears = Math.trunc(totalYears); + let truncMonths = Math.trunc(totalMonths); + let truncDays = Math.trunc(totalDays); + let truncHours = Math.trunc(totalHours); + let truncMin = Math.trunc(totalMin); + let truncSec = Math.trunc(totalSeconds); + + let pluralCase = ""; + + if (truncYears !== 0) { + truncYears > 1 ? pluralCase = "s" : pluralCase = ""; + return truncYears + " year" + pluralCase + " ago"; + } else if (truncMonths !== 0) { + truncMonths > 1 ? pluralCase = "s" : pluralCase = ""; + return truncMonths + " month" + pluralCase + " ago"; + } else if (truncDays !== 0) { + truncDays > 1 ? pluralCase = "s" : pluralCase = ""; + return truncDays + " day" + pluralCase + " ago"; + } else if (truncHours !== 0) { + truncHours > 1 ? pluralCase = "s" : pluralCase = ""; + return truncHours + " hour" + pluralCase + " ago"; + } else if (truncMin !== 0) { + truncMin > 1 ? pluralCase = "s" : pluralCase = ""; + return truncMin + " minute" + pluralCase + " ago"; + } else if (truncSec !== 0) { + truncSec > 1 ? pluralCase = "s" : pluralCase = ""; + return truncSec + " second" + pluralCase + " ago"; + } + } + renderSearchResultsOrVideo = () => { if (this.searchResultsFound) { if (this.searchResults.length !== 0) { return
      {this.searchResults.map((video) => { let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); - return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}> {filteredTitle}
    • ; + let channelTitle = video.snippet.channelTitle; + let videoDescription = video.snippet.description; + let pusblishDate = this.roundPublishTime2(video.snippet.publishedAt); + // let duration = video.contentDetails.duration; + //let viewCount = video.statistics.viewCount; + //this.roundPublishTime(pusblishDate); + //this.roundPublishTime2(video.snippet.publishedAt); + return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}> +
      + +
      + {filteredTitle} + {channelTitle} + {pusblishDate} + {/*
      {viewCount}
      */} +

      {videoDescription}

      +
      +
      +
    • ; })}
    ; } else if (this.lisOfBackUp.length !== 0) { -- cgit v1.2.3-70-g09d2 From 4446a3a52c4cf4b03c201ab2d6a9179647686e40 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Mon, 22 Jul 2019 18:47:14 -0400 Subject: Pulled Duration and ViewCount details, Need to csss duration --- src/client/DocServer.ts | 4 ++ src/client/apis/youtube/YoutubeBox.scss | 37 ++++++++++++++++++ src/client/apis/youtube/YoutubeBox.tsx | 64 ++++++++++++++++++++++++++++--- src/server/Message.ts | 3 +- src/server/index.ts | 2 + src/server/youtubeApi/youtubeApiSample.js | 21 ++++++++++ 6 files changed, 125 insertions(+), 6 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index bc5819061..8a9abb514 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -168,6 +168,10 @@ export namespace DocServer { Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.SearchVideo, userInput: videoTitle }, callBack); } + export function getYoutubeVideoDetails(videoIds: string, callBack: (videoDetails: any[]) => void) { + Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.VideoDetails, videoIds: videoIds }, callBack); + } + /** * Given a list of Doc GUIDs, this utility function will asynchronously attempt to each id's associated diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss index 5b539b463..00979f945 100644 --- a/src/client/apis/youtube/YoutubeBox.scss +++ b/src/client/apis/youtube/YoutubeBox.scss @@ -19,6 +19,27 @@ li:hover { display: inline-flex; height: 175px; + .video_duration { + margin: 0; + padding: 0; + border: 0; + background: transparent; + display: inline-block; + position: absolute; + bottom: 0; + right: 0; + margin: 4px; + color: #FFFFFF; + background-color: rgba(0, 0, 0, 0.80); + padding: 2px 4px; + border-radius: 2px; + letter-spacing: .5px; + font-size: 1.2rem; + font-weight: 500; + line-height: 1.2rem; + + } + .textual_info { font-family: Arial, Helvetica, sans-serif; @@ -80,6 +101,22 @@ li:hover { font-weight: 400; text-transform: none; } + + .viewCount { + + margin-left: 8px; + padding: 0; + border: 0; + background: transparent; + color: #606060; + max-width: 100%; + line-height: 1.8rem; + max-height: 3.6rem; + overflow: hidden; + font-size: 1.3rem; + font-weight: 400; + text-transform: none; + } diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 7ac8d06f6..824a0251d 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -26,12 +26,15 @@ export class YoutubeBox extends React.Component { @observable videoClicked: boolean = false; @observable selectedVideoUrl: string = ""; @observable lisOfBackUp: JSX.Element[] = []; + @observable videoIds: string | undefined; + @observable videoDetails: any[] = []; public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } async componentWillMount() { //DocServer.getYoutubeChannels(); + //DocServer.getYoutubeVideoDetails("Ks-_Mh1QhMc, 1NmvhSmN2uM", (results: any[]) => console.log("Details results: ", results)); let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); if (!castedBackUpDocs) { this.props.Document.cachedSearch = castedBackUpDocs = new List(); @@ -95,6 +98,15 @@ export class YoutubeBox extends React.Component { this.searchResults = videos; if (this.searchResults.length > 0) { this.searchResultsFound = true; + this.videoIds = ""; + videos.forEach((video) => { + if (this.videoIds === "") { + this.videoIds = video.id.videoId; + } else { + this.videoIds = this.videoIds! + ", " + video.id.videoId; + } + }); + DocServer.getYoutubeVideoDetails(this.videoIds, this.processVideoDetails); this.backUpSearchResults(videos); if (this.videoClicked) { this.videoClicked = false; @@ -102,6 +114,12 @@ export class YoutubeBox extends React.Component { } } + @action + processVideoDetails = (videoDetails: any[]) => { + this.videoDetails = videoDetails; + console.log("Detail Res: ", this.videoDetails); + } + backUpSearchResults = (videos: any[]) => { let newCachedList = new List(); this.props.Document.cachedSearch = newCachedList; @@ -190,28 +208,64 @@ export class YoutubeBox extends React.Component { } } + convertIsoTimeToDuration = (isoDur: string) => { + + let convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":"); + + if (1 === convertedTime.length) { + 2 !== convertedTime[0].length && (convertedTime[0] = "0" + convertedTime[0]), convertedTime[0] = "0:" + convertedTime[0]; + } else { + for (var r = 1, l = convertedTime.length - 1; l >= r; r++) { + 2 !== convertedTime[r].length && (convertedTime[r] = "0" + convertedTime[r]); + } + } + + return convertedTime.join(":"); + } + + abbreviateViewCount = (viewCount: number) => { + if (viewCount < 1000) { + return viewCount.toString(); + } else if (viewCount >= 1000 && viewCount < 1000000) { + return (Math.trunc(viewCount / 1000)) + "K"; + } else if (viewCount >= 1000000 && viewCount < 1000000000) { + return (Math.trunc(viewCount / 1000000)) + "M"; + } else if (viewCount >= 1000000000) { + return (Math.trunc(viewCount / 1000000000)) + "B"; + } + } + renderSearchResultsOrVideo = () => { if (this.searchResultsFound) { if (this.searchResults.length !== 0) { return
      - {this.searchResults.map((video) => { + {this.searchResults.map((video, index) => { let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); let channelTitle = video.snippet.channelTitle; let videoDescription = video.snippet.description; let pusblishDate = this.roundPublishTime2(video.snippet.publishedAt); - // let duration = video.contentDetails.duration; - //let viewCount = video.statistics.viewCount; + let duration; + let viewCount; + if (this.videoDetails.length !== 0) { + duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration); + viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount); + } //this.roundPublishTime(pusblishDate); //this.roundPublishTime2(video.snippet.publishedAt); + return
    • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
      - +
      + + {duration} +
      {filteredTitle} {channelTitle} + {viewCount} {pusblishDate} - {/*
      {viewCount}
      */}

      {videoDescription}

      +
    • ; diff --git a/src/server/Message.ts b/src/server/Message.ts index 1e29aef0b..aaee143e8 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -25,12 +25,13 @@ export interface Transferable { } export enum YoutubeQueryTypes { - Channels, SearchVideo + Channels, SearchVideo, VideoDetails } export interface YoutubeQueryInput { readonly type: YoutubeQueryTypes; readonly userInput?: string; + readonly videoIds?: string; } export interface Reference { diff --git a/src/server/index.ts b/src/server/index.ts index 60e34de8c..dfbc1a468 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -537,6 +537,8 @@ function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any break; case YoutubeQueryType.SearchVideo: YoutubeApi.authorizedGetVideos(youtubeApiKey, query.userInput, callback); + case YoutubeQueryType.VideoDetails: + YoutubeApi.authorizedGetVideoDetails(youtubeApiKey, query.videoIds, callback); } } diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js index f875812d5..cf41a33e7 100644 --- a/src/server/youtubeApi/youtubeApiSample.js +++ b/src/server/youtubeApi/youtubeApiSample.js @@ -33,6 +33,10 @@ module.exports.authorizedGetVideos = (apiKey, userInput, callBack) => { authorize(JSON.parse(apiKey), getSampleVideos, { userInput: userInput, callBack: callBack }); } +module.exports.authorizedGetVideoDetails = (apiKey, videoIds, callBack) => { + authorize(JSON.parse(apiKey), getVideoDetails, { videoIds: videoIds, callBack: callBack }); +} + /** * Create an OAuth2 client with the given credentials, and then execute the @@ -156,4 +160,21 @@ function getSampleVideos(auth, args) { console.log('Videos found: ' + videos[0].id.videoId, " ", unescape(videos[0].snippet.title)); args.callBack(videos); }); +} + +function getVideoDetails(auth, args) { + let service = google.youtube('v3'); + service.videos.list({ + auth: auth, + part: 'contentDetails, statistics', + id: args.videoIds + }, function (err, response) { + if (err) { + console.log('The API returned an error: ' + err); + return; + } + let videoDetails = response.data.items; + console.log('Video Details founds: ', videoDetails); + args.callBack(videoDetails); + }); } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From e1b750da8faf8f00707de1b65efbd210c19fa723 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 14:29:08 -0400 Subject: Store and css --- src/client/apis/youtube/YoutubeBox.scss | 10 +++++----- src/client/apis/youtube/YoutubeBox.tsx | 23 +++++++++++++++++++++-- 2 files changed, 26 insertions(+), 7 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss index 00979f945..1fc91a9ae 100644 --- a/src/client/apis/youtube/YoutubeBox.scss +++ b/src/client/apis/youtube/YoutubeBox.scss @@ -20,14 +20,14 @@ li:hover { height: 175px; .video_duration { - margin: 0; - padding: 0; + // margin: 0; + // padding: 0; border: 0; background: transparent; display: inline-block; - position: absolute; - bottom: 0; - right: 0; + position: relative; + bottom: 25px; + left: 85%; margin: 4px; color: #FFFFFF; background-color: rgba(0, 0, 0, 0.80); diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 824a0251d..373eee5c4 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -12,7 +12,7 @@ import { DocServer } from "../../DocServer"; import { NumCast, Cast, StrCast } from "../../../new_fields/Types"; import "./YoutubeBox.scss"; import { Docs } from "../../documents/Documents"; -import { Doc } from "../../../new_fields/Doc"; +import { Doc, DocListCastAsync } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; @@ -36,12 +36,29 @@ export class YoutubeBox extends React.Component { //DocServer.getYoutubeChannels(); //DocServer.getYoutubeVideoDetails("Ks-_Mh1QhMc, 1NmvhSmN2uM", (results: any[]) => console.log("Details results: ", results)); let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); + let castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc); + let awaitedBackUp = await castedSearchBackUp; + + console.log("Backup results: ", awaitedBackUp); + console.log("Original Backup results: ", castedBackUpDocs); + + let json = Cast(awaitedBackUp!.json, Doc); + let jsonList = await DocListCastAsync(json); + console.log("Fucked up list: ", jsonList); + for (let video of jsonList!) { + let videoId = await Cast(video.id, Doc); + let id = StrCast(videoId!.videoId); + console.log("ID: ", id); + } + + + if (!castedBackUpDocs) { this.props.Document.cachedSearch = castedBackUpDocs = new List(); } if (castedBackUpDocs.length !== 0) { - this.searchResultsFound = true; + runInAction(() => this.searchResultsFound = true); for (let videoBackUp of castedBackUpDocs) { let curBackUp = await videoBackUp; @@ -121,6 +138,8 @@ export class YoutubeBox extends React.Component { } backUpSearchResults = (videos: any[]) => { + console.log("Res: ", videos); + this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp"); let newCachedList = new List(); this.props.Document.cachedSearch = newCachedList; videos.forEach((video) => { -- cgit v1.2.3-70-g09d2 From 0591b8f1e60d1285fd9aac3e61160824948a166b Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 17:33:31 -0400 Subject: Everything related to search stored --- src/client/apis/youtube/YoutubeBox.tsx | 123 +++++++++++++++++++----------- src/client/documents/Documents.ts | 2 +- src/server/youtubeApi/youtubeApiSample.js | 4 - 3 files changed, 81 insertions(+), 48 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 373eee5c4..e630c11ae 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -16,6 +16,16 @@ import { Doc, DocListCastAsync } from "../../../new_fields/Doc"; import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; +interface VideoTemplate { + thumbnailUrl: string; + videoTitle: string; + videoId: string; + duration: string; + channelTitle: string; + viewCount: string; + publishDate: string; + videoDescription: string; +} @observer export class YoutubeBox extends React.Component { @@ -28,58 +38,71 @@ export class YoutubeBox extends React.Component { @observable lisOfBackUp: JSX.Element[] = []; @observable videoIds: string | undefined; @observable videoDetails: any[] = []; + @observable curVideoTemplates: VideoTemplate[] = []; public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } async componentWillMount() { //DocServer.getYoutubeChannels(); - //DocServer.getYoutubeVideoDetails("Ks-_Mh1QhMc, 1NmvhSmN2uM", (results: any[]) => console.log("Details results: ", results)); - let castedBackUpDocs = Cast(this.props.Document.cachedSearch, listSpec(Doc)); let castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc); let awaitedBackUp = await castedSearchBackUp; + let castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc); + let awaitedDetails = await castedDetailBackUp; - console.log("Backup results: ", awaitedBackUp); - console.log("Original Backup results: ", castedBackUpDocs); - - let json = Cast(awaitedBackUp!.json, Doc); - let jsonList = await DocListCastAsync(json); - console.log("Fucked up list: ", jsonList); - for (let video of jsonList!) { - let videoId = await Cast(video.id, Doc); - let id = StrCast(videoId!.videoId); - console.log("ID: ", id); - } + let jsonList = await DocListCastAsync(awaitedBackUp!.json); + let jsonDetailList = await DocListCastAsync(awaitedDetails!.json); - if (!castedBackUpDocs) { - this.props.Document.cachedSearch = castedBackUpDocs = new List(); - } - if (castedBackUpDocs.length !== 0) { - + if (jsonList!.length !== 0) { runInAction(() => this.searchResultsFound = true); + let index = 0; + for (let video of jsonList!) { + + let videoId = await Cast(video.id, Doc); + let id = StrCast(videoId!.videoId); + let snippet = await Cast(video.snippet, Doc); + let videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title)); + let thumbnail = await Cast(snippet!.thumbnails, Doc); + let thumbnailMedium = await Cast(thumbnail!.medium, Doc); + let thumbnailUrl = StrCast(thumbnailMedium!.url); + let videoDescription = StrCast(snippet!.description); + let pusblishDate = (this.roundPublishTime2(StrCast(snippet!.publishedAt)))!; + let channelTitle = StrCast(snippet!.channelTitle); + let duration: string; + let viewCount: string; + if (jsonDetailList!.length !== 0) { + let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc); + let statistics = await Cast(jsonDetailList![index].statistics, Doc); + duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration)); + viewCount = this.abbreviateViewCount(NumCast(statistics!.viewCount))!; + } + index = index + 1; + let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; + runInAction(() => this.curVideoTemplates.push(newTemplate)); + + // runInAction(() => this.lisOfBackUp.push(( + //
    • this.embedVideoOnClick(id, videoTitle)} key={Utils.GenerateGuid() + id}> + //
      + //
      + // + // {duration} + //
      + //
      + // {videoTitle} + // {channelTitle} + // {viewCount} + // {pusblishDate} + //

      {videoDescription}

      + + //
      + //
      + //
    • ) + // )); - for (let videoBackUp of castedBackUpDocs) { - let curBackUp = await videoBackUp; - let videoId = StrCast(curBackUp.videoId); - let videoTitle = StrCast(curBackUp.videoTitle); - let thumbnailUrl = StrCast(curBackUp.thumbnailUrl); - runInAction(() => this.lisOfBackUp.push(( -
    • this.embedVideoOnClick(videoId, videoTitle)} - key={Utils.GenerateGuid()} - > - - {videoTitle} -
    • ) - )); } - - } - - } _ignore = 0; @@ -134,11 +157,10 @@ export class YoutubeBox extends React.Component { @action processVideoDetails = (videoDetails: any[]) => { this.videoDetails = videoDetails; - console.log("Detail Res: ", this.videoDetails); + this.props.Document.cachedDetails = Docs.Get.DocumentHierarchyFromJson(videoDetails, "detailBackUp"); } backUpSearchResults = (videos: any[]) => { - console.log("Res: ", videos); this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp"); let newCachedList = new List(); this.props.Document.cachedSearch = newCachedList; @@ -164,7 +186,6 @@ export class YoutubeBox extends React.Component { let videoYearDif = curDate.getFullYear() - date.getFullYear(); let videoMonthDif = curDate.getMonth() - date.getMonth(); let videoDayDif = curDate.getDay() - date.getDay(); - console.log("video day dif: ", videoDayDif, " first day: ", curDate.getDay(), " second day: ", date.getDay()); let videoHoursDif = curDate.getHours() - date.getHours(); let videoMinutesDif = curDate.getMinutes() - date.getMinutes(); let videoSecondsDif = curDate.getSeconds() - date.getSeconds(); @@ -182,7 +203,6 @@ export class YoutubeBox extends React.Component { return videoSecondsDif + " seconds ago"; } - console.log("Date : ", date); } roundPublishTime2 = (publishTime: string) => { @@ -290,8 +310,26 @@ export class YoutubeBox extends React.Component { ; })}
    ; - } else if (this.lisOfBackUp.length !== 0) { - return
      {this.lisOfBackUp}
    ; + } else if (this.curVideoTemplates.length !== 0) { + return
      + {this.curVideoTemplates.map((video: VideoTemplate) => { + return
    • this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}> +
      +
      + + {video.duration} +
      +
      + {video.videoTitle} + {video.channelTitle} + {video.viewCount} + {video.publishDate} +

      {video.videoDescription}

      +
      +
      +
    • ; + })} +
    ; } } else { return (null); @@ -301,7 +339,6 @@ export class YoutubeBox extends React.Component { @action embedVideoOnClick = (videoId: string, filteredTitle: string) => { let embeddedUrl = "https://www.youtube.com/embed/" + videoId; - console.log("EmbeddedUrl: ", embeddedUrl); this.selectedVideoUrl = embeddedUrl; let addFunction = this.props.addDocument!; let newVideoX = NumCast(this.props.Document.x); diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 191be9b7d..333e9859b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -498,7 +498,7 @@ export namespace Docs { const convertObject = (object: any, title?: string): Doc => { let target = new Doc(), result: Opt; Object.keys(object).map(key => (result = toField(object[key], key)) && (target[key] = result)); - title && (target.title = title); + title && !target.title && (target.title = title); return target; }; diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js index cf41a33e7..4fede08aa 100644 --- a/src/server/youtubeApi/youtubeApiSample.js +++ b/src/server/youtubeApi/youtubeApiSample.js @@ -23,8 +23,6 @@ module.exports.readApiKey = (callback) => { module.exports.authorizedGetChannel = (apiKey) => { //this didnt get called - console.log("I get called ", apiKey); - console.log(TOKEN_PATH); // Authorize a client with the loaded credentials, then call the YouTube API. authorize(JSON.parse(apiKey), getChannel); } @@ -157,7 +155,6 @@ function getSampleVideos(auth, args) { return; } let videos = response.data.items; - console.log('Videos found: ' + videos[0].id.videoId, " ", unescape(videos[0].snippet.title)); args.callBack(videos); }); } @@ -174,7 +171,6 @@ function getVideoDetails(auth, args) { return; } let videoDetails = response.data.items; - console.log('Video Details founds: ', videoDetails); args.callBack(videoDetails); }); } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 778286579008b57a76fbf82235348b613f5c1a5b Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 18:56:23 -0400 Subject: Fixed document --- src/client/apis/youtube/YoutubeBox.tsx | 24 ++---------------------- src/server/youtubeApi/youtubeApiSample.js | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index e630c11ae..d2f5112c2 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -81,26 +81,6 @@ export class YoutubeBox extends React.Component { index = index + 1; let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; runInAction(() => this.curVideoTemplates.push(newTemplate)); - - // runInAction(() => this.lisOfBackUp.push(( - //
  • this.embedVideoOnClick(id, videoTitle)} key={Utils.GenerateGuid() + id}> - //
    - //
    - // - // {duration} - //
    - //
    - // {videoTitle} - // {channelTitle} - // {viewCount} - // {pusblishDate} - //

    {videoDescription}

    - - //
    - //
    - //
  • ) - // )); - } } } @@ -146,6 +126,7 @@ export class YoutubeBox extends React.Component { this.videoIds = this.videoIds! + ", " + video.id.videoId; } }); + console.log("Video Ids: ", this.videoIds); DocServer.getYoutubeVideoDetails(this.videoIds, this.processVideoDetails); this.backUpSearchResults(videos); if (this.videoClicked) { @@ -289,8 +270,7 @@ export class YoutubeBox extends React.Component { duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration); viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount); } - //this.roundPublishTime(pusblishDate); - //this.roundPublishTime2(video.snippet.publishedAt); + return
  • this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
    diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js index 4fede08aa..f81f0dfb5 100644 --- a/src/server/youtubeApi/youtubeApiSample.js +++ b/src/server/youtubeApi/youtubeApiSample.js @@ -167,7 +167,7 @@ function getVideoDetails(auth, args) { id: args.videoIds }, function (err, response) { if (err) { - console.log('The API returned an error: ' + err); + console.log('The API returned an error from details: ' + err); return; } let videoDetails = response.data.items; -- cgit v1.2.3-70-g09d2 From c86f580191ddf70a6ac2994819a4f33731d79011 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 19:07:59 -0400 Subject: Documentation --- src/client/apis/youtube/YoutubeBox.tsx | 87 +++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 37 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index d2f5112c2..414abcc15 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -27,6 +27,9 @@ interface VideoTemplate { videoDescription: string; } +/** + * This class models the youtube search document that can be dropped on to canvas. + */ @observer export class YoutubeBox extends React.Component { @@ -43,6 +46,10 @@ export class YoutubeBox extends React.Component { public static LayoutString() { return FieldView.LayoutString(YoutubeBox); } + /** + * When component mounts, last search's results are laoded in based on the back up stored + * in the document of the props. + */ async componentWillMount() { //DocServer.getYoutubeChannels(); let castedSearchBackUp = Cast(this.props.Document.cachedSearchResults, Doc); @@ -58,6 +65,7 @@ export class YoutubeBox extends React.Component { if (jsonList!.length !== 0) { runInAction(() => this.searchResultsFound = true); let index = 0; + //getting the necessary information from backUps and building templates that will be used to map in render for (let video of jsonList!) { let videoId = await Cast(video.id, Doc); @@ -68,7 +76,7 @@ export class YoutubeBox extends React.Component { let thumbnailMedium = await Cast(thumbnail!.medium, Doc); let thumbnailUrl = StrCast(thumbnailMedium!.url); let videoDescription = StrCast(snippet!.description); - let pusblishDate = (this.roundPublishTime2(StrCast(snippet!.publishedAt)))!; + let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!; let channelTitle = StrCast(snippet!.channelTitle); let duration: string; let viewCount: string; @@ -103,6 +111,9 @@ export class YoutubeBox extends React.Component { } } + /** + * Function that submits the title entered by user on enter press. + */ onEnterKeyDown = (e: React.KeyboardEvent) => { if (e.keyCode === 13) { let submittedTitle = this.YoutubeSearchElement!.value; @@ -113,6 +124,11 @@ export class YoutubeBox extends React.Component { } } + /** + * The callback that is passed in to server, which functions as a way to + * get videos that is returned by search. It also makes a call to server + * to get details for the videos found. + */ @action processesVideoResults = (videos: any[]) => { this.searchResults = videos; @@ -126,7 +142,7 @@ export class YoutubeBox extends React.Component { this.videoIds = this.videoIds! + ", " + video.id.videoId; } }); - console.log("Video Ids: ", this.videoIds); + //Asking for details that include duration and viewCount from server for videoIds DocServer.getYoutubeVideoDetails(this.videoIds, this.processVideoDetails); this.backUpSearchResults(videos); if (this.videoClicked) { @@ -135,25 +151,26 @@ export class YoutubeBox extends React.Component { } } + /** + * The callback that is given to server to process and receive returned details about the videos. + */ @action processVideoDetails = (videoDetails: any[]) => { this.videoDetails = videoDetails; this.props.Document.cachedDetails = Docs.Get.DocumentHierarchyFromJson(videoDetails, "detailBackUp"); } + /** + * The function that stores the search results in the props document. + */ backUpSearchResults = (videos: any[]) => { this.props.Document.cachedSearchResults = Docs.Get.DocumentHierarchyFromJson(videos, "videosBackUp"); - let newCachedList = new List(); - this.props.Document.cachedSearch = newCachedList; - videos.forEach((video) => { - let videoBackUp = new Doc(); - videoBackUp.videoId = video.id.videoId; - videoBackUp.videoTitle = this.filterYoutubeTitleResult(video.snippet.title); - videoBackUp.thumbnailUrl = video.snippet.thumbnails.medium.url; - newCachedList.push(videoBackUp); - }); } + /** + * The function that filters out escaped characters returned by the api + * in the title of the videos. + */ filterYoutubeTitleResult = (resultTitle: string) => { let processedTitle: string = resultTitle.ReplaceAll("&", "&"); processedTitle = processedTitle.ReplaceAll("'", "'"); @@ -161,32 +178,13 @@ export class YoutubeBox extends React.Component { return processedTitle; } - roundPublishTime = (publishTime: string) => { - let date = new Date(publishTime); - let curDate = new Date(); - let videoYearDif = curDate.getFullYear() - date.getFullYear(); - let videoMonthDif = curDate.getMonth() - date.getMonth(); - let videoDayDif = curDate.getDay() - date.getDay(); - let videoHoursDif = curDate.getHours() - date.getHours(); - let videoMinutesDif = curDate.getMinutes() - date.getMinutes(); - let videoSecondsDif = curDate.getSeconds() - date.getSeconds(); - if (videoYearDif !== 0) { - return videoYearDif + " years ago"; - } else if (videoMonthDif !== 0) { - return videoMonthDif + " months ago"; - } else if (videoDayDif !== 0) { - return videoDayDif + " days ago"; - } else if (videoHoursDif !== 0) { - return videoHoursDif + " hours ago"; - } else if (videoMinutesDif) { - return videoMinutesDif + " minutes ago"; - } else if (videoSecondsDif) { - return videoSecondsDif + " seconds ago"; - } - } - roundPublishTime2 = (publishTime: string) => { + /** + * The function that converts ISO date, which is passed in, to normal date and finds the + * difference between today's date and that date, in terms of "ago" to imitate youtube. + */ + roundPublishTime = (publishTime: string) => { let date = new Date(publishTime).getTime(); let curDate = new Date().getTime(); let timeDif = curDate - date; @@ -228,6 +226,9 @@ export class YoutubeBox extends React.Component { } } + /** + * The function that converts the passed in ISO time to normal duration time. + */ convertIsoTimeToDuration = (isoDur: string) => { let convertedTime = isoDur.replace(/D|H|M/g, ":").replace(/P|T|S/g, "").split(":"); @@ -243,6 +244,10 @@ export class YoutubeBox extends React.Component { return convertedTime.join(":"); } + /** + * The function that rounds the viewCount to the nearest + * thousand, million or billion, given a viewCount number. + */ abbreviateViewCount = (viewCount: number) => { if (viewCount < 1000) { return viewCount.toString(); @@ -255,6 +260,11 @@ export class YoutubeBox extends React.Component { } } + /** + * The function that is called to decide on what'll be rendered by the component. + * It renders search Results if found. If user didn't do a new search, it renders from the videoTemplates + * generated by the backUps. If none present, renders nothing. + */ renderSearchResultsOrVideo = () => { if (this.searchResultsFound) { if (this.searchResults.length !== 0) { @@ -263,7 +273,7 @@ export class YoutubeBox extends React.Component { let filteredTitle = this.filterYoutubeTitleResult(video.snippet.title); let channelTitle = video.snippet.channelTitle; let videoDescription = video.snippet.description; - let pusblishDate = this.roundPublishTime2(video.snippet.publishedAt); + let pusblishDate = this.roundPublishTime(video.snippet.publishedAt); let duration; let viewCount; if (this.videoDetails.length !== 0) { @@ -316,6 +326,10 @@ export class YoutubeBox extends React.Component { } } + /** + * Given a videoId and title, creates a new youtube embedded url, and uses that + * to create a new video document. + */ @action embedVideoOnClick = (videoId: string, filteredTitle: string) => { let embeddedUrl = "https://www.youtube.com/embed/" + videoId; @@ -329,7 +343,6 @@ export class YoutubeBox extends React.Component { } render() { - let field = this.props.Document[this.props.fieldKey]; let content =
    this.YoutubeSearchElement = e!} /> -- cgit v1.2.3-70-g09d2 From 6cdebe507b777f60a0e30a1d7a75300304fbce09 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 19:17:03 -0400 Subject: Refactor and flag --- src/client/apis/youtube/YoutubeBox.tsx | 63 ++++++++++++++++--------------- src/server/youtubeApi/youtubeApiSample.js | 4 +- 2 files changed, 35 insertions(+), 32 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 414abcc15..8d6334c6e 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -58,37 +58,40 @@ export class YoutubeBox extends React.Component { let awaitedDetails = await castedDetailBackUp; - - let jsonList = await DocListCastAsync(awaitedBackUp!.json); - let jsonDetailList = await DocListCastAsync(awaitedDetails!.json); - - if (jsonList!.length !== 0) { - runInAction(() => this.searchResultsFound = true); - let index = 0; - //getting the necessary information from backUps and building templates that will be used to map in render - for (let video of jsonList!) { - - let videoId = await Cast(video.id, Doc); - let id = StrCast(videoId!.videoId); - let snippet = await Cast(video.snippet, Doc); - let videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title)); - let thumbnail = await Cast(snippet!.thumbnails, Doc); - let thumbnailMedium = await Cast(thumbnail!.medium, Doc); - let thumbnailUrl = StrCast(thumbnailMedium!.url); - let videoDescription = StrCast(snippet!.description); - let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!; - let channelTitle = StrCast(snippet!.channelTitle); - let duration: string; - let viewCount: string; - if (jsonDetailList!.length !== 0) { - let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc); - let statistics = await Cast(jsonDetailList![index].statistics, Doc); - duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration)); - viewCount = this.abbreviateViewCount(NumCast(statistics!.viewCount))!; + if (awaitedBackUp) { + + + let jsonList = await DocListCastAsync(awaitedBackUp!.json); + let jsonDetailList = await DocListCastAsync(awaitedDetails!.json); + + if (jsonList!.length !== 0) { + runInAction(() => this.searchResultsFound = true); + let index = 0; + //getting the necessary information from backUps and building templates that will be used to map in render + for (let video of jsonList!) { + + let videoId = await Cast(video.id, Doc); + let id = StrCast(videoId!.videoId); + let snippet = await Cast(video.snippet, Doc); + let videoTitle = this.filterYoutubeTitleResult(StrCast(snippet!.title)); + let thumbnail = await Cast(snippet!.thumbnails, Doc); + let thumbnailMedium = await Cast(thumbnail!.medium, Doc); + let thumbnailUrl = StrCast(thumbnailMedium!.url); + let videoDescription = StrCast(snippet!.description); + let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!; + let channelTitle = StrCast(snippet!.channelTitle); + let duration: string; + let viewCount: string; + if (jsonDetailList!.length !== 0) { + let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc); + let statistics = await Cast(jsonDetailList![index].statistics, Doc); + duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration)); + viewCount = this.abbreviateViewCount(NumCast(statistics!.viewCount))!; + } + index = index + 1; + let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; + runInAction(() => this.curVideoTemplates.push(newTemplate)); } - index = index + 1; - let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; - runInAction(() => this.curVideoTemplates.push(newTemplate)); } } } diff --git a/src/server/youtubeApi/youtubeApiSample.js b/src/server/youtubeApi/youtubeApiSample.js index f81f0dfb5..9853241b6 100644 --- a/src/server/youtubeApi/youtubeApiSample.js +++ b/src/server/youtubeApi/youtubeApiSample.js @@ -28,7 +28,7 @@ module.exports.authorizedGetChannel = (apiKey) => { } module.exports.authorizedGetVideos = (apiKey, userInput, callBack) => { - authorize(JSON.parse(apiKey), getSampleVideos, { userInput: userInput, callBack: callBack }); + authorize(JSON.parse(apiKey), getVideos, { userInput: userInput, callBack: callBack }); } module.exports.authorizedGetVideoDetails = (apiKey, videoIds, callBack) => { @@ -141,7 +141,7 @@ function getChannel(auth) { }); } -function getSampleVideos(auth, args) { +function getVideos(auth, args) { let service = google.youtube('v3'); service.search.list({ auth: auth, -- cgit v1.2.3-70-g09d2 From f5b17bf655ab93d3214ebd1eb7697dd21265d3b5 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 23 Jul 2019 19:27:25 -0400 Subject: Casting problem fixed --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 8d6334c6e..019d191bc 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -86,7 +86,7 @@ export class YoutubeBox extends React.Component { let contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc); let statistics = await Cast(jsonDetailList![index].statistics, Doc); duration = this.convertIsoTimeToDuration(StrCast(contentDetails!.duration)); - viewCount = this.abbreviateViewCount(NumCast(statistics!.viewCount))!; + viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!; } index = index + 1; let newTemplate: VideoTemplate = { videoId: id, videoTitle: videoTitle, thumbnailUrl: thumbnailUrl, publishDate: pusblishDate, channelTitle: channelTitle, videoDescription: videoDescription, duration: duration!, viewCount: viewCount! }; -- cgit v1.2.3-70-g09d2 From 5b455e2aaf119c7db1fe9ef22d71a3accf55a8e2 Mon Sep 17 00:00:00 2001 From: bob Date: Tue, 30 Jul 2019 10:27:42 -0400 Subject: tweaks. --- src/client/apis/youtube/YoutubeBox.tsx | 22 +++++++++------------- src/client/views/collections/CollectionView.tsx | 2 +- .../views/collections/CollectionViewChromes.tsx | 1 + 3 files changed, 11 insertions(+), 14 deletions(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 019d191bc..7f9a3ad70 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -1,20 +1,16 @@ -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 { action, observable, runInAction } from 'mobx'; import { observer } from "mobx-react"; -import { computed, reaction, IReactionDisposer, observable, action, runInAction } from 'mobx'; -import { DocumentDecorations } from "../../views/DocumentDecorations"; -import { InkingControl } from "../../views/InkingControl"; +import { Doc, DocListCastAsync } from "../../../new_fields/Doc"; +import { Cast, NumCast, StrCast } from "../../../new_fields/Types"; import { Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; -import { NumCast, Cast, StrCast } from "../../../new_fields/Types"; -import "./YoutubeBox.scss"; import { Docs } from "../../documents/Documents"; -import { Doc, DocListCastAsync } from "../../../new_fields/Doc"; -import { listSpec } from "../../../new_fields/Schema"; -import { List } from "../../../new_fields/List"; +import { DocumentDecorations } from "../../views/DocumentDecorations"; +import { InkingControl } from "../../views/InkingControl"; +import { FieldView, FieldViewProps } from "../../views/nodes/FieldView"; +import "../../views/nodes/WebBox.scss"; +import "./YoutubeBox.scss"; +import React = require("react"); interface VideoTemplate { thumbnailUrl: string; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index b7ac8768f..212cc5477 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -85,7 +85,7 @@ export class CollectionView extends React.Component { } else { return [ - (), + (), this.SubViewHelper(type, renderProps) ]; } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 2bffe3cc0..38aafd3cc 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -208,6 +208,7 @@ export class CollectionViewBaseChrome extends React.Component { }} onPointerDown={this.openViewSpecs} />
    Date: Tue, 30 Jul 2019 16:44:54 -0400 Subject: oops styling fixeS --- src/client/apis/youtube/YoutubeBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/apis/youtube/YoutubeBox.tsx') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index 7f9a3ad70..dc142802c 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -343,7 +343,7 @@ export class YoutubeBox extends React.Component { render() { let content = -
    +
    this.YoutubeSearchElement = e!} /> {this.renderSearchResultsOrVideo()}
    ; -- cgit v1.2.3-70-g09d2