From 0f034260b18dc06f1a1267af8707481eff8ac21a Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Tue, 25 Jun 2019 16:58:02 -0400 Subject: Youtube Api Example Js setup --- src/client/apis/youtube/youtubeApiSample.d.ts | 3 + src/client/apis/youtube/youtubeApiSample.js | 128 ++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/client/apis/youtube/youtubeApiSample.d.ts create mode 100644 src/client/apis/youtube/youtubeApiSample.js (limited to 'src/client/apis') diff --git a/src/client/apis/youtube/youtubeApiSample.d.ts b/src/client/apis/youtube/youtubeApiSample.d.ts new file mode 100644 index 000000000..87a669e36 --- /dev/null +++ b/src/client/apis/youtube/youtubeApiSample.d.ts @@ -0,0 +1,3 @@ + +declare const YoutubeApi: any; +export = YoutubeApi; \ No newline at end of file diff --git a/src/client/apis/youtube/youtubeApiSample.js b/src/client/apis/youtube/youtubeApiSample.js new file mode 100644 index 000000000..07c3add36 --- /dev/null +++ b/src/client/apis/youtube/youtubeApiSample.js @@ -0,0 +1,128 @@ +let fs = require('fs'); +let readline = require('readline'); +let { google } = require('googleapis'); +let OAuth2 = google.auth.OAuth2; + +// If modifying these scopes, delete your previously saved credentials +// at ~/.credentials/youtube-nodejs-quickstart.json +let SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']; +let TOKEN_DIR = (process.env.HOME || process.env.HOMEPATH || + process.env.USERPROFILE) + '/.credentials/'; +let TOKEN_PATH = TOKEN_DIR + 'youtube-nodejs-quickstart.json'; + +function readFsFile() { + // Load client secrets from a local file. + fs.readFile('client_secret.json', function processClientSecrets(err, content) { + if (err) { + console.log('Error loading client secret file: ' + err); + return; + } + // Authorize a client with the loaded credentials, then call the YouTube API. + authorize(JSON.parse(content), getChannel); + }); +} + +/** + * Create an OAuth2 client with the given credentials, and then execute the + * given callback function. + * + * @param {Object} credentials The authorization client credentials. + * @param {function} callback The callback to call with the authorized client. + */ +function authorize(credentials, callback) { + let clientSecret = credentials.installed.client_secret; + let clientId = credentials.installed.client_id; + let redirectUrl = credentials.installed.redirect_uris[0]; + let oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl); + + // Check if we have previously stored a token. + fs.readFile(TOKEN_PATH, function (err, token) { + if (err) { + getNewToken(oauth2Client, callback); + } else { + oauth2Client.credentials = JSON.parse(token); + callback(oauth2Client); + } + }); +} + +/** + * Get and store new token after prompting for user authorization, and then + * execute the given callback with the authorized OAuth2 client. + * + * @param {google.auth.OAuth2} oauth2Client The OAuth2 client to get token for. + * @param {getEventsCallback} callback The callback to call with the authorized + * client. + */ +function getNewToken(oauth2Client, callback) { + var authUrl = oauth2Client.generateAuthUrl({ + access_type: 'offline', + scope: SCOPES + }); + console.log('Authorize this app by visiting this url: ', authUrl); + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + rl.question('Enter the code from that page here: ', function (code) { + rl.close(); + oauth2Client.getToken(code, function (err, token) { + if (err) { + console.log('Error while trying to retrieve access token', err); + return; + } + oauth2Client.credentials = token; + storeToken(token); + callback(oauth2Client); + }); + }); +} + +/** + * Store token to disk be used in later program executions. + * + * @param {Object} token The token to store to disk. + */ +function storeToken(token) { + try { + fs.mkdirSync(TOKEN_DIR); + } catch (err) { + if (err.code != 'EEXIST') { + throw err; + } + } + fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => { + if (err) throw err; + console.log('Token stored to ' + TOKEN_PATH); + }); + console.log('Token stored to ' + TOKEN_PATH); +} + +/** + * Lists the names and IDs of up to 10 files. + * + * @param {google.auth.OAuth2} auth An authorized OAuth2 client. + */ +function getChannel(auth) { + var service = google.youtube('v3'); + service.channels.list({ + auth: auth, + part: 'snippet,contentDetails,statistics', + forUsername: 'GoogleDevelopers' + }, function (err, response) { + if (err) { + console.log('The API returned an error: ' + err); + return; + } + var channels = response.data.items; + if (channels.length == 0) { + console.log('No channel found.'); + } else { + console.log('This channel\'s ID is %s. Its title is \'%s\', and ' + + 'it has %s views.', + channels[0].id, + channels[0].snippet.title, + channels[0].statistics.viewCount); + } + }); +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 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 --- package.json | 2 + src/client/DocServer.ts | 6 +++ src/client/apis/youtube/YoutubeBox.tsx | 72 +++++++++++++++++++++++++ src/client/apis/youtube/youtubeApiSample.js | 22 +++----- src/client/documents/Documents.ts | 20 ++++++- src/client/views/MainView.tsx | 6 ++- src/client/views/nodes/DocumentContentsView.tsx | 3 +- src/new_fields/URLField.ts | 3 +- src/server/Message.ts | 1 + src/server/index.ts | 20 ++++++- 10 files changed, 135 insertions(+), 20 deletions(-) create mode 100644 src/client/apis/youtube/YoutubeBox.tsx (limited to 'src/client/apis') diff --git a/package.json b/package.json index 2371d530e..2b1c8f262 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "bluebird": "^3.5.3", "body-parser": "^1.18.3", "bootstrap": "^4.3.1", + "child_process": "^1.0.2", "class-transformer": "^0.2.0", "connect-flash": "^0.1.1", "connect-mongo": "^2.0.3", @@ -171,6 +172,7 @@ "react-simple-dropdown": "^3.2.3", "react-split-pane": "^0.1.85", "react-table": "^6.9.2", + "readline": "^1.3.0", "request": "^2.88.0", "request-image-size": "^2.1.0", "request-promise": "^4.2.4", diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index cbcf751ee..c9cbce78e 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -47,6 +47,12 @@ export namespace DocServer { } } + export async function getYoutubeApiKey() { + let apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiKey, undefined); + return apiKey; + } + + export async function GetRefFields(ids: string[]): Promise<{ [id: string]: Opt }> { const requestedIds: string[] = []; const waitingIds: string[] = []; 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 f70b95879e87a6bb61aaae5de29747d9474623a7 Mon Sep 17 00:00:00 2001 From: Mohammad Amoush Date: Thu, 27 Jun 2019 17:14:15 -0400 Subject: css for youtubeBox --- src/client/apis/youtube/YoutubeBox.scss | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/client/apis') diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss index 962f814a2..e6ccfea90 100644 --- a/src/client/apis/youtube/YoutubeBox.scss +++ b/src/client/apis/youtube/YoutubeBox.scss @@ -1,3 +1,13 @@ +ul { + list-style-type: none; +} + + li { margin: 4px; +} + +li:hover { + cursor: pointer; + opacity: 0.8; } \ No newline at end of file -- 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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:34:52 -0400 Subject: styling fixes to youtube box --- src/client/apis/youtube/YoutubeBox.scss | 222 ++++++++++++++++---------------- 1 file changed, 112 insertions(+), 110 deletions(-) (limited to 'src/client/apis') diff --git a/src/client/apis/youtube/YoutubeBox.scss b/src/client/apis/youtube/YoutubeBox.scss index 1fc91a9ae..eabdbb1ac 100644 --- a/src/client/apis/youtube/YoutubeBox.scss +++ b/src/client/apis/youtube/YoutubeBox.scss @@ -1,124 +1,126 @@ -ul { - list-style-type: none; - padding-inline-start: 10px; -} - - -li { - margin: 4px; - display: inline-flex; -} - -li:hover { - cursor: pointer; - opacity: 0.8; -} - -.search_wrapper { - width: 100%; - display: inline-flex; - height: 175px; - - .video_duration { - // margin: 0; - // padding: 0; - border: 0; - background: transparent; - display: inline-block; - position: relative; - bottom: 25px; - left: 85%; +.youtubeBox-cont { + ul { + list-style-type: none; + padding-inline-start: 10px; + } + + + li { 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; + display: inline-flex; + } + li:hover { + cursor: pointer; + opacity: 0.8; } - .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; - } + .search_wrapper { + width: 100%; + display: inline-flex; + height: 175px; - .channelName { - color:#606060; - margin-left: 4px; - font-size: 1.3rem; - font-weight: 400; - line-height: 1.8rem; - text-transform: none; - margin-top: 0px; + .video_duration { + // margin: 0; + // padding: 0; + border: 0; + background: transparent; display: inline-block; - } + position: relative; + bottom: 25px; + left: 85%; + 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; - .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; - } + .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; + } + + .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; + } + - .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; } - - - } } \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 2b25d87366efe167091f329c6e57d6fe1d9de7af Mon Sep 17 00:00:00 2001 From: yipstanley 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') 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 From 02346eabdefd428ca23d6a3fbefdcd51ef62b738 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sun, 4 Aug 2019 01:17:29 -0400 Subject: fixed errors/warnings --- src/client/apis/youtube/YoutubeBox.tsx | 8 +- src/client/views/InkingControl.tsx | 4 +- src/client/views/TemplateMenu.tsx | 2 +- .../views/collections/CollectionSchemaCells.tsx | 4 +- .../views/collections/CollectionSchemaHeaders.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 5 +- .../views/collections/CollectionStackingView.tsx | 116 +++++++++------------ src/client/views/collections/CollectionView.tsx | 11 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 23 ++-- src/client/views/nodes/ButtonBox.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 4 +- src/client/views/search/FilterBox.tsx | 2 +- src/client/views/search/SearchItem.tsx | 2 +- src/client/views/search/ToggleBar.tsx | 2 +- src/new_fields/Doc.ts | 4 +- 16 files changed, 82 insertions(+), 111 deletions(-) (limited to 'src/client/apis') diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx index dc142802c..d73988bb8 100644 --- a/src/client/apis/youtube/YoutubeBox.tsx +++ b/src/client/apis/youtube/YoutubeBox.tsx @@ -57,7 +57,7 @@ export class YoutubeBox extends React.Component { if (awaitedBackUp) { - let jsonList = await DocListCastAsync(awaitedBackUp!.json); + let jsonList = await DocListCastAsync(awaitedBackUp.json); let jsonDetailList = await DocListCastAsync(awaitedDetails!.json); if (jsonList!.length !== 0) { @@ -76,8 +76,8 @@ export class YoutubeBox extends React.Component { let videoDescription = StrCast(snippet!.description); let pusblishDate = (this.roundPublishTime(StrCast(snippet!.publishedAt)))!; let channelTitle = StrCast(snippet!.channelTitle); - let duration: string; - let viewCount: string; + 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); @@ -85,7 +85,7 @@ export class YoutubeBox extends React.Component { 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! }; + 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/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 58c83915b..3f40642b5 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -1,5 +1,5 @@ import { observable, action, computed, runInAction } from "mobx"; -import { ColorState } from 'react-color'; +import { ColorResult } from 'react-color'; import React = require("react"); import { observer } from "mobx-react"; import "./InkingControl.scss"; @@ -41,7 +41,7 @@ export class InkingControl extends React.Component { } @undoBatch - switchColor = action((color: ColorState): void => { + switchColor = action((color: ColorResult): void => { this._selectedColor = color.hex + (color.rgb.a !== undefined ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff"); if (InkingControl.Instance.selectedTool === InkTool.None) { if (MainOverlayTextBox.Instance.SetColor(color.hex)) return; diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 6dd908445..393e97a7e 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -68,7 +68,7 @@ export class TemplateMenu extends React.Component { } }, 10); } else if (topDocView.props.ContainingCollectionView) { - let collView = topDocView.props.ContainingCollectionView!; + let collView = topDocView.props.ContainingCollectionView; let [sx, sy] = xf.inverse().transformPoint(0, 0); let [x, y] = collView.props.ScreenToLocalTransform().transformPoint(sx, sy); topDoc.x = x; diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 4ff65b277..7e3061354 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -175,11 +175,11 @@ export class CollectionSchemaCell extends React.Component { }; let onPointerEnter = (e: React.PointerEvent): void => { if (e.buttons === 1 && SelectionManager.GetIsDragging() && (type === "document" || type === undefined)) { - dragRef!.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; + dragRef.current!.className = "collectionSchemaView-cellContainer doc-drag-over"; } }; let onPointerLeave = (e: React.PointerEvent): void => { - dragRef!.current!.className = "collectionSchemaView-cellContainer"; + dragRef.current!.className = "collectionSchemaView-cellContainer"; }; let contents: any = "incorrect type"; diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index dfd65770e..d24f63fbb 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -13,7 +13,7 @@ import { faFile } from "@fortawesome/free-regular-svg-icons"; import { SchemaHeaderField, RandomPastel, PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField"; import { undoBatch } from "../../util/UndoManager"; -library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile, faSortAmountDown, faSortAmountUp, faTimes); +library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes); export interface HeaderProps { keyValue: SchemaHeaderField; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index 2d4c88b94..75787c0a8 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -303,14 +303,13 @@ export class SchemaTable extends React.Component { return resized; }, [] as { "id": string, "value": number }[]); } - - @computed get sorted(): { "id": string, "desc": boolean }[] { + @computed get sorted(): { "id": string, "desc"?: true }[] { return this.columns.reduce((sorted, shf) => { if (shf.desc) { sorted.push({ "id": shf.heading, "desc": shf.desc }); } return sorted; - }, [] as { "id": string, "desc": boolean }[]); + }, [] as { "id": string, "desc"?: true }[]); } @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index d7c3ac3b8..4a751c84c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,26 +1,25 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, reaction, untracked, observable, runInAction } from "mobx"; +import { CursorProperty } from "csstype"; +import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; import { observer } from "mobx-react"; -import { Doc, HeightSym, WidthSym, DocListCast } from "../../../new_fields/Doc"; +import Switch from 'rc-switch'; +import { Doc, HeightSym, WidthSym } from "../../../new_fields/Doc"; import { Id } from "../../../new_fields/FieldSymbols"; -import { BoolCast, NumCast, Cast, StrCast } from "../../../new_fields/Types"; -import { emptyFunction, Utils, returnTrue } from "../../../Utils"; -import { CollectionSchemaPreview } from "./CollectionSchemaView"; -import "./CollectionStackingView.scss"; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; -import { undoBatch } from "../../util/UndoManager"; -import { DragManager } from "../../util/DragManager"; +import { List } from "../../../new_fields/List"; +import { listSpec } from "../../../new_fields/Schema"; +import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; +import { emptyFunction } from "../../../Utils"; import { DocumentType } from "../../documents/Documents"; +import { DragManager } from "../../util/DragManager"; import { Transform } from "../../util/Transform"; -import { CursorProperty } from "csstype"; -import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; -import { listSpec } from "../../../new_fields/Schema"; -import { SchemaHeaderField, RandomPastel } from "../../../new_fields/SchemaHeaderField"; -import { List } from "../../../new_fields/List"; +import { undoBatch } from "../../util/UndoManager"; import { EditableView } from "../EditableView"; -import { CollectionViewProps } from "./CollectionBaseView"; -import Switch from 'rc-switch'; +import { CollectionSchemaPreview } from "./CollectionSchemaView"; +import "./CollectionStackingView.scss"; +import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn"; +import { CollectionSubView } from "./CollectionSubView"; @observer export class CollectionStackingView extends CollectionSubView(doc => doc) { @@ -32,13 +31,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _columnStart: number = 0; @observable private cursor: CursorProperty = "grab"; get sectionHeaders() { return Cast(this.props.Document.sectionHeaders, listSpec(SchemaHeaderField)); } - @computed get chromeCollapsed() { return this.props.chromeCollapsed; } @computed get xMargin() { return NumCast(this.props.Document.xMargin, 2 * this.gridGap); } @computed get yMargin() { return NumCast(this.props.Document.yMargin, 2 * this.gridGap); } @computed get gridGap() { return NumCast(this.props.Document.gridGap, 10); } @computed get singleColumn() { return BoolCast(this.props.Document.singleColumn, true); } @computed get columnWidth() { return this.singleColumn ? (this.props.PanelWidth() / (this.props as any).ContentScaling() - 2 * this.xMargin) : Math.min(this.props.PanelWidth() - 2 * this.xMargin, NumCast(this.props.Document.columnWidth, 250)); } @computed get filteredChildren() { return this.childDocs.filter(d => !d.isMinimized); } + @computed get sectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : ""; } get layoutDoc() { // if this document's layout field contains a document (ie, a rendering template), then we will use that @@ -46,37 +45,32 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return this.props.Document.layout instanceof Doc ? this.props.Document.layout : this.props.Document; } - @computed - get SectionFilter() { return this.singleColumn ? StrCast(this.props.Document.sectionFilter) : "" } get Sections() { - let sectionHeaders = this.sectionHeaders; - if (!sectionHeaders) { - this.props.Document.sectionHeaders = sectionHeaders = new List(); + if (!this.sectionFilter) return new Map(); + + if (this.sectionHeaders === undefined) { + this.props.Document.sectionHeaders = new List(); } - let fields = new Map(sectionHeaders.map(sh => [sh, []])); - if (this.SectionFilter) { - this.filteredChildren.map(d => { - let sectionValue = (d[this.SectionFilter] ? d[this.SectionFilter] : `NO ${this.SectionFilter.toUpperCase()} VALUE`) as object; - // the next five lines ensures that floating point rounding errors don't create more than one section -syip - let parsed = parseInt(sectionValue.toString()); - let castedSectionValue: any = sectionValue; - if (!isNaN(parsed)) { - castedSectionValue = parsed; - } + const sectionHeaders = this.sectionHeaders!; + let fields = new Map(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); + this.filteredChildren.map(d => { + let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object; + // the next five lines ensures that floating point rounding errors don't create more than one section -syip + let parsed = parseInt(sectionValue.toString()); + let castedSectionValue = !isNaN(parsed) ? parsed : sectionValue; - // look for if header exists already - let existingHeader = sectionHeaders!.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.SectionFilter.toUpperCase()} VALUE`)); - if (existingHeader) { - fields.get(existingHeader)!.push(d); - } - else { - let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.SectionFilter.toUpperCase()} VALUE`); - fields.set(newSchemaHeader, [d]); - sectionHeaders!.push(newSchemaHeader); - } - }); - } else fields.clear(); + // look for if header exists already + let existingHeader = sectionHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`)); + if (existingHeader) { + fields.get(existingHeader)!.push(d); + } + else { + let newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.sectionFilter.toUpperCase()} VALUE`); + fields.set(newSchemaHeader, [d]); + sectionHeaders.push(newSchemaHeader); + } + }); return fields; } @@ -96,10 +90,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { // reset section headers when a new filter is inputted this._sectionFilterDisposer = reaction( - () => this.SectionFilter, - () => { - this.props.Document.sectionHeaders = new List(); - } + () => this.sectionFilter, + () => this.props.Document.sectionHeaders = new List() ); } componentWillUnmount() { @@ -186,8 +178,8 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { @undoBatch @action drop = (e: Event, de: DragManager.DropEvent) => { - let targInd = -1; let where = [de.x, de.y]; + let targInd = -1; if (de.data instanceof DragManager.DocumentDragData) { this._docXfs.map((cd, i) => { let pos = cd.dxf().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap); @@ -233,8 +225,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } }); } + headings = () => Array.from(this.Sections.keys()); section = (heading: SchemaHeaderField | undefined, docList: Doc[]) => { - let key = this.SectionFilter; + let key = this.sectionFilter; let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined; let types = docList.length ? docList.map(d => typeof d[key]) : this.childDocs.map(d => typeof d[key]); if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) { @@ -245,7 +238,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return Array.from(this.Sections.keys())} + headings={this.headings} heading={heading ? heading.heading : ""} headingObject={heading} docList={docList} @@ -254,16 +247,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { createDropTarget={this.createDropTarget} screenToLocalTransform={this.props.ScreenToLocalTransform} />; - } @action addGroup = (value: string) => { - if (value) { - if (this.sectionHeaders) { - this.sectionHeaders.push(new SchemaHeaderField(value)); - return true; - } + if (value && this.sectionHeaders) { + this.sectionHeaders.push(new SchemaHeaderField(value)); + return true; } return false; } @@ -276,11 +266,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } onToggle = (checked: Boolean) => { - if (checked) { - this.props.CollectionView.props.Document.chromeStatus = 'collapsed'; - } else { - this.props.CollectionView.props.Document.chromeStatus = 'view-mode'; - } + this.props.CollectionView.props.Document.chromeSatus = checked ? "collapsed" : "view-mode"; } render() { @@ -296,10 +282,10 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { return (
    e.stopPropagation()} > - {this.SectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). - map(section => this.section(section[0], section[1])) : + {this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc). + map((section: [SchemaHeaderField, Doc[]]) => this.section(section[0], section[1])) : this.section(undefined, this.filteredChildren)} - {(this.SectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ? + {(this.sectionFilter && (this.props.CollectionView.props.Document.chromeStatus !== 'view-mode' && this.props.CollectionView.props.Document.chromeStatus !== 'disabled')) ?
    diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 57dc5879b..f59fee985 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -20,16 +20,7 @@ import { CollectionTreeView } from "./CollectionTreeView"; import { CollectionViewBaseChrome } from './CollectionViewChromes'; export const COLLECTION_BORDER_WIDTH = 2; -library.add(faTh); -library.add(faTree); -library.add(faSquare); -library.add(faProjectDiagram); -library.add(faSignature); -library.add(faThList); -library.add(faFingerprint); -library.add(faColumns); -library.add(faEllipsisV); -library.add(faImage, faEye); +library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any); @observer export class CollectionView extends React.Component { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b4d065d26..c943fac74 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -40,7 +40,7 @@ import v5 = require("uuid/v5"); import { setScheduler } from "bluebird"; import { DocumentType, Docs } from "../../../documents/Documents"; -library.add(faEye, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); +library.add(faEye as any, faTable, faPaintBrush, faExpandArrowsAlt, faCompressArrowsAlt, faCompass, faUpload); export const panZoomSchema = createSchema({ panX: "number", @@ -206,8 +206,9 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { let cy = NumCast(cd.y) - this._clusterDistance; let cw = NumCast(cd.width) + 2 * this._clusterDistance; let ch = NumCast(cd.height) + 2 * this._clusterDistance; - if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) + if (!cd.z && this.intersectRect({ left: cx, top: cy, width: cw, height: ch }, { left: probe[0], top: probe[1], width: 1, height: 1 })) { return NumCast(cd.cluster); + } return cluster; }, -1); if (cluster !== -1) { @@ -236,20 +237,20 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { updateClusters() { this.sets.length = 0; this.childDocs.map(c => { - let included = [] + let included = []; for (let i = 0; i < this.sets.length; i++) { - for (let j = 0; j < this.sets[i].length; j++) { - if (this.boundsOverlap(c, this.sets[i][j])) { + for (let member of this.sets[i]) { + if (this.boundsOverlap(c, member)) { included.push(i); break; } } } - if (included.length === 0) + if (included.length === 0) { this.sets.push([c]); - else if (included.length === 1) + } else if (included.length === 1) { this.sets[included[0]].push(c); - else { + } else { this.sets[included[0]].push(c); for (let s = 1; s < included.length; s++) { this.sets[included[0]].push(...this.sets[included[s]]); @@ -257,11 +258,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { } } }); - for (let s = 0; s < this.sets.length; s++) { - for (let i = 0; i < this.sets[s].length; i++) { - this.sets[s][i].cluster = s; - } - } + this.sets.map((set, i) => set.map(member => member.cluster = i)); } getClusterColor = (doc: Doc) => { diff --git a/src/client/views/nodes/ButtonBox.tsx b/src/client/views/nodes/ButtonBox.tsx index e2c559c9a..640795789 100644 --- a/src/client/views/nodes/ButtonBox.tsx +++ b/src/client/views/nodes/ButtonBox.tsx @@ -16,7 +16,7 @@ import './ButtonBox.scss'; import { observer } from 'mobx-react'; import { DocumentIconContainer } from './DocumentIcon'; -library.add(faEdit); +library.add(faEdit as any); const ButtonSchema = createSchema({ onClick: ScriptField, diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0347fc9b2..4528cf94d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -95,7 +95,7 @@ export interface DocumentViewProps { addDocTab: (doc: Doc, dataDoc: Doc | undefined, where: string) => void; collapseToPoint?: (scrpt: number[], expandedDocs: Doc[] | undefined) => void; zoomToScale: (scale: number) => void; - backgroundColor: (doc: Doc) => string; + backgroundColor: (doc: Doc) => string | undefined; getScale: () => number; animateBetweenIcon?: (iconPos: number[], startTime: number, maximizing: boolean) => void; ChromeHeight?: () => number; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index fa072aecf..a49709e83 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -152,9 +152,7 @@ export class PDFBox extends DocComponent(PdfDocumen } scrollTo(y: number) { - if (this._mainCont.current) { - this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current!.offsetHeight / 2), 0), behavior: "auto" }); - } + this._mainCont.current && this._mainCont.current.scrollTo({ top: Math.max(y - (this._mainCont.current.offsetHeight / 2), 0), behavior: "auto" }); } settingsPanel() { diff --git a/src/client/views/search/FilterBox.tsx b/src/client/views/search/FilterBox.tsx index 995ddd5c3..3e8582d61 100644 --- a/src/client/views/search/FilterBox.tsx +++ b/src/client/views/search/FilterBox.tsx @@ -384,7 +384,7 @@ export class FilterBox extends React.Component {
    Collection Filters Active
    : undefined}
    - ) + ); } // Useful queries: diff --git a/src/client/views/search/SearchItem.tsx b/src/client/views/search/SearchItem.tsx index 1b9bba5c6..48eb87251 100644 --- a/src/client/views/search/SearchItem.tsx +++ b/src/client/views/search/SearchItem.tsx @@ -134,7 +134,7 @@ export class LinkContextMenu extends React.Component {
    - ) + ); } } diff --git a/src/client/views/search/ToggleBar.tsx b/src/client/views/search/ToggleBar.tsx index a30104089..ed5ecd3ba 100644 --- a/src/client/views/search/ToggleBar.tsx +++ b/src/client/views/search/ToggleBar.tsx @@ -59,7 +59,7 @@ export class ToggleBar extends React.Component{ this._forwardTimeline.play(); this._forwardTimeline.reverse(); this.props.handleChange(); - console.log(this.props.getStatus()) + console.log(this.props.getStatus()); } @action.bound diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index b081588a3..c01f4e8cf 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -358,7 +358,7 @@ export namespace Doc { let docExtensionForField = doc[fieldKey + "_ext"] as Doc; if (docExtensionForField === undefined) { setTimeout(() => { - CreateDocumentExtensionForField(doc, field); + CreateDocumentExtensionForField(doc, fieldKey); }, 0); } else if (doc instanceof Doc) { // backward compatibility -- add fields for docs that don't have them already docExtensionForField.extendsDoc === undefined && setTimeout(() => docExtensionForField.extendsDoc = doc, 0); @@ -535,7 +535,7 @@ export namespace Doc { d.layout = detailLayout; d.nativeWidth = d.nativeHeight = undefined; if (detailLayout instanceof Doc) { - let delegDetailLayout = Doc.MakeDelegate(detailLayout) as Doc; + let delegDetailLayout = Doc.MakeDelegate(detailLayout); d.layout = delegDetailLayout; delegDetailLayout.layout = await delegDetailLayout.detailedLayout; } -- cgit v1.2.3-70-g09d2