diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/CollectionCarouselView.tsx | 51 | ||||
-rw-r--r-- | src/client/views/nodes/ComparisonBox.scss | 8 | ||||
-rw-r--r-- | src/client/views/nodes/ComparisonBox.tsx | 90 | ||||
-rw-r--r-- | src/client/views/pdf/AnchorMenu.tsx | 13 | ||||
-rw-r--r-- | src/client/views/pdf/Annotation.scss | 19 | ||||
-rw-r--r-- | src/client/views/pdf/PDFViewer.tsx | 1 |
6 files changed, 136 insertions, 46 deletions
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index b5b6e1f4b..f2d634eaa 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -21,6 +21,8 @@ import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import './CollectionCarouselView.scss'; import { CollectionSubView } from './CollectionSubView'; import { Tooltip } from '@mui/material'; +import { DocUtils } from '../../documents/DocUtils'; +import { Any } from '@react-spring/web'; enum cardMode { // PRACTICE = 'practice', @@ -49,7 +51,8 @@ export class CollectionCarouselView extends CollectionSubView() { constructor(props: any) { super(props); makeObservable(this); - // this.layoutDoc.filterOp = cardMode.ALL; + // this.setModes(); + this.layoutDoc.filterOp = cardMode.ALL; Doc.setDocFilter(this.Document, 'data_star', undefined, 'match'); this.layoutDoc.practiceMode = practiceMode.NORMAL; this.layoutDoc._carousel_index = 0; @@ -67,6 +70,9 @@ export class CollectionCarouselView extends CollectionSubView() { }; @computed get carouselItems() { + this.childLayoutPairs.map(pair => { + pair.layout.embedContainer = this.Document; + }); return this.childLayoutPairs.filter(pair => pair.layout.type !== DocumentType.LINK); } @computed get marginX() { @@ -80,6 +86,13 @@ export class CollectionCarouselView extends CollectionSubView() { this._filterMessage = mes; }; + setModes = () => { + this.layoutDoc.filterOp = cardMode.ALL; + Doc.setDocFilter(this.Document, 'data_star', undefined, 'match'); + this.layoutDoc.practiceMode = practiceMode.NORMAL; + this.layoutDoc._carousel_index = 0; + }; + move = (dir: number) => { const moveToCardWithField = (match: (doc: Doc) => boolean): boolean => { let startInd = (NumCast(this.layoutDoc._carousel_index) + dir) % this.carouselItems.length; @@ -107,8 +120,8 @@ export class CollectionCarouselView extends CollectionSubView() { break; case practiceMode.PRACTICE && cardMode.STAR: if (!moveToCardWithField((doc: Doc) => doc[this.practiceField] !== practiceVal.CORRECT && doc[this.starField] === true)) { - this._filterMessage = 'No flashcards to show! Unselect star mode to view all flashcards.'; - this._practiceMessage = 'Unselect practice mode to view all flashcards.'; + this._filterMessage = 'No flashcards to show! Unselect mode to view all flashcards.'; + this._practiceMessage = undefined; } break; default: @@ -179,20 +192,24 @@ export class CollectionCarouselView extends CollectionSubView() { this.layoutDoc.filterOp = mode; switch (mode) { case cardMode.STAR: - Doc.setDocFilter(this.Document, 'data_star', true, 'match'); + // Doc.setDocFilter(this.Document, 'data_star', true, 'match'); this.move(1); break; default: this.setFilterMessage(undefined); // prettier-ignore - Doc.setDocFilter(this.Document, 'data_star', true, 'remove'); + // Doc.setDocFilter(this.Document, 'data_star', true, 'remove'); } }; @computed get content() { + if (this.layoutDoc._carousel_index === this.carouselItems.length && this.layoutDoc._carousel_index !== 0) { + this.move(1); + } const index = NumCast(this.layoutDoc._carousel_index); const curDoc = this.carouselItems?.[index]; const captionProps = { ...this._props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined }; const carouselShowsCaptions = StrCast(this.layoutDoc._layout_showCaption); + return !(curDoc?.layout instanceof Doc) ? null : ( <> <div className="collectionCarouselView-image" key="image"> @@ -207,6 +224,7 @@ export class CollectionCarouselView extends CollectionSubView() { onClickScript={this.onContentClick} isDocumentActive={this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive} isContentActive={this._props.childContentsActive ?? this._props.isContentActive() === false ? returnFalse : emptyFunction} + addDocument={this._props.addDocument} hideCaptions={!!carouselShowsCaptions} // hide captions if the carousel is configured to show the captions renderDepth={this._props.renderDepth + 1} LayoutTemplate={this._props.childLayoutTemplate} @@ -235,12 +253,21 @@ export class CollectionCarouselView extends CollectionSubView() { } containsDifTypes = (): boolean => { - return this.carouselItems.filter(doc => !doc.layout.isFlashcard).length === 0; + return this.carouselItems.filter(doc => !doc.layout._layout_isFlashcard).length !== 0; }; addFlashcard() { const newDoc = Docs.Create.ComparisonDocument('', { _layout_isFlashcard: true, _width: 300, _height: 300 }); this.addDocument?.(newDoc); + // DocUtils.copyDragFactory(newDoc); + // this._props.addDocument?.(); + // newDoc.layout = this.layoutDoc; + // newDoc.data = this.dataDoc; + // Doc.AddDocToList() + // this._props.parent._props.addDocument(); + // this.childLayoutPairs.push({ newDoc.layout, newDoc.data}); + // this._props.addDocument?.(newDoc); + // console.log('HERE'); } @computed get buttons() { @@ -260,11 +287,11 @@ export class CollectionCarouselView extends CollectionSubView() { <FontAwesomeIcon icon="star" color={this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)].layout[this.starField] ? 'yellow' : 'gray'} size="1x" /> </div> </Tooltip> - <Tooltip title="add new flashcard to pile"> + {/* <Tooltip title="add new flashcard to pile"> <div key="add" className="carouselView-add" onClick={this.addFlashcard}> <FontAwesomeIcon icon="plus" color={this.carouselItems?.[NumCast(this.layoutDoc._carousel_index)].layout[this.starField] ? 'yellow' : 'gray'} size="1x" /> </div> - </Tooltip> + </Tooltip> */} </div> ) : null} {this.layoutDoc.practiceMode === practiceMode.PRACTICE ? ( @@ -286,8 +313,10 @@ export class CollectionCarouselView extends CollectionSubView() { } togglePracticeMode = (mode: practiceMode) => { - if (mode === this.layoutDoc.practiceMode) this.setPracticeMode(practiceMode.NORMAL); - else this.setPracticeMode(mode); + if (mode === this.layoutDoc.practiceMode) { + this.setPracticeMode(practiceMode.NORMAL); + // this.setPracticeMessage("undefined"); + } else this.setPracticeMode(mode); }; toggleFilterMode = () => { this.layoutDoc.filterOp === cardMode.STAR ? this.setFilterMode(cardMode.ALL) : this.setFilterMode(cardMode.STAR)}; //prettier-ignore setColor = (mode: practiceMode | cardMode, which: string) => { return which === mode ? 'white' : 'light gray'}; //prettier-ignore @@ -351,7 +380,7 @@ export class CollectionCarouselView extends CollectionSubView() { }}> Recently missed! </p> - {!this.containsDifTypes() ? this.menu : null} + {!this.containsDifTypes() && this.carouselItems.length !== 0 ? this.menu : null} {this.Document._chromeHidden || (!this._filterMessage && !this._practiceMessage) ? this.buttons : null} </div> ); diff --git a/src/client/views/nodes/ComparisonBox.scss b/src/client/views/nodes/ComparisonBox.scss index 0b2c356e5..f7389e39b 100644 --- a/src/client/views/nodes/ComparisonBox.scss +++ b/src/client/views/nodes/ComparisonBox.scss @@ -16,14 +16,15 @@ } .input-box { position: absolute; + top: 50; padding: 10px; width: 100%; - height: 100%; + height: 70%; display: flex; } .submit-button { - position: relative; + position: absolute; padding-bottom: 10px; padding-left: 5px; padding-right: 5px; @@ -31,6 +32,7 @@ border-radius: 2px; height: 15%; display: flex; + bottom: 0; button { flex: 1; @@ -149,7 +151,7 @@ top: 10px; left: 10px; z-index: 200; - padding: 5px; + // padding: 5px; background: #dfdfdf; } diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index 9eb5f6ca2..2fc297bec 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -28,6 +28,7 @@ import ReactLoading from 'react-loading'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { tickStep } from 'd3'; +import { CollectionCarouselView } from '../collections/CollectionCarouselView'; @observer export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @@ -46,8 +47,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() @observable private _isEmpty = false; @observable _yRelativeToTop: boolean = true; - public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined; - @action handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { this._inputValue = e.target.value; console.log(this._inputValue); @@ -248,6 +247,41 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() _closeRef = React.createRef<HTMLDivElement>(); + createFlashcardPile(collectionArr: Doc[], gpt: boolean) { + const newCol = Docs.Create.CarouselDocument(collectionArr, { + _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250) + 50, + _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200) + 50, + _layout_fitWidth: false, + _layout_autoHeight: true, + }); + newCol['x'] = this.layoutDoc['x']; + newCol['y'] = NumCast(this.layoutDoc['y']) + 50; + newCol.type_collection = 'carousel'; + console.log(newCol.data); + + if (gpt) { + this._props.DocumentView?.()._props.addDocument?.(newCol); + this._props.removeDocument?.(this.Document); + } else { + this._props.addDocument?.(newCol); + this._props.removeDocument?.(this.Document); + this.Document.embedContainer = newCol; + } + } + + gptFlashcardPile = async () => { + var text = (await this.askGPT(GPTCallType.FLASHCARD)) || ''; + + var senArr = text.trim().split('Question: '); + var collectionArr: Doc[] = []; + for (let i = 1; i < senArr.length; i++) { + const newDoc = Docs.Create.ComparisonDocument(senArr[i].trim(), { _layout_isFlashcard: true, _width: 300, _height: 300 }); + newDoc.text = senArr[i].trim(); + collectionArr.push(newDoc); + } + this.createFlashcardPile(collectionArr, true); + }; + /** * Flips a flashcard to the alternate side for the user to view. */ @@ -305,35 +339,27 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() <div className="dash-tooltip">Ask GPT to create an answer on the back side of the flashcard based on your question on the front</div> ) }> - <div style={{ position: 'absolute', bottom: '3px', right: '56px', cursor: 'pointer' }} onPointerDown={e => (!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}> + <div style={{ position: 'absolute', bottom: '3px', right: '50px', cursor: 'pointer' }} onPointerDown={e => (!this.layoutDoc[`_${this._props.fieldKey}_usePath`] ? this.askGPT(GPTCallType.CHATCARD) : null)}> <FontAwesomeIcon icon="lightbulb" size="xl" /> </div> </Tooltip> - <Tooltip title={<div>Create a flashcard pile</div>}> - <div - style={{ position: 'absolute', bottom: '3px', right: '80px', cursor: 'pointer' }} - onPointerDown={e => { - // this.displayLabelHandler(e.target.value, e.clientY); - const collectionArr: Doc[] = []; - collectionArr.push(this.Document); - const newCol = Docs.Create.CarouselDocument(collectionArr, { - _width: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 250) + 50, - _height: NumCast(this.layoutDoc['_' + this._props.fieldKey + '_width'], 200) + 50, - _layout_fitWidth: false, - _layout_autoHeight: true, - }); - newCol['x'] = this.layoutDoc['x']; - newCol['y'] = NumCast(this.layoutDoc['y']) + 50; - this._props.addDocument?.(newCol); - this._props.removeDocument?.(this.Document); - this.Document.embedContainer = newCol; - }}> - <FontAwesomeIcon icon="folder-plus" size="xl" /> + {DocCast(this.Document.embedContainer).type_collection === 'carousel' ? null : ( + <div> + <Tooltip title={<div>Create a flashcard pile</div>}> + <div style={{ position: 'absolute', bottom: '3px', right: '74px', cursor: 'pointer' }} onPointerDown={e => this.createFlashcardPile([this.Document], false)}> + <FontAwesomeIcon icon="folder-plus" size="xl" /> + </div> + </Tooltip> + <Tooltip title={<div className="dash-tooltip">Create new flashcard stack based on text</div>}> + <div style={{ position: 'absolute', bottom: '3px', right: '104px', cursor: 'pointer' }} onClick={e => this.gptFlashcardPile()}> + <FontAwesomeIcon icon="layer-group" size="xl" /> + </div> + </Tooltip> </div> - </Tooltip> + )} <Tooltip title={<div className="dash-tooltip">Hover to reveal</div>}> <div style={{ position: 'absolute', bottom: '3px', right: '25px', cursor: 'pointer' }} onClick={e => this.handleHover()}> - <FontAwesomeIcon color={this.revealOp === 'hover' ? 'blue' : 'black'} icon="layer-group" size="xl" /> + <FontAwesomeIcon color={this.revealOp === 'hover' ? 'blue' : 'black'} icon="hand-point-up" size="xl" /> </div> </Tooltip> {/* <Tooltip title={<div className="dash-tooltip">Remove this side of the flashcard</div>}> @@ -425,6 +451,12 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() if (callType == GPTCallType.QUIZ) this._outputValue = res; // DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res; // this._outputValue = res; + + if (callType === GPTCallType.FLASHCARD) { + // console.log(res); + this._loading = false; + return res; + } console.log(res); } catch (err) { console.error('GPT call failed'); @@ -520,7 +552,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() // DocCast(this.dataDoc[this.fieldKey + '_0'])[DocData].text = dataSplit[1]; // DocCast(this.dataDoc[this.fieldKey + '_0']).text = dataSplit[1]; // console.log('HI' + DocCast(this.dataDoc[this.fieldKey + '_0']).text); - console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text)); + //console.log('HEREEE' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text)); } if (!this.dataDoc[this.fieldKey + '_1'] && !this._isEmpty) { @@ -545,7 +577,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() return ( <div className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} style={{ display: 'flex', flexDirection: 'column' }}> <p style={{ color: 'white', padding: 10 }}>{text}</p> - <p style={{ display: text === '' ? 'flex' : 'none', color: 'white', padding: 10 }}>Return to all flashcards and add text to both sides. </p> + <p style={{ display: text === '' ? 'flex' : 'none', color: 'white', marginLeft: '10px' }}>Return to all flashcards and add text to both sides. </p> <div className="input-box"> <textarea value={this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? this._outputValue : this._inputValue} @@ -574,7 +606,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() ); } - console.log('HEREEE2' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)); + //console.log('HEREEE2' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text)); // render a normal flashcard when not a QuizCard return ( <div @@ -589,7 +621,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() }} // onPointerUp={() => (this._isAnyChildContentActive = true)} > - {StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '' && !this.childActive ? <p className="explain">Enter text in the flashcard. </p> : null} + {!this.layoutDoc[`_${this._props.fieldKey}_usePath`] && StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text) === '' && !this.childActive ? <p className="explain">Enter text in the flashcard. </p> : null} {displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)} {this._loading ? ( <div className="loading-spinner" style={{ position: 'absolute' }}> diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index cedd3c7c3..c1198b4f7 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -15,6 +15,7 @@ import { LinkPopup } from '../linking/LinkPopup'; import { DocumentView } from '../nodes/DocumentView'; import './AnchorMenu.scss'; import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup'; +import ReactLoading from 'react-loading'; @observer export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -24,6 +25,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { private _disposer: IReactionDisposer | undefined; private _commentRef = React.createRef<HTMLDivElement>(); private _cropRef = React.createRef<HTMLDivElement>(); + @observable private _loading = false; // @observable protected _top: number = -300; // @observable protected _left: number = -300; @@ -110,15 +112,16 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { */ gptFlashcards = async (e: React.PointerEvent) => { const queryText = this._selectedText; + this._loading = true; try { const res = await gptAPICall(queryText, GPTCallType.FLASHCARD); console.log(res); - GPTPopup.Instance.setText(res || 'Something went wrong.'); + // GPTPopup.Instance.setText(res || 'Something went wrong.'); this.transferToFlashcard(res || 'Something went wrong'); } catch (err) { console.error(err); } - GPTPopup.Instance.setLoading(false); + // GPTPopup.Instance.setLoading(false); }; /* @@ -147,6 +150,7 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { newCol.zIndex = 100; this.addToCollection?.(newCol); + this._loading = false; }; pointerDown = (e: React.PointerEvent) => { @@ -263,6 +267,11 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { /> </div> )} + {this._loading ? ( + <div className="loading-spinner" style={{ position: 'absolute' }}> + <ReactLoading type="spin" height={30} width={30} color={'white'} /> + </div> + ) : null} </> ) : ( <> diff --git a/src/client/views/pdf/Annotation.scss b/src/client/views/pdf/Annotation.scss index 1de60ffed..329819ea2 100644 --- a/src/client/views/pdf/Annotation.scss +++ b/src/client/views/pdf/Annotation.scss @@ -7,4 +7,21 @@ &:hover { cursor: pointer; } -}
\ No newline at end of file +} +.loading-spinner { + display: flex; + justify-content: center; + align-items: center; + height: 90%; + width: 93%; + left: 10; + font-size: 20px; + font-weight: bold; + color: #0b0a0a; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 92f890e70..af7a5774d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -464,6 +464,7 @@ export class PDFViewer extends ObservableReactComponent<IViewerProps> { this._mainCont.current!.style.transform = ''; } this._selectionContent = selRange.cloneContents(); + this._selectionText = this._selectionContent?.textContent || ''; // clear selection |