From f63aa4c65f6336c7708c50ced83867702a310240 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 4 Oct 2024 18:39:15 -0400 Subject: cleaning up CardDeckView so that animations work smoothly --- .../views/collections/CollectionCalendarView.tsx | 99 ---------- .../views/collections/CollectionCardDeckView.tsx | 199 +++++++++------------ src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 2 - 4 files changed, 85 insertions(+), 217 deletions(-) delete mode 100644 src/client/views/collections/CollectionCalendarView.tsx (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx deleted file mode 100644 index 0ea9f8ebc..000000000 --- a/src/client/views/collections/CollectionCalendarView.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { computed, makeObservable } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { emptyFunction } from '../../../Utils'; -import { dateRangeStrToDates, returnTrue } from '../../../ClientUtils'; -import { Doc, DocListCast } from '../../../fields/Doc'; -import { StrCast } from '../../../fields/Types'; -import { CollectionStackingView } from './CollectionStackingView'; -import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; - -@observer -export class CollectionCalendarView extends CollectionSubView() { - constructor(props: SubCollectionViewProps) { - super(props); - makeObservable(this); - } - - componentDidMount(): void {} - - componentWillUnmount(): void {} - - @computed get allCalendars() { - return this.childDocs; // returns a list of docs (i.e. calendars) - } - - removeCalendar = () => {}; - - addCalendar = (/* doc: Doc | Doc[], annotationKey?: string | undefined */): boolean => - // bring up calendar modal with option to create a calendar - true; - - _stackRef = React.createRef(); - - panelHeight = () => this._props.PanelHeight() - 40; // this should be the height of the stacking view. For now, it's the hieight of the calendar view minus 40 to allow for a title - - // most recent calendar should come first - sortByMostRecentDate = (calendarA: Doc, calendarB: Doc) => { - const aDateRangeStr = StrCast(DocListCast(calendarA.data).lastElement()?.date_range); - const bDateRangeStr = StrCast(DocListCast(calendarB.data).lastElement()?.date_range); - - const { start: aFromDate, end: aToDate } = dateRangeStrToDates(aDateRangeStr); - const { start: bFromDate, end: bToDate } = dateRangeStrToDates(bDateRangeStr); - - if (aFromDate > bFromDate) { - return -1; // a comes first - } - if (aFromDate < bFromDate) { - return 1; // b comes first - } - // start dates are the same - if (aToDate > bToDate) { - return -1; // a comes first - } - if (aToDate < bToDate) { - return 1; // b comes first - } - return 0; // same start and end dates - }; - - screenToLocalTransform = () => - this._props - .ScreenToLocalTransform() - .translate(Doc.NativeWidth(this.Document), 0) - .scale(this._props.NativeDimScaling?.() || 1); - - get calendarsKey() { - return this._props.fieldKey; - } - - render() { - return ( -
- -
- ); - } -} diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 101cc8082..a9ab9de26 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -2,7 +2,6 @@ import { IReactionDisposer, ObservableMap, action, computed, makeObservable, obs import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils'; -import { emptyFunction } from '../../../Utils'; import { Doc } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; @@ -13,7 +12,6 @@ import { gptImageLabel } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager } from '../../util/DragManager'; import { dropActionType } from '../../util/DropActionTypes'; -import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; import { undoable } from '../../util/UndoManager'; @@ -23,12 +21,12 @@ import { DocumentView } 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', Type = 'type', Color = 'color', - Custom = 'custom', Chat = 'chat', Tag = 'tag', None = '', @@ -46,14 +44,13 @@ export class CollectionCardView extends CollectionSubView() { private _dropDisposer?: DragManager.DragDropDisposer; private _disposers: { [key: string]: IReactionDisposer } = {}; private _textToDoc = new Map(); - private _dropped = false; // indicate when a card doc has just moved; + 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) @observable _forceChildXf = 0; @observable _hoveredNodeIndex = -1; @observable _docRefs = new ObservableMap(); @observable _maxRowCount = 10; @observable _docDraggedIndex: number = -1; - @observable overIndex: number = -1; static imageUrlToBase64 = async (imageUrl: string): Promise => { try { @@ -101,7 +98,6 @@ export class CollectionCardView extends CollectionSubView() { componentDidMount() { this._props.setContentViewBox?.(this); - // Reaction to cardSort changes this._disposers.sort = reaction( () => GPTPopup.Instance.visible, isVis => { @@ -135,8 +131,7 @@ export class CollectionCardView extends CollectionSubView() { } /** - * The child documents to be rendered-- either all of them except the Links or the docs in the currently active - * custom group + * 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); @@ -155,8 +150,7 @@ export class CollectionCardView extends CollectionSubView() { */ quizMode = () => { const randomIndex = Math.floor(Math.random() * this.childDocs.length); - SelectionManager.DeselectAll(); - DocumentView.SelectView(DocumentView.getDocumentView(this.childDocs[randomIndex]), false); + DocumentView.getDocumentView(this.childDocs[randomIndex])?.select(false); }; /** @@ -168,29 +162,15 @@ export class CollectionCardView extends CollectionSubView() { @action setHoveredNodeIndex = (index: number) => { - if (!DocumentView.SelectedDocs().includes(this.childDocs[index])) { - this._hoveredNodeIndex = index; - } + if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index; }; - /** - * Translates the hovered node to the center of the screen - * @param index - * @returns - */ - translateHover = (index: number) => (this._hoveredNodeIndex === index && !DocumentView.SelectedDocs().includes(this.childDocs[index]) ? -50 : 0); - - isSelected = (index: number) => DocumentView.SelectedDocs().includes(this.childDocs[index]); - - /** - * Returns all the documents except the one that's currently selected - */ - inactiveDocs = () => this.childDocsWithoutLinks.filter(d => !DocumentView.SelectedDocs().includes(d)); + isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected; childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, this._props.PanelWidth() / 2); childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale; onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick); - isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive(); - isChildContentActive = () => !!this.isContentActive(); + isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive(); + isAnyChildContentActive = this._props.isAnyChildContentActive; /** * Returns the degree to rotate a card dependind on the amount of cards in their row and their index in said row @@ -202,13 +182,14 @@ export class CollectionCardView extends CollectionSubView() { if (amCards == 1) return 0; const possRotate = -30 + index * (30 / ((amCards - (amCards % 2)) / 2)); - const stepMag = Math.abs(-30 + (amCards / 2 - 1) * (30 / ((amCards - (amCards % 2)) / 2))); - - if (amCards % 2 === 0 && possRotate === 0) { - return possRotate + Math.abs(-30 + (index - 1) * (30 / (amCards / 2))); - } - if (amCards % 2 === 0 && index > (amCards + 1) / 2) { - return possRotate + stepMag; + if (amCards % 2 === 0) { + if (possRotate === 0) { + return possRotate + Math.abs(-30 + (index - 1) * (30 / (amCards / 2))); + } + if (index > (amCards + 1) / 2) { + const stepMag = Math.abs(-30 + (amCards / 2 - 1) * (30 / ((amCards - (amCards % 2)) / 2))); + return possRotate + stepMag; + } } return possRotate; @@ -274,21 +255,12 @@ export class CollectionCardView extends CollectionSubView() { /** * Checks to see if a card is being dragged and calls the appropriate methods if so - * @param e the current pointer event */ - @action onPointerMove = (x: number, y: number) => { this._docDraggedIndex = DragManager.docsBeingDragged.length ? this.findCardDropIndex(x, y) : -1; }; - /** - * Handles external drop of images/PDFs etc from outside Dash. - */ - onExternalDrop = async (e: React.DragEvent): Promise => { - super.onExternalDrop(e, {}); - }; - /** * Resets all the doc dragging vairables once a card is dropped * @param e @@ -326,7 +298,7 @@ export class CollectionCardView extends CollectionSubView() { } /** - * Used to determine how to sort cards based on tags. The lestmost tags are given lower values while cards to the right are + * Used to determine how to sort cards based on tags. The leftmost tags are given lower values while cards to the right are * given higher values. Decimals are used to determine placement for cards with multiple tags * @param doc the doc whose value is being determined * @returns its value based on its tags @@ -352,24 +324,14 @@ export class CollectionCardView extends CollectionSubView() { docs.sort((docA, docB) => { const [typeA, typeB] = (() => { switch (sortType) { - case cardSortings.Time: - return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()]; - case cardSortings.Color: { - const d1 = DashColor(StrCast(docA.backgroundColor)); - const d2 = DashColor(StrCast(docB.backgroundColor)); - return [d1.hsv().hue(), d2.hsv().hue()]; - } - case cardSortings.Tag: - return [this.tagValue(docA) ?? 9999, this.tagValue(docB) ?? 9999]; - case cardSortings.Chat: - return [NumCast(docA.chatIndex) ?? 9999, NumCast(docB.chatIndex) ?? 9999]; - default: - return [StrCast(docA.type), StrCast(docB.type)]; + default: + case cardSortings.Type: return [StrCast(docA.type), StrCast(docB.type)]; + case cardSortings.Chat: return [NumCast(docA.chatIndex, 9999), NumCast(docB.chatIndex,9999)]; + case cardSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()]; + case cardSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()]; } - })(); - - const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0; - return isDesc ? out : -out; + })(); //prettier-ignore + return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1); }); if (dragIndex !== -1) { const draggedDoc = DragManager.docsBeingDragged[0]; @@ -382,6 +344,15 @@ 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 + ? false + : undefined; + displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => ( ); @@ -416,13 +388,11 @@ export class CollectionCardView extends CollectionSubView() { if (this.sortedDocs.length < this._maxRowCount) { return this.sortedDocs.length; } - // 13 - 3 = 10 const totalCards = this.sortedDocs.length; // if 9 or less if (index < totalCards - (totalCards % this._maxRowCount)) { return this._maxRowCount; } - // (3) return totalCards % this._maxRowCount; }; /** @@ -443,17 +413,17 @@ export class CollectionCardView extends CollectionSubView() { /** * Determines how far to translate a card in the y direction depending on its index, whether or not its being hovered, or if it's selected * @param isHovered - * @param isSelected + * @param isActive * @param realIndex * @param amCards * @param calcRowIndex * @returns */ - calculateTranslateY = (isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) => { + calculateTranslateY = (isHovered: boolean, isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => { const rowHeight = (this._props.PanelHeight() * this.fitContentScale) / this.numRows; const rowIndex = Math.trunc(realIndex / this._maxRowCount); const rowToCenterShift = this.numRows / 2 - rowIndex; - if (isSelected) return rowToCenterShift * rowHeight - rowHeight / 2; + if (isActive) return rowToCenterShift * rowHeight - rowHeight / 2; if (amCards == 1) return 50 * this.fitContentScale; return this.translateY(amCards, calcRowIndex, realIndex); }; @@ -576,15 +546,43 @@ export class CollectionCardView extends CollectionSubView() { await this.childPairStringListAndUpdateSortDesc(); }; + childScreenToLocal = computedFn((doc: Doc, index: number, calcRowIndex: number, isSelected: boolean, amCards: number) => () => { + // need to explicitly trigger an invalidation since we're reading everything from the Dom + this._forceChildXf; + this._props.ScreenToLocalTransform(); + + const dref = this._docRefs.get(doc); + const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv); + if (!scale) return new Transform(0, 0, 0); + + return new Transform(-translateX + (dref?.centeringX || 0) * scale, + -translateY + (dref?.centeringY || 0) * scale, 1) + .scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore + }); + + 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) { + this._dropped = false; + } else { + // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts. + // Turn them back on when the animation has completed and the render and backend structures are in synch + SnappingManager.SetIsResizing(doc[Id]); + setTimeout( + action(() => { + SnappingManager.SetIsResizing(undefined); + this._forceChildXf++; + }), + 1000 + ); + } + }); + /** * Actually renders all the cards */ @computed get renderCards() { - const sortedDocs = this.sortedDocs; - const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc)); - const isEmpty = this.childDocsWithoutLinks.length === 0; - - if (isEmpty) { + if (!this.childDocsWithoutLinks.length) { return ( Sorry ! There are no cards in this group @@ -593,31 +591,18 @@ export class CollectionCardView extends CollectionSubView() { } // Map sorted documents to their rendered components - return sortedDocs.map((doc, index) => { - const realIndex = sortedDocs.indexOf(doc); + return this.sortedDocs.map((doc, index) => { + const realIndex = this.sortedDocs.indexOf(doc); const calcRowIndex = this.overflowIndexCalc(realIndex); const amCards = this.overflowAmCardsCalc(realIndex); const view = DocumentView.getDocumentView(doc, this.DocumentView?.()); - const isSelected = view?.ComponentView?.isAnyChildContentActive?.() || view?.IsSelected ? true : false; - const childScreenToLocal = () => { - // need to explicitly trigger an invalidation since we're reading everything from the Dom - this._forceChildXf; - this._props.ScreenToLocalTransform(); - - const dref = this._docRefs.get(doc); - const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv); - if (!scale) return new Transform(0, 0, 0); - - return new Transform(-translateX + (dref?.centeringX || 0) * scale, - -translateY + (dref?.centeringY || 0) * scale, 1) - .scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore - }; + const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, !!view?.IsContentActive, amCards); const translateIfSelected = () => { const indexInRow = index % this._maxRowCount; const rowIndex = Math.trunc(index / this._maxRowCount); - const rowCenterIndex = Math.min(this._maxRowCount, sortedDocs.length - rowIndex * this._maxRowCount) / 2; + const rowCenterIndex = Math.min(this._maxRowCount, this.sortedDocs.length - rowIndex * this._maxRowCount) / 2; return (rowCenterIndex - indexInRow) * 100 - 50; }; const aspect = NumCast(doc.height) / NumCast(doc.width, 1); @@ -627,33 +612,18 @@ export class CollectionCardView extends CollectionSubView() { return (
{ - // if a card doc has just moved, or a card is selected and in front, then ignore this event - if (DocumentView.SelectedDocs().includes(doc) || this._dropped) { - this._dropped = false; - } else { - // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts. - // Turn them back on when the animation has completed and the render and backend structures are in synch - SnappingManager.SetIsResizing(doc[Id]); - setTimeout( - action(() => { - SnappingManager.SetIsResizing(undefined); - this._forceChildXf++; - }), - 1000 - ); - } - })} + className={`card-item${view?.IsContentActive ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`} + onPointerUp={() => this.cardPointerUp(doc)} style={{ width: this.childPanelWidth(), height: 'max-content', - transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px) - translateX(calc(${isSelected ? translateIfSelected() : 0}% + ${this.translateOverflowX(realIndex, amCards)}px)) - rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg) - scale(${isSelected ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.05 : 1})`, + transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, !!view?.IsContentActive, realIndex, amCards, calcRowIndex)}px) + translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(realIndex, 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})`, }} // prettier-ignore - onPointerEnter={() => !SnappingManager.IsDragging && this.setHoveredNodeIndex(index)}> + onPointerEnter={() => this.setHoveredNodeIndex(index)} + onPointerLeave={() => this.setHoveredNodeIndex(-1)}> {this.displayDoc(doc, childScreenToLocal)}
); @@ -680,8 +650,7 @@ export class CollectionCardView extends CollectionSubView() { ...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }), ...(!isEmpty && { height: `${100 * this.fitContentScale}%` }), gridAutoRows: `${100 / this.numRows}%`, - }} - onMouseLeave={() => this.setHoveredNodeIndex(-1)}> + }}> {this.renderCards} diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5d32482c3..581201a20 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -296,7 +296,7 @@ export function CollectionSubView() { return false; } - protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: (docs: Doc[]) => void) { + protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions = {}, completed?: (docs: Doc[]) => void) { if (e.ctrlKey) { e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl return; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index c9e934448..7418d4360 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -16,7 +15,6 @@ import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView } from '../nodes/FieldView'; import { OpenWhere } from '../nodes/OpenWhere'; -import { CollectionCalendarView } from './CollectionCalendarView'; import { CollectionCardView } from './CollectionCardDeckView'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; import { CollectionCarouselView } from './CollectionCarouselView'; -- cgit v1.2.3-70-g09d2 From cd7e213e7e0b9b6606ae068c29412bb64cdf6f2d Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 7 Oct 2024 10:43:48 -0400 Subject: comment cleanup --- .../views/collections/CollectionCardDeckView.tsx | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index a9ab9de26..0c2bcc580 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -411,15 +411,14 @@ export class CollectionCardView extends CollectionSubView() { translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (this._maxRowCount - calcRowCards) * (this.childPanelWidth() / 2)); /** - * Determines how far to translate a card in the y direction depending on its index, whether or not its being hovered, or if it's selected - * @param isHovered - * @param isActive - * @param realIndex - * @param amCards - * @param calcRowIndex - * @returns + * Determines how far to translate a card in the y direction depending on its index and if it's selected + * @param isActive whether the card is focused for interaction + * @param realIndex index of card from start of deck + * @param amCards ?? + * @param calcRowIndex index of card from start of row + * @returns Y translation of card */ - calculateTranslateY = (isHovered: boolean, isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => { + calculateTranslateY = (isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => { const rowHeight = (this._props.PanelHeight() * this.fitContentScale) / this.numRows; const rowIndex = Math.trunc(realIndex / this._maxRowCount); const rowToCenterShift = this.numRows / 2 - rowIndex; @@ -592,9 +591,8 @@ export class CollectionCardView extends CollectionSubView() { // Map sorted documents to their rendered components return this.sortedDocs.map((doc, index) => { - const realIndex = this.sortedDocs.indexOf(doc); - const calcRowIndex = this.overflowIndexCalc(realIndex); - const amCards = this.overflowAmCardsCalc(realIndex); + 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); @@ -617,8 +615,8 @@ export class CollectionCardView extends CollectionSubView() { style={{ width: this.childPanelWidth(), height: 'max-content', - transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, !!view?.IsContentActive, realIndex, amCards, calcRowIndex)}px) - translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(realIndex, amCards)}px)) + 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})`, }} // prettier-ignore -- cgit v1.2.3-70-g09d2 From 34fdaf6f1405c59dbf18d03cc489a50715a7e4e9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 7 Oct 2024 13:02:15 -0400 Subject: lint fixes --- src/client/util/UndoManager.ts | 5 ++- src/client/views/GestureOverlay.tsx | 3 +- src/client/views/ScriptingRepl.tsx | 3 +- .../views/collections/CollectionDockingView.tsx | 8 ++--- .../CollectionFreeFormInfoState.tsx | 1 - src/client/views/nodes/DataVizBox/DataVizBox.tsx | 1 - src/fields/Doc.ts | 5 ++- src/fields/RichTextUtils.ts | 6 ---- src/server/websocket.ts | 42 +++++++++++----------- 9 files changed, 31 insertions(+), 43 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts index ce0e7768b..07d3bb708 100644 --- a/src/client/util/UndoManager.ts +++ b/src/client/util/UndoManager.ts @@ -43,7 +43,6 @@ export function undoable(fn: (...args: any[]) => T, batchName: string): (...a return function (...fargs) { const batch = UndoManager.StartBatch(batchName); try { - // eslint-disable-next-line prefer-rest-params return fn.apply(undefined, fargs); } finally { batch.end(); @@ -51,9 +50,9 @@ export function undoable(fn: (...args: any[]) => T, batchName: string): (...a }; } -// eslint-disable-next-line no-redeclare, @typescript-eslint/no-explicit-any +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function undoBatch(target: any, key: string | symbol, descriptor?: TypedPropertyDescriptor): any; -// eslint-disable-next-line no-redeclare, @typescript-eslint/no-explicit-any +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function undoBatch(target: any, key?: string | symbol, descriptor?: TypedPropertyDescriptor<(...args: any[]) => unknown>): any { if (!key) { return function (...fargs: unknown[]) { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 5fddaec9a..afeecaa63 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -70,7 +70,6 @@ export class GestureOverlay extends ObservableReactComponent ({ X: pt.x, Y: pt.y }))); if (intersectRect(scribbleBounds, strokeBounds)) { diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx index 2de867746..8ab91a6b5 100644 --- a/src/client/views/ScriptingRepl.tsx +++ b/src/client/views/ScriptingRepl.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/no-array-index-key */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -182,7 +181,7 @@ export class ScriptingRepl extends ObservableReactComponent { this.maybeScrollToBottom(); return; } - const result = undoable(() => script.run({}, e => this.commands.push({ command: this.commandString, result: e as string })), 'run:' + this.commandString)(); + const result = undoable(() => script.run({}, err => this.commands.push({ command: this.commandString, result: err as string })), 'run:' + this.commandString)(); if (result.success) { this.commands.push({ command: this.commandString, result: result.result }); this.commandsHistory.push(this.commandString); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index d1304b8f4..e1786d2c9 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -31,17 +31,17 @@ import { ScriptingRepl } from '../ScriptingRepl'; import { UndoStack } from '../UndoStack'; import './CollectionDockingView.scss'; import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; -import { TabHTMLElement } from './TabDocView'; +import { TabDocView, TabHTMLElement } from './TabDocView'; @observer export class CollectionDockingView extends CollectionSubView() { - static tabClass: unknown = null; + static tabClass?: typeof TabDocView; /** * Initialize by assigning the add split method to DocumentView and by * configuring golden layout to render its documents using the specified React component * @param ele - typically would be set to TabDocView */ - public static Init(ele: unknown) { + public static Init(ele: typeof TabDocView) { this.tabClass = ele; DocumentView.addSplit = CollectionDockingView.AddSplit; } @@ -544,7 +544,7 @@ export class CollectionDockingView extends CollectionSubView() { tabCreated = (tab: { contentItem: { element: HTMLElement[] } }) => { this.tabMap.add(tab); // InitTab is added to the tab's HTMLElement in TabDocView - const tabdocviewContent = tab.contentItem.element[0]?.firstChild?.firstChild as unknown as TabHTMLElement; + const tabdocviewContent = tab.contentItem.element[0]?.firstChild?.firstChild as TabHTMLElement; tabdocviewContent?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content) }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx index c17371151..51add85a8 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx @@ -46,7 +46,6 @@ export function InfoState( gif?: string, entryFunc?: () => unknown ) { - // eslint-disable-next-line new-cap return new infoState(msg, arcs, gif, entryFunc); } diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index df6e74d85..3dd568fda 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/jsx-props-no-spreading */ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox } from '@mui/material'; import { Colors, Toggle, ToggleType, Type } from 'browndash-components'; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index b3a17de8f..60cf8b321 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -97,9 +97,8 @@ export namespace Field { }); return script; } - export function toString(fieldIn: unknown) { - const field = fieldIn as FieldType; - if (typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field); + export function toString(field: FieldResult | FieldType | undefined) { + if (field instanceof Promise || typeof field === 'string' || typeof field === 'number' || typeof field === 'boolean') return String(field); return field?.[ToString]?.() || ''; } export function IsField(field: unknown): field is FieldType; diff --git a/src/fields/RichTextUtils.ts b/src/fields/RichTextUtils.ts index b3534dde7..8c073c87b 100644 --- a/src/fields/RichTextUtils.ts +++ b/src/fields/RichTextUtils.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-use-before-define */ import { AssertionError } from 'assert'; @@ -175,7 +174,6 @@ export namespace RichTextUtils { const indentMap = new Map(); let globalOffset = 0; const nodes: Node[] = []; - // eslint-disable-next-line no-restricted-syntax for (const element of structured) { if (Array.isArray(element)) { lists.push(element); @@ -374,11 +372,9 @@ export namespace RichTextUtils { const marksToStyle = async (nodes: (Node | null)[]): Promise => { const requests: docsV1.Schema$Request[] = []; let position = 1; - // eslint-disable-next-line no-restricted-syntax for (const node of nodes) { if (node === null) { position += 2; - // eslint-disable-next-line no-continue continue; } const { marks, attrs, nodeSize } = node; @@ -390,9 +386,7 @@ export namespace RichTextUtils { }; let mark: Mark; const markMap = BuildMarkMap(marks); - // eslint-disable-next-line no-restricted-syntax for (const markName of Object.keys(schema.marks)) { - // eslint-disable-next-line no-cond-assign if (ignored.includes(markName) || !(mark = markMap[markName])) { continue; } diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 1e25a8a27..effe94219 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -61,27 +61,6 @@ export namespace WebSocket { Database.Instance.getDocuments(ids, callback); } - const pendingOps = new Map(); - - function dispatchNextOp(id: string): unknown { - const next = pendingOps.get(id)?.shift(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const nextOp = (res: boolean) => dispatchNextOp(id); - if (next) { - const { diff, socket } = next; - // ideally, we'd call the Database update method for all actions, but for now we handle list insertion/removal on our own - switch (diff.diff.$addToSet ? 'add' : diff.diff.$remFromSet ? 'rem' : 'set') { - case 'add': return GetRefFieldLocal(id, (result) => addToListField(socket, diff, result, nextOp)); // prettier-ignore - case 'rem': return GetRefFieldLocal(id, (result) => remFromListField(socket, diff, result, nextOp)); // prettier-ignore - default: return Database.Instance.update(id, diff.diff, - () => nextOp(socket.broadcast.emit(MessageStore.UpdateField.Message, diff)), - false - ); // prettier-ignore - } - } - return !pendingOps.get(id)?.length && pendingOps.delete(id); - } - function addToListField(socket: Socket, diff: Diff, listDoc: serializedDoctype | undefined, cb: (res: boolean) => void): void { const $addToSet = diff.diff.$addToSet as serializedFieldsType; const updatefield = Array.from(Object.keys($addToSet ?? {}))[0]; @@ -181,6 +160,27 @@ export namespace WebSocket { } else cb(false); } + const pendingOps = new Map(); + + function dispatchNextOp(id: string): unknown { + const next = pendingOps.get(id)?.shift(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const nextOp = (res: boolean) => dispatchNextOp(id); + if (next) { + const { diff, socket } = next; + // ideally, we'd call the Database update method for all actions, but for now we handle list insertion/removal on our own + switch (diff.diff.$addToSet ? 'add' : diff.diff.$remFromSet ? 'rem' : 'set') { + case 'add': return GetRefFieldLocal(id, (result) => addToListField(socket, diff, result, nextOp)); // prettier-ignore + case 'rem': return GetRefFieldLocal(id, (result) => remFromListField(socket, diff, result, nextOp)); // prettier-ignore + default: return Database.Instance.update(id, diff.diff, + () => nextOp(socket.broadcast.emit(MessageStore.UpdateField.Message, diff)), + false + ); // prettier-ignore + } + } + return !pendingOps.get(id)?.length && pendingOps.delete(id); + } + function UpdateField(socket: Socket, diff: Diff) { const curUser = socketMap.get(socket); if (curUser) { -- cgit v1.2.3-70-g09d2 From efaa2991ac2670c73a81ab07f61c1626a4300507 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 7 Oct 2024 22:03:54 -0400 Subject: refactored imageUrlToBase64 into clientUtils. lint fixes. --- src/ClientUtils.ts | 21 ++++++++++++ src/client/views/InkTranscription.tsx | 36 +++++---------------- .../views/collections/CollectionCardDeckView.tsx | 23 ++------------ .../collectionFreeForm/ImageLabelBox.tsx | 22 +++++++------ src/client/views/nodes/ComparisonBox.tsx | 37 ++++++---------------- src/client/views/nodes/LabelBox.tsx | 3 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 21 ++---------- src/client/views/smartdraw/SmartDrawHandler.tsx | 4 +-- 8 files changed, 59 insertions(+), 108 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index 972910071..e8165d5ab 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -655,6 +655,27 @@ export function dateRangeStrToDates(dateStr: string) { return { start: new Date(dateRangeParts[0]), end: new Date(dateRangeParts[1]) }; } +/** + * converts the image to base url formate + * @param imageUrl imageurl taken from the collection icon + */ +export async function imageUrlToBase64(imageUrl: string): Promise { + 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; + } +} + function replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) { if (oldDiv.childNodes && newDiv) { for (let i = 0; i < oldDiv.childNodes.length; i++) { diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 24d53a8c8..c5a9d3ba4 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -1,19 +1,19 @@ import * as iink from 'iink-ts'; import { action, observable } from 'mobx'; import * as React from 'react'; +import { imageUrlToBase64 } from '../../ClientUtils'; +import { aggregateBounds } from '../../Utils'; import { Doc, DocListCast } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; import { Cast, DateCast, ImageCast, NumCast } from '../../fields/Types'; -import { aggregateBounds } from '../../Utils'; +import { ImageField, URLField } from '../../fields/URLField'; +import { gptHandwriting } from '../apis/gpt/GPT'; import { DocumentType } from '../documents/DocumentTypes'; -import { CollectionFreeFormView, MarqueeView } from './collections/collectionFreeForm'; -import { InkingStroke } from './InkingStroke'; -import './InkTranscription.scss'; import { Docs } from '../documents/Documents'; +import './InkTranscription.scss'; +import { InkingStroke } from './InkingStroke'; +import { CollectionFreeFormView, MarqueeView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; -import { ImageField } from '../../fields/URLField'; -import { gptHandwriting } from '../apis/gpt/GPT'; -import { URLField } from '../../fields/URLField'; /** * Class component that handles inking in writing mode */ @@ -260,7 +260,7 @@ export class InkTranscription extends React.Component { const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; let response; try { - const hrefBase64 = await this.imageUrlToBase64(hrefComplete); + const hrefBase64 = await imageUrlToBase64(hrefComplete); response = await gptHandwriting(hrefBase64); } catch { console.error('Error getting image'); @@ -291,26 +291,6 @@ export class InkTranscription extends React.Component { } return undefined; } - /** - * converts the image to base url formate - * @param imageUrl imageurl taken from the collection icon - */ - imageUrlToBase64 = async (imageUrl: string): Promise => { - 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; - } - }; /** * Creates the ink grouping once the user leaves the writing mode. diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index 842724e39..c61b0b4dd 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -1,7 +1,8 @@ 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, returnZero } from '../../../ClientUtils'; import { Doc } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; @@ -21,7 +22,6 @@ import { DocumentView } 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', @@ -52,23 +52,6 @@ export class CollectionCardView extends CollectionSubView() { @observable _maxRowCount = 10; @observable _docDraggedIndex: number = -1; - static imageUrlToBase64 = async (imageUrl: string): Promise => { - 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; - } - }; - constructor(props: SubCollectionViewProps) { super(props); makeObservable(this); @@ -462,7 +445,7 @@ export class CollectionCardView extends CollectionSubView() { const hrefParts = href.split('.'); const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; try { - const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete); + 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 diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx index 753685b97..583f2e656 100644 --- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx +++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx @@ -6,6 +6,7 @@ import 'ldrs/ring'; import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import React from 'react'; +import { imageUrlToBase64 } from '../../../../ClientUtils'; import { Utils, numberRange } from '../../../../Utils'; import { Doc, NumListCast, Opt } from '../../../../fields/Doc'; import { DocData } from '../../../../fields/DocSymbols'; @@ -22,13 +23,13 @@ import { MainView } from '../../MainView'; import { DocumentView } from '../../nodes/DocumentView'; import { FieldView, FieldViewProps } from '../../nodes/FieldView'; import { OpenWhere } from '../../nodes/OpenWhere'; -import { CollectionCardView } from '../CollectionCardDeckView'; import './ImageLabelBox.scss'; import { MarqueeOptionsMenu } from './MarqueeOptionsMenu'; export class ImageInformationItem {} export class ImageLabelBoxData { + // eslint-disable-next-line no-use-before-define static _instance: ImageLabelBoxData; @observable _docs: Doc[] = []; @observable _labelGroups: string[] = []; @@ -47,8 +48,8 @@ export class ImageLabelBoxData { }; @action - addLabel = (label: string) => { - label = label.toUpperCase().trim(); + addLabel = (labelIn: string) => { + const label = labelIn.toUpperCase().trim(); if (label.length > 0) { if (!this._labelGroups.includes(label)) { this._labelGroups = [...this._labelGroups, label.startsWith('#') ? label : '#' + label]; @@ -68,9 +69,10 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageLabelBox, fieldKey); } + // eslint-disable-next-line no-use-before-define + public static Instance: ImageLabelBox; private _dropDisposer?: DragManager.DragDropDisposer; - public static Instance: ImageLabelBox; private _inputRef = React.createRef(); @observable _loading: boolean = false; private _currentLabel: string = ''; @@ -99,7 +101,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { } @observable _displayImageInformation: boolean = false; - constructor(props: any) { + constructor(props: FieldViewProps) { super(props); makeObservable(this); ring.register(); @@ -165,7 +167,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { const imageInfos = this._selectedImages.map(async doc => { if (!doc[DocData].tags_chat) { const [name, type] = ImageCast(doc[Doc.LayoutFieldKey(doc)]).url.href.split('.'); - return CollectionCardView.imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 => + return imageUrlToBase64(`${name}_o.${type}`).then(hrefBase64 => !hrefBase64 ? undefined : gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels => ({ doc, labels }))) ; // prettier-ignore @@ -178,13 +180,13 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { const labels = imageInfo.labels.split('\n'); labels.forEach(label => { - label = + const hashLabel = '#' + label .replace(/^\d+\.\s*|-|f\*/, '') .replace(/^#/, '') .trim(); - (imageInfo.doc[DocData].tags_chat as List).push(label); + (imageInfo.doc[DocData].tags_chat as List).push(hashLabel); }); } }); @@ -214,7 +216,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { // most similar one. this._selectedImages.forEach(doc => { const embedLists = numberRange((doc[DocData].tags_chat as List).length).map(n => Array.from(NumListCast(doc[DocData][`tags_embedding_${n + 1}`]))); - const bestEmbedScore = (embedding: Opt) => Math.max(...embedLists.map((l, index) => (embedding && similarity(Array.from(embedding), l)!) || 0)); + const bestEmbedScore = (embedding: Opt) => Math.max(...embedLists.map(l => (embedding && similarity(Array.from(embedding), l)!) || 0)); const {label: mostSimilarLabelCollect} = this._labelGroups.map(label => ({ label, similarityScore: bestEmbedScore(labelToEmbedding.get(label)) })) .reduce((prev, cur) => cur.similarityScore < 0.3 || cur.similarityScore <= prev.similarityScore ? prev: cur, @@ -243,7 +245,7 @@ export class ImageLabelBox extends ViewBoxBaseComponent() { if (this._selectedImages.length === 0) { return (
this.createDropTarget(ele!)}> -

In order to classify and sort images, marquee select the desired images and press the 'Classify and Sort Images' button. Then, add the desired groups for the images to be put in.

+

In order to classify and sort images, marquee select the desired images and press the 'Classify and Sort Images' button. Then, add the desired groups for the images to be put in.

); } diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx index c32bbc803..f7c478729 100644 --- a/src/client/views/nodes/ComparisonBox.tsx +++ b/src/client/views/nodes/ComparisonBox.tsx @@ -5,7 +5,7 @@ import { IReactionDisposer, action, computed, makeObservable, observable, reacti import { observer } from 'mobx-react'; import * as React from 'react'; import ReactLoading from 'react-loading'; -import { returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; +import { imageUrlToBase64, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents } from '../../../ClientUtils'; import { emptyFunction } from '../../../Utils'; import { Doc, Opt } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; @@ -365,7 +365,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() 'Content-Type': 'application/json', }, }); - this.Document.phoneticTranscription = response.data['transcription']; + this.Document.phoneticTranscription = response.data.transcription; }; /** @@ -374,7 +374,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() * @returns */ getYouTubeVideoId = (url: string) => { - const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/; + const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=|\?v=)([^#&?]*).*/; const match = url.match(regExp); return match && match[2].length === 11 ? match[2] : null; }; @@ -392,7 +392,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() 'Content-Type': 'application/json', }, }); - return response.data['transcription']; + return response.data.transcription; }; createFlashcardPile(collectionArr: Doc[], gpt: boolean) { @@ -402,9 +402,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() _layout_fitWidth: false, _layout_autoHeight: true, }); - newCol['x'] = this.layoutDoc['x']; - newCol['y'] = NumCast(this.layoutDoc['y']) + 50; - newCol.type_collection = 'carousel'; + newCol.x = this.layoutDoc.x; + newCol.y = NumCast(this.layoutDoc.y) + 50; + newCol.type_collection = CollectionViewType.Carousel as string; for (let i = 0; i < collectionArr.length; i++) { DocCast(collectionArr[i])[DocData].embedContainer = newCol; } @@ -606,34 +606,17 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent() _height: 150, title: '--snapshot' + NumCast(this.layoutDoc._layout_currentTimecode) + ' image-', }); - imageSnapshot['x'] = this.layoutDoc['x']; - imageSnapshot['y'] = this.layoutDoc['y']; + imageSnapshot.x = this.layoutDoc.x; + imageSnapshot.y = this.layoutDoc.y; return imageSnapshot; } catch (error) { console.log(error); } }; - static imageUrlToBase64 = async (imageUrl: string): Promise => { - 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; - } - }; - getImageDesc = async (u: string) => { try { - const hrefBase64 = await ComparisonBox.imageUrlToBase64(u); + const hrefBase64 = await imageUrlToBase64(u); const response = await gptImageLabel(hrefBase64, 'Answer the following question as a short flashcard response. Do not include a label.' + (this.dataDoc.text as RichTextField)?.Text); DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = response; diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx index 07c0a114a..696ba5697 100644 --- a/src/client/views/nodes/LabelBox.tsx +++ b/src/client/views/nodes/LabelBox.tsx @@ -1,5 +1,5 @@ import { Property } from 'csstype'; -import { action, computed, makeObservable, observable } from 'mobx'; +import { action, computed, makeObservable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import * as textfit from 'textfit'; @@ -22,7 +22,6 @@ export class LabelBox extends ViewBoxBaseComponent() { } private dropDisposer?: DragManager.DragDropDisposer; private _timeout: NodeJS.Timeout | undefined; - @observable private _editLabel = false; _divRef: HTMLDivElement | null = null; constructor(props: FieldViewProps) { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 037b6315c..c89737e1e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -13,7 +13,7 @@ import { EditorState, NodeSelection, Plugin, Selection, TextSelection, Transacti import { EditorView, NodeViewConstructor } from 'prosemirror-view'; import * as React from 'react'; import { BsMarkdownFill } from 'react-icons/bs'; -import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils'; +import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, ClientUtils, DivWidth, imageUrlToBase64, returnFalse, returnZero, setupMoveUpEvents, simMouseEvent, smoothScroll, StopEvent } from '../../../../ClientUtils'; import { DateField } from '../../../../fields/DateField'; import { CreateLinkToActiveAudio, Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc'; import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DocCss, DocData, ForceServerWrite, UpdatingFromServer } from '../../../../fields/DocSymbols'; @@ -1008,30 +1008,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent => { - 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; - } - }; - getImageDesc = async (u: string) => { // if (StrCast(this.dataDoc.description)) return StrCast(this.dataDoc.description); // Return existing description // const { href } = (u as URLField).url; const hrefParts = u.split('.'); const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; try { - const hrefBase64 = await FormattedTextBox.imageUrlToBase64(u); + const hrefBase64 = await imageUrlToBase64(u); const response = await gptImageLabel( hrefBase64, 'Make flashcards out of this text and image with each question and answer labeled as question and answer. Do not label each flashcard and do not include asterisks: ' + (this.dataDoc.text as RichTextField)?.Text diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index 75ef55060..b4635673c 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { AiOutlineSend } from 'react-icons/ai'; import ReactLoading from 'react-loading'; import { INode, parse } from 'svgson'; +import { imageUrlToBase64 } from '../../../ClientUtils'; import { unimplementedFunction } from '../../../Utils'; import { Doc, DocListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; @@ -19,7 +20,6 @@ import { undoable } from '../../util/UndoManager'; import { SVGToBezier, SVGType } from '../../util/bezierFit'; import { InkingStroke } from '../InkingStroke'; import { ObservableReactComponent } from '../ObservableReactComponent'; -import { CollectionCardView } from '../collections/CollectionCardDeckView'; import { MarqueeView } from '../collections/collectionFreeForm'; import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView'; import './SmartDrawHandler.scss'; @@ -310,7 +310,7 @@ export class SmartDrawHandler extends ObservableReactComponent { const hrefParts = href.split('.'); const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`; try { - const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete); + const hrefBase64 = await imageUrlToBase64(hrefComplete); const strokes = DocListCast(drawing[DocData].data); const coords: string[] = []; strokes.forEach((stroke, i) => { -- cgit v1.2.3-70-g09d2