aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionCardDeckView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections/CollectionCardDeckView.tsx')
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx138
1 files changed, 78 insertions, 60 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 3122aa587..286df30aa 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,11 +1,14 @@
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
import * as React from 'react';
-import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils';
+import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
+import { emptyFunction } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
+import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
import { URLField } from '../../../fields/URLField';
import { gptImageLabel } from '../../apis/gpt/GPT';
@@ -17,11 +20,10 @@ import { Transform } from '../../util/Transform';
import { undoable } from '../../util/UndoManager';
import { StyleProp } from '../StyleProp';
import { TagItem } from '../TagsView';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { computedFn } from 'mobx-utils';
enum cardSortings {
Time = 'time',
@@ -45,29 +47,14 @@ export class CollectionCardView extends CollectionSubView() {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _textToDoc = new Map<string, Doc>();
private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
+ private _clickScript = () => ScriptField.MakeScript('scriptContext._curDoc=this', { scriptContext: 'any' })!;
@observable _forceChildXf = 0;
@observable _hoveredNodeIndex = -1;
@observable _docRefs = new ObservableMap<Doc, DocumentView>();
@observable _maxRowCount = 10;
@observable _docDraggedIndex: number = -1;
-
- static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
- try {
- const response = await fetch(imageUrl);
- const blob = await response.blob();
-
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsDataURL(blob);
- reader.onloadend = () => resolve(reader.result as string);
- reader.onerror = error => reject(error);
- });
- } catch (error) {
- console.error('Error:', error);
- throw error;
- }
- };
+ @observable _curDoc: Doc | undefined = undefined;
constructor(props: SubCollectionViewProps) {
super(props);
@@ -133,25 +120,25 @@ export class CollectionCardView extends CollectionSubView() {
/**
* The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
*/
- @computed get childDocsWithoutLinks() {
- return this.childDocs.filter(l => !l.layout_isSvg);
+ @computed get childCards() {
+ return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
}
/**
* how much to scale down the contents of the view so that everything will fit
*/
@computed get fitContentScale() {
- const length = Math.min(this.childDocsWithoutLinks.length, this._maxRowCount);
+ const length = Math.min(this.childCards.length, this._maxRowCount);
return (this.childPanelWidth() * length) / this._props.PanelWidth();
}
/**
* When in quiz mode, randomly selects a document
*/
- quizMode = () => {
+ quizMode = action(() => {
const randomIndex = Math.floor(Math.random() * this.childDocs.length);
- DocumentView.getDocumentView(this.childDocs[randomIndex])?.select(false);
- };
+ this._curDoc = this.childDocs[randomIndex];
+ });
/**
* Number of rows of cards to be rendered
@@ -294,7 +281,12 @@ export class CollectionCardView extends CollectionSubView() {
);
@computed get sortedDocs() {
- return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.Document.cardSort_isDesc), this._docDraggedIndex);
+ return this.sort(
+ this.childCards.map(card => card.layout),
+ this.cardSort,
+ BoolCast(this.Document.cardSort_isDesc),
+ this._docDraggedIndex
+ );
}
/**
@@ -344,14 +336,16 @@ export class CollectionCardView extends CollectionSubView() {
return docs;
};
- isChildContentActive = () =>
- this._props.isContentActive?.() === false
- ? false
- : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
- ? true
- : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+ isChildContentActive = computedFn(
+ (doc: Doc) => () =>
+ this._props.isContentActive?.() === false
? false
- : undefined;
+ : this._props.isDocumentActive?.() && this._curDoc === doc
+ ? true
+ : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+ ? false
+ : undefined
+ ); // prettier-ignore
displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => (
<DocumentView
@@ -370,11 +364,14 @@ export class CollectionCardView extends CollectionSubView() {
isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
PanelWidth={this.childPanelWidth}
PanelHeight={this.childPanelHeight}
+ waitForDoubleClickToClick={returnNever}
+ scriptContext={this}
+ onClickScript={this._curDoc === doc ? undefined : this._clickScript}
dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
showTags={BoolCast(this.layoutDoc.showChildTags)}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- isContentActive={this.isChildContentActive}
+ isContentActive={this.isChildContentActive(doc)}
dontHideOnDrag
/>
);
@@ -442,12 +439,14 @@ export class CollectionCardView extends CollectionSubView() {
default: return StrCast(doc.title);
} // prettier-ignore
};
- const docTextPromises = this.childDocsWithoutLinks.map(async doc => {
- const docText = (await docToText(doc)) ?? '';
- doc.gptInputText = docText;
- this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
- return `======${docText.replace(/\n/g, ' ').trim()}======`;
- });
+ const docTextPromises = this.childCards
+ .map(pair => pair.layout)
+ .map(async doc => {
+ const docText = (await docToText(doc)) ?? '';
+ doc.gptInputText = docText;
+ this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
+ return `======${docText.replace(/\n/g, ' ').trim()}======`;
+ });
return Promise.all<string>(docTextPromises);
};
@@ -462,8 +461,8 @@ export class CollectionCardView extends CollectionSubView() {
const hrefParts = href.split('.');
const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
try {
- const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete);
- const response = await gptImageLabel(hrefBase64);
+ const hrefBase64 = await imageUrlToBase64(hrefComplete);
+ const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.');
image[DocData].description = response.trim();
return response; // Return the response from gptImageLabel
} catch (error) {
@@ -561,7 +560,7 @@ export class CollectionCardView extends CollectionSubView() {
cardPointerUp = action((doc: Doc) => {
// if a card doc has just moved, or a card is selected and in front, then ignore this event
- if (this.isSelected(doc) || this._dropped) {
+ if (this._curDoc === doc || this._dropped) {
this._dropped = false;
} else {
// otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts.
@@ -581,21 +580,16 @@ export class CollectionCardView extends CollectionSubView() {
* Actually renders all the cards
*/
@computed get renderCards() {
- if (!this.childDocsWithoutLinks.length) {
- return (
- <span className="no-card-span" style={{ width: ` ${this._props.PanelWidth()}px`, height: ` ${this._props.PanelHeight()}px` }}>
- Sorry ! There are no cards in this group
- </span>
- );
+ if (!this.childCards.length) {
+ return null;
}
// Map sorted documents to their rendered components
return this.sortedDocs.map((doc, index) => {
const calcRowIndex = this.overflowIndexCalc(index);
const amCards = this.overflowAmCardsCalc(index);
- const view = DocumentView.getDocumentView(doc, this.DocumentView?.());
- const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, !!view?.IsContentActive, amCards);
+ const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, doc === this._curDoc, amCards);
const translateIfSelected = () => {
const indexInRow = index % this._maxRowCount;
@@ -610,15 +604,15 @@ export class CollectionCardView extends CollectionSubView() {
return (
<div
key={doc[Id]}
- className={`card-item${view?.IsContentActive ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
+ className={`card-item${doc === this._curDoc ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
onPointerUp={() => this.cardPointerUp(doc)}
style={{
width: this.childPanelWidth(),
height: 'max-content',
- transform: `translateY(${this.calculateTranslateY(!!view?.IsContentActive, index, amCards, calcRowIndex)}px)
- translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
- rotate(${!view?.IsContentActive ? this.rotate(amCards, calcRowIndex) : 0}deg)
- scale(${view?.IsContentActive ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
+ transform: `translateY(${this.calculateTranslateY(doc === this._curDoc, index, amCards, calcRowIndex)}px)
+ translateX(calc(${doc === this._curDoc ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
+ rotate(${doc !== this._curDoc ? this.rotate(amCards, calcRowIndex) : 0}deg)
+ scale(${doc === this._curDoc ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
}} // prettier-ignore
onPointerEnter={() => this.setHoveredNodeIndex(index)}
onPointerLeave={() => this.setHoveredNodeIndex(-1)}>
@@ -628,13 +622,35 @@ export class CollectionCardView extends CollectionSubView() {
});
}
- render() {
- const isEmpty = this.childDocsWithoutLinks.length === 0;
+ contentScreenToLocalXf = () => this._props.ScreenToLocalTransform().scale(this._props.NativeDimScaling?.() || 1);
+ docViewProps = (): DocumentViewProps => ({
+ ...this._props, //
+ isDocumentActive: this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this._props.isContentActive,
+ isContentActive: emptyFunction,
+ ScreenToLocalTransform: this.contentScreenToLocalXf,
+ });
+ answered = action(() => {
+ this._curDoc = this.curDoc ? this.filteredChildDocs()[(this.filteredChildDocs().findIndex(this.curDoc) + 1) % (this.filteredChildDocs().length || 1)] : undefined;
+ });
+ curDoc = () => this._curDoc;
+ render() {
+ const isEmpty = this.childCards.length === 0;
return (
<div
className="collectionCardView-outer"
ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
+ onPointerDown={action(() => {
+ this._curDoc = undefined;
+ SnappingManager.SetHideDecorations(true);
+ setTimeout(
+ action(() => {
+ SnappingManager.SetHideDecorations(false);
+ this._forceChildXf++;
+ }),
+ 1000
+ );
+ })}
onPointerLeave={action(() => (this._docDraggedIndex = -1))}
onPointerMove={e => this.onPointerMove(...this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY))}
onDrop={this.onExternalDrop.bind(this)}
@@ -646,11 +662,13 @@ export class CollectionCardView extends CollectionSubView() {
className="card-wrapper"
style={{
...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }),
- ...(!isEmpty && { height: `${100 * this.fitContentScale}%` }),
+ ...{ height: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
+ ...{ width: `${100 * (isEmpty ? 1 : this.fitContentScale)}%` },
gridAutoRows: `${100 / this.numRows}%`,
}}>
{this.renderCards}
</div>
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
</div>
);
}