From 3958654925e92b1046b3ed5d49160514b6e48258 Mon Sep 17 00:00:00 2001 From: geireann Date: Tue, 6 Jun 2023 14:36:21 -0400 Subject: added rec stuff and begun updating components --- .../RecommendationList/RecommendationList.scss | 131 ++++++++++++++ .../RecommendationList/RecommendationList.tsx | 192 +++++++++++++++++++++ .../views/newlightbox/RecommendationList/index.ts | 1 + .../views/newlightbox/RecommendationList/utils.ts | 9 + 4 files changed, 333 insertions(+) create mode 100644 src/client/views/newlightbox/RecommendationList/RecommendationList.scss create mode 100644 src/client/views/newlightbox/RecommendationList/RecommendationList.tsx create mode 100644 src/client/views/newlightbox/RecommendationList/index.ts create mode 100644 src/client/views/newlightbox/RecommendationList/utils.ts (limited to 'src/client/views/newlightbox/RecommendationList') diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.scss b/src/client/views/newlightbox/RecommendationList/RecommendationList.scss new file mode 100644 index 000000000..e59834353 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.scss @@ -0,0 +1,131 @@ +@import '../NewLightboxStyles.scss'; + +.recommendationlist-container { + height: calc(100% - 40px); + margin: 20px; + border-radius: 20px; + overflow-y: scroll; + + .recommendations { + height: fit-content; + padding: 20px; + display: flex; + flex-direction: column; + gap: 20px; + background: $gray-l1; + border-radius: 0px 0px 20px 20px; + } + + .header { + top: 0px; + position: sticky; + background: $gray-l1; + border-bottom: $standard-border; + border-color: $gray-l2; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + border-radius: 20px 20px 0px 0px; + padding: 20px; + z-index: 2; + gap: 10px; + color: $text-color-lm; + + .lb-label { + color: $gray-l3; + font-weight: $h1-weight; + font-size: $body-size; + } + + .lb-caret { + display: flex; + flex-direction: row; + justify-content: flex-end; + align-items: center; + gap: 5px; + cursor: pointer; + width: 100%; + user-select: none; + font-size: $body-size; + } + + .more { + width: 100%; + } + + &.dark { + color: $text-color-dm; + } + + .title { + height: 30px; + min-height: 30px; + font-size: $h1-size; + font-weight: $h1-weight; + text-align: left; + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + } + + .keywords { + display: flex; + flex-flow: row wrap; + gap: 5px; + + .keyword-input { + padding: 3px 7px; + background: $gray-l2; + outline: none; + border: none; + height: 21.5px; + color: $text-color-lm; + } + + .keyword { + padding: 3px 7px; + width: fit-content; + background: $gray-l2; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; + gap: 10px; + font-size: $body-size; + font-weight: $body-weight; + + &.loading { + animation: skeleton-loading-l2 1s linear infinite alternate; + min-width: 70px; + height: 21.5px; + } + + .remove { + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + color: $text-color-lm; + padding: 1.5px; + border-radius: 2px; + + &:hover { + background: $gray-l1; + } + } + } + + } + } + + &.dark { + background: $black; + } + + &.light, + &.default { + background: $gray-l1; + } +} \ No newline at end of file diff --git a/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx new file mode 100644 index 000000000..674a501a7 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/RecommendationList.tsx @@ -0,0 +1,192 @@ +import { GrClose } from 'react-icons/gr'; +import { IRecommendation, Recommendation } from "../components"; +import './RecommendationList.scss'; +import * as React from 'react'; +import { IRecommendationList } from "./utils"; +import { NewLightboxView } from '../NewLightboxView'; +import { DocCast, StrCast } from '../../../../fields/Types'; +import { FaCaretDown, FaCaretUp } from 'react-icons/fa'; +import { Doc, DocListCast, StrListCast } from '../../../../fields/Doc'; +import { IDocRequest, fetchKeywords, fetchRecommendations } from '../utils'; +import { IBounds } from '../ExploreView/utils'; +import { List } from '../../../../fields/List'; +import { Id } from '../../../../fields/FieldSymbols'; +import { LightboxView } from '../../LightboxView'; + +export const RecommendationList = (props: IRecommendationList) => { + const {loading, keywords} = props + const [loadingKeywords, setLoadingKeywords] = React.useState(true) + const [showMore, setShowMore] = React.useState(false) + const [keywordsLoc, setKeywordsLoc] = React.useState([]) + const [update, setUpdate] = React.useState(true) + const initialRecs: IRecommendation[] = [ + {loading: true}, + {loading: true}, + {loading: true}, + {loading: true}, + {loading: true} + ]; + const [recs, setRecs] = React.useState(initialRecs) + + React.useEffect(() => { + const getKeywords = async () => { + let text = StrCast(LightboxView.LightboxDoc?.text) + console.log('fetching keywords w/: ', text) + const response = await fetchKeywords(text, 5, true) + const kw = response.keywords; + console.log(kw); + NewLightboxView.SetKeywords(kw); + if (LightboxView.LightboxDoc) { + console.log('setting keywords on doc') + LightboxView.LightboxDoc.keywords = new List(kw); + setKeywordsLoc(NewLightboxView.Keywords); + } + setLoadingKeywords(false) + } + let keywordsList = StrListCast(LightboxView.LightboxDoc!.keywords) + if (!keywordsList || keywordsList.length < 2) { + setLoadingKeywords(true) + getKeywords() + setUpdate(!update) + } else { + setKeywordsLoc(keywordsList) + setLoadingKeywords(false) + setUpdate(!update) + } + }, [NewLightboxView.LightboxDoc]) + + // terms: vannevar bush, information spaces, + React.useEffect(() => { + const getRecommendations = async () => { + console.log('fetching recommendations') + let query = undefined + if (keywordsLoc) query = keywordsLoc.join(',') + let src = StrCast(NewLightboxView.LightboxDoc?.text) + let dashDocs:IDocRequest[] = []; + // get linked docs + let linkedDocs = DocListCast(NewLightboxView.LightboxDoc?.links) + console.log("linked docs", linkedDocs) + // get context docs + let contextDocs: Doc[] = DocListCast(DocCast(NewLightboxView.LightboxDoc?.context).data) + let docId = NewLightboxView.LightboxDoc && NewLightboxView.LightboxDoc[Id] + console.log("context docs", contextDocs) + contextDocs.forEach((doc: Doc) => { + if (docId !== doc[Id]){ + dashDocs.push({ + title: StrCast(doc.title), + text: StrCast(doc.text), + id: doc[Id], + type: StrCast(doc.type) + }) + } + }) + console.log("dash docs", dashDocs) + if (query !== undefined) { + const response = await fetchRecommendations(src, query, dashDocs, true) + const num_recs = response.num_recommendations + const recs = response.recommendations + const keywords = response.keywords + const response_bounds: IBounds = { + max_x: response.max_x, + max_y: response.max_y, + min_x: response.min_x, + min_y: response.min_y + } + // if (NewLightboxView.NewLightboxDoc) { + // NewLightboxView.NewLightboxDoc.keywords = new List(keywords); + // setKeywordsLoc(NewLightboxView.Keywords); + // } + // console.log(response_bounds) + NewLightboxView.SetBounds(response_bounds) + const recommendations: IRecommendation[] = []; + for (const key in recs) { + console.log(key) + const title = recs[key].title; + const url = recs[key].url + const type = recs[key].type + const text = recs[key].text + const transcript = recs[key].transcript + const previewUrl = recs[key].previewUrl + const embedding = recs[key].embedding + const distance = recs[key].distance + const source = recs[key].source + const related_concepts = recs[key].related_concepts + const docId = recs[key].doc_id + related_concepts.length >= 1 && recommendations.push({ + title: title, + data: url, + type: type, + text: text, + transcript: transcript, + previewUrl: previewUrl, + embedding: embedding, + distance: Math.round(distance * 100) / 100, + source: source, + related_concepts: related_concepts, + docId: docId + }) + } + recommendations.sort((a, b) => { + if (a.distance && b.distance) { + return a.distance - b.distance + } else return 0 + }) + NewLightboxView.SetRecs(recommendations) + setRecs(recommendations) + } + } + getRecommendations(); + }, [update]) + + + + return
{e.stopPropagation()}}> +
+
+ Recommendations +
+ {NewLightboxView.LightboxDoc &&
+ The recommendations are produced based on the text in the document {StrCast(NewLightboxView.LightboxDoc.title)}. The following keywords are used to fetch the recommendations. +
} +
Keywords
+ {loadingKeywords ?
+
+
+
+
+
+ : +
+ {keywordsLoc && keywordsLoc.map((word, ind) => { + return
+ {word} +
{ + let kw = keywordsLoc + kw.splice(ind) + NewLightboxView.SetKeywords(kw) + }}>{}
+
+ })} +
+ } + {!showMore ? +
{setShowMore(true)}}> + More +
+ : +
+
{setShowMore(false)}}> + Less +
+
Type
+
Sources
+
+ } +
+
+ {recs && recs.map((rec: IRecommendation) => { + return + })} +
+
+} \ No newline at end of file diff --git a/src/client/views/newlightbox/RecommendationList/index.ts b/src/client/views/newlightbox/RecommendationList/index.ts new file mode 100644 index 000000000..f4555c1f2 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/index.ts @@ -0,0 +1 @@ +export * from './RecommendationList' \ No newline at end of file diff --git a/src/client/views/newlightbox/RecommendationList/utils.ts b/src/client/views/newlightbox/RecommendationList/utils.ts new file mode 100644 index 000000000..cdfff3258 --- /dev/null +++ b/src/client/views/newlightbox/RecommendationList/utils.ts @@ -0,0 +1,9 @@ +import { IRecommendation } from "../components"; + +export interface IRecommendationList { + loading?: boolean, + keywords?: string[], + recs?: IRecommendation[] + getRecs?: any +} + -- cgit v1.2.3-70-g09d2