import * as React from 'react'; import { observer } from 'mobx-react'; import { observable, action, runInAction } 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 _numFilteredResults = 0; private _startIndex = -1; private _endIndex = -1; static Instance: SearchBox; constructor(props: any) { super(props); SearchBox.Instance = 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(); }); } @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); // accounts for the fact that there may be fewer documents than the max that are returned let filteredDocs = FilterBox.Instance.filterDocsByType(docs); count = Math.min(numFound, count); // uh what is going on here with the first part? 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}`); } return resDocs; } 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; } @action resultsScrolled = async (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(`START: ${startIndex}`); console.log(`END: ${endIndex}`); this._startIndex = startIndex; this._endIndex = endIndex; if (this._numTotalResults === 0 && this._openNoResults) { this._visibleElements = [
No Search Results
]; return; } else if (this._visibleElements.length !== this._numTotalResults) { this._visibleElements = Array(this._numTotalResults); this._isSearch = Array(this._numTotalResults); } for (let i = 0; i < this._numTotalResults; i++) { 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 = await this.getResults(this._searchString, i - this._results.length) 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}
{/*
*/}
); } }