import * as React from 'react'; import { observer } from 'mobx-react'; import { observable, action, runInAction, flow } from 'mobx'; import "./SearchBox.scss"; import "./FilterBox.scss"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { SetupDrag } from '../../util/DragManager'; import { Docs } from '../../documents/Documents'; import { NumCast } from '../../../new_fields/Types'; import { Doc } from '../../../new_fields/Doc'; import { SearchItem } from './SearchItem'; import { DocServer } from '../../DocServer'; import * as rp from 'request-promise'; import { Id } from '../../../new_fields/FieldSymbols'; import { SearchUtil } from '../../util/SearchUtil'; import { RouteStore } from '../../../server/RouteStore'; import { FilterBox } from './FilterBox'; import { start } from 'repl'; @observer export class SearchBox extends React.Component { @observable private _searchString: string = ""; @observable private _resultsOpen: boolean = false; @observable private _results: Doc[] = []; @observable private _openNoResults: boolean = false; @observable public _pageNum: number = 0; //temp @observable public _maxNum: number = 10; @observable private _visibleElements: JSX.Element[] = []; @observable private _scrollY: number = 0; private _isSearch: ("search" | "placeholder" | undefined)[] = []; private _currentIndex = 0; private _numTotalResults = 0; private _startIndex = -1; private _endIndex = -1; private _fetchedIndices: number[] = [0]; static Instance: SearchBox; private _maxSearchIndex: number = 0; constructor(props: any) { super(props); SearchBox.Instance = this; this.resultsScrolled = this.resultsScrolled.bind(this); } @action getViews = async (doc: Doc) => { const results = await SearchUtil.GetViewsOfDocument(doc); let toReturn: Doc[] = []; await runInAction(() => { toReturn = results; }); return toReturn; } @action.bound onChange(e: React.ChangeEvent) { this._searchString = e.target.value; this._openNoResults = false; this._results = []; this._visibleElements = []; this._currentIndex = 0; this._numTotalResults = 0; this._startIndex = -1; this._endIndex = -1; } enter = (e: React.KeyboardEvent) => { if (e.key === "Enter") { this.submitSearch(); } } public static async convertDataUri(imageUri: string, returnedFilename: string) { try { let posting = DocServer.prepend(RouteStore.dataUriToImage); const returnedUri = await rp.post(posting, { body: { uri: imageUri, name: returnedFilename }, json: true, }); return returnedUri; } catch (e) { console.log(e); } } @action submitSearch = async () => { let query = this._searchString; // searchbox gets query let results: Doc[]; query = FilterBox.Instance.getFinalQuery(query); //if there is no query there should be no result if (query === "") { results = []; } else { //gets json result into a list of documents that can be used //these are filtered by type this._currentIndex = 0; results = await this.getResults(query, 12); } runInAction(() => { this._resultsOpen = true; this._results = results; this._openNoResults = true; this.resultsScrolled(); }); } getResultsHelp = async (query: string) => { // docs length = this._results.length --> number of docs that are shown (after filtering) // stops looking once this._results.length >= maxDisplayIndex // max search index = number of results looked through in solr (solr index) --> increments of 10 // max display index = number of documents that SHOULD be shown (should include buffer), this._endIndex + buffer (= 4) // currentRequest = promise | undefined, if undefined, can run and look for more. If not undefined, then there is a request in progress and it cannot look for more yet let buffer = 4; let maxDisplayIndex: number = this._endIndex + buffer; let curRequest = undefined; while (this._results.length < maxDisplayIndex) { if (curRequest === undefined) { } } } @action getResults = async (query: string, count: number) => { let resDocs = []; // count is total number of documents to be shown (i believe) console.log(`Count: ${count}`); while (resDocs.length < count) { let index = count === -1 ? undefined : this._currentIndex; let num = count === -1 ? undefined : Math.min(this._numTotalResults - this._currentIndex + 1, this._maxNum); // num found has to be the number of docs before filtering happens - this is the total num const { docs, numFound } = await SearchUtil.Search(query, true, index, num); let filteredDocs = FilterBox.Instance.filterDocsByType(docs); // accounts for the fact that there may be fewer documents than the max that are returned count = Math.min(numFound, count); // happens at the beginning if (numFound !== this._numTotalResults && this._numTotalResults === 0) { console.log(`Total: ${numFound}`); this._numTotalResults = numFound; } // if (filteredDocs.length < docs.length) { // this._numResults -= docs.length - filteredDocs.length; // console.log(`New Total: ${this._numResults}`); // } resDocs.push(...filteredDocs); this._currentIndex += docs.length; console.log(`ResDocs: ${resDocs.length}`); console.log(`CurrIndex: ${this._currentIndex}`); } // console.log(this.getResults2(query, count, [])); return resDocs; } // @action // getResults2 = async (query: string, count: number, docs: Doc[]) => { // console.log("results 2") // let buffer = 4; // // let goalIndex = this._endIndex + count; // // let bottomBound = Math.floor(goalIndex / 10) * 10; // let tempIndex = this._currentIndex; // let goalNum = this._endIndex + buffer; // let resDocs: Doc[] = docs; // // let topBound = bottomBound - 10; // // let unfilteredDocs: Doc[]; // // let unfilteredFound: number; // // means this has already been fetched // // if (this._fetchedIndices.includes(topBound)) { // // return; // // } // let index = count <= 0 ? undefined : this._currentIndex; // if (index) { // let topBound = Math.ceil(index / 10) * 10; // if (this._fetchedIndices.includes(topBound)) { // return; // } // let startIndex = this._fetchedIndices[this._fetchedIndices.length - 1]; // let endIndex = startIndex + 10; // this._fetchedIndices.push(endIndex); // console.log(this._fetchedIndices) // let prom: Promise = SearchUtil.Search(query, true, index, 10); // prom.then((res: SearchUtil.DocSearchResult) => { // count = Math.min(res.numFound, count); // console.log(res.docs); // let filteredDocs = FilterBox.Instance.filterDocsByType(res.docs); // if (res.numFound !== this._numTotalResults && this._numTotalResults === 0) { // this._numTotalResults = res.numFound; // } // resDocs.push(...filteredDocs); // this._currentIndex += res.docs.length; // if (resDocs.length <= count) { // runInAction(() => { // return this.getResults2(query, count, resDocs); // }); // } // else { // return resDocs; // } // // console.log(tempIndex); // console.log(resDocs.length); // }) // } // } collectionRef = React.createRef(); startDragCollection = async () => { const results = await this.getResults(FilterBox.Instance.getFinalQuery(this._searchString), -1); const docs = results.map(doc => { const isProto = Doc.GetT(doc, "isPrototype", "boolean", true); if (isProto) { return Doc.MakeDelegate(doc); } else { return Doc.MakeAlias(doc); } }); let x = 0; let y = 0; for (const doc of docs) { doc.x = x; doc.y = y; const size = 200; const aspect = NumCast(doc.nativeHeight) / NumCast(doc.nativeWidth, 1); if (aspect > 1) { doc.height = size; doc.width = size / aspect; } else if (aspect > 0) { doc.width = size; doc.height = size * aspect; } else { doc.width = size; doc.height = size; } doc.zoomBasis = 1; x += 250; if (x > 1000) { x = 0; y += 300; } } return Docs.FreeformDocument(docs, { width: 400, height: 400, panX: 175, panY: 175, backgroundColor: "grey", title: `Search Docs: "${this._searchString}"` }); } @action.bound openSearch(e: React.PointerEvent) { e.stopPropagation(); this._openNoResults = false; FilterBox.Instance.closeFilter(); this._resultsOpen = true; FilterBox.Instance._pointerTime = e.timeStamp; } @action.bound closeSearch = () => { console.log("closing search") FilterBox.Instance.closeFilter(); this.closeResults(); } @action.bound closeResults() { this._resultsOpen = false; this._results = []; this._visibleElements = []; this._currentIndex = 0; this._numTotalResults = 0; this._startIndex = -1; this._endIndex = -1; } resultsScrolled = flow(function* (this: SearchBox, e?: React.UIEvent) { let scrollY = e ? e.currentTarget.scrollTop : 0; let buffer = 4; let startIndex = Math.floor(Math.max(0, scrollY / 70 - buffer)); let endIndex = Math.ceil(Math.min(this._numTotalResults - 1, startIndex + (560 / 70) + buffer)); if (startIndex === this._startIndex && endIndex === this._endIndex) { return; } console.log("_________________________________________________________________________________________________________") console.log(`START: ${startIndex}`); console.log(`END: ${endIndex}`); this._startIndex = startIndex; this._endIndex = endIndex; if (this._numTotalResults === 0 && this._openNoResults) { this._visibleElements = [
No Search Results
]; return; } // only hit right at the beginning // visibleElements is all of the elements (even the ones you can't see) else if (this._visibleElements.length !== this._numTotalResults) { // undefined until a searchitem is put in there this._visibleElements = Array(this._numTotalResults); // indicates if things are placeholders this._isSearch = Array(this._numTotalResults); } for (let i = 0; i < this._numTotalResults; i++) { //if the index is out of the window then put a placeholder in //should ones that have already been found get set to placeholders? if (i < startIndex || i > endIndex) { if (this._isSearch[i] !== "placeholder") { this._isSearch[i] = "placeholder"; this._visibleElements[i] =
; } } else { if (this._isSearch[i] !== "search") { let result: Doc | undefined = undefined; if (i >= this._results.length) { // _________________________________________________________________________________________________ let results: Doc[] = yield this.getResults(this._searchString, i + 1 - this._results.length); if (results.length !== 0) { runInAction(() => { this._results.push(...results); result = this._results[i]; if (result) { this._visibleElements[i] = ; this._isSearch[i] = "search"; } }); } // _________________________________________________________________________________________________ } else { result = this._results[i]; if (result) { this._visibleElements[i] = ; this._isSearch[i] = "search"; } } } } } }); render() { return (
{/* {(this._results.length !== 0) ? ( this._results.map(result => ) ) : this._openNoResults ? (
No Search Results
) : null} */} {this._visibleElements}
{/*
*/}
); } }