import { makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc } from '../../../../fields/Doc'; import { ObservableReactComponent } from '../../ObservableReactComponent'; import { CollectionFreeFormInfoState, infoState } from './CollectionFreeFormInfoState'; import { Button } from '../../../util/CurrentUserUtils'; import { InfoState } from './CollectionFreeFormInfoState'; import { DocButtonState } from '../../nodes/DocumentLinksButton'; import { InkTool } from '../../../../fields/InkField'; import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; import { CollectionFreeFormView } from '.'; import { DocListCast } from '../../../../fields/Doc'; import { ButtonType } from '../../nodes/FontIconBox/FontIconBox'; export interface CollectionFreeFormInfoUIProps { Document: Doc; LayoutDoc: Doc; childDocs: () => Doc[]; close: () => void; } @observer export class CollectionFreeFormInfoUI extends ObservableReactComponent { _originalBackground: string | undefined; private tutorialStates: { [key: string]: infoState } = {}; public static Init() { CollectionFreeFormView.SetInfoUICreator( (doc: Doc, layout: Doc, childDocs: () => Doc[], close: () => void) => ); } constructor(props: CollectionFreeFormInfoUIProps) { super(props); makeObservable(this); this.tutorialStates = {}; // Initialize an empty object this.currState = this.setupStates(); // Call setupStates() here } @observable _currState: infoState | undefined = undefined; @observable _nextState: infoState | undefined = undefined; // Track next state get currState() { return this._currState; } set currState(val) { runInAction(() => { this._currState = val; }); } componentWillUnmount(): void { this._props.Document.backgroundColor = this._originalBackground; } skipToState = (newState: infoState) => { runInAction(() => { if (!this._currState) { this._currState = newState; // Assign directly if undefined } else { this._currState = newState; } }); }; createNextButton = (newState: ReturnType) => { return { title: "Next", toolTip: "Next", btnType: ButtonType.ClickButton, scripts: { onClick: `this.skipToState(${newState})` }, targetState: newState }; }; setupStates = () => { let docCounter = this._props.childDocs().length; let lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] let linkCounter = Doc.Links(lastDocCreated)?.length; let presentationCounter = DocListCast(Doc.ActivePresentation?.data).length this._originalBackground = this._props.Document.backgroundColor as string; this.tutorialStates.multipleDocs = InfoState("Let's create a new link! Click the link icon on one document and connect it to another.", { // eslint-disable-next-line no-use-before-define linkStarted: [() => DocumentLinksButton.StartLink, () => { linkCounter = Doc.Links(lastDocCreated).length // eslint-disable-next-line no-use-before-define return startedLink}], // docCreated: [() => this._props.childDocs().length > docCounter, () => { // docCounter += 1 // lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] // // eslint-disable-next-line no-use-before-define // return this.tutorialStates.makePresentation}] }, 'dash-create-link-board.gif' ); this.tutorialStates.presentDocs = InfoState("Select a document then click the 'pin' button in the top left to create your presentation.", { // eslint-disable-next-line no-use-before-define docPinned: [() => DocListCast(Doc.ActivePresentation?.data).length > presentationCounter, () => { presentationCounter++ // eslint-disable-next-line no-use-before-define return pinnedDoc}], }, 'pin-explanation.gif'); this.tutorialStates.nestedCollections = InfoState("Want to learn how to create a nested collection? Click the : button and add a 'collection' doc", { // eslint-disable-next-line no-use-before-define docCreated: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] // eslint-disable-next-line no-use-before-define return marqueeSelection}] }, 'dash-nested-collection.gif') this.tutorialStates.makePresentation = InfoState("Add a new document to create a presentation!", { docCreated: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] // eslint-disable-next-line no-use-before-define return this.tutorialStates.presentDocs}], }); const skipToLinksButton: Button = { title: "Links Tutorial", toolTip: "Skip", btnType: ButtonType.ClickButton, scripts: { onClick: "this.skipToState(this.tutorialStates.multipleDocs)" }, targetState: this.tutorialStates.multipleDocs }; const skipToPinsButton: Button = { title: "Pins Tutorial", toolTip: "Skip", btnType: ButtonType.ClickButton, scripts: { onClick: "this.skipToState(this.tutorialStates.makePresentation)" }, targetState: this.tutorialStates.makePresentation }; const skipToPresentationButton: Button = { title: "Collections Tutorial", toolTip: "Skip", btnType: ButtonType.ClickButton, scripts: { onClick: "this.skipToState(this.tutorialStates.nestedCollections)" }, targetState: this.tutorialStates.nestedCollections }; const ending = InfoState("If you have any more questions, feel free to ask Dash's AI Bot!") // Traditional tutorial const completed = InfoState( "Eager to learn more? Click the ? icon in the top right corner to read our full documentation.", { docRemoved: [() => this._props.childDocs().length === 1, () => this.tutorialStates.start] }, 'documentation.png', ); const penMode = InfoState("You're in pen mode! Click and drag to draw your first masterpiece, then click the Ink button once you're done.", { activePen: [() => Doc.ActiveTool !== InkTool.Ink, () => completed], }); const briefArtisticFeature = InfoState("Finally, want to explore the art feature of Dash? Click the 'Ink' button on the hotbar then click the pen button.", { penModeActivated: [() => Doc.ActiveTool === InkTool.Ink, () => penMode], }); const activatePresentation = InfoState("Lastly, click the linked node and start the presentation!", { presentation: [() => Doc.ActivePresentation?.presentation_status === "auto", () => briefArtisticFeature], }); const deletePresentation = InfoState("Cool! Click 'setOnClick to follow primary link' for your non-presentation doc and try deleting the presentation.", { docRemoved: [() => this._props.childDocs().length < docCounter, () => { docCounter -= 1 return activatePresentation}], }, 'onclick-node.gif'); const trailedPresentation = InfoState("Try linking your presentation to the last doc you created (now highlighted).", { linkAdd: [() => Doc.Links(lastDocCreated)?.length > linkCounter, () => { linkCounter += 1 return deletePresentation }], docAdded: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 // Last doc that is not the presentation lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 2] linkCounter = Doc.Links(lastDocCreated)?.length return deletePresentation}] }, 'link-presentation.gif'); const pinnedPresentation = InfoState("Want to see something cool? Zoom out, click the trail button on the presentation, and drag it inside the canvas.", { docAdded: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 // Last doc that is not the presentation lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 2] Doc.HighlightDoc(lastDocCreated) linkCounter = Doc.Links(lastDocCreated)?.length return trailedPresentation}], }, 'dash-trail-explanation.gif'); const pinnedDoc2 = InfoState("You pinned another doc. Press autoplay to the right to show your presentation!", { autoPresentation: [() => Doc.ActivePresentation?.presentation_status === "auto", () => pinnedPresentation], }); const pinnedDoc = InfoState("You just pinned your doc. Pin another doc to add to the presentation!", { // eslint-disable-next-line no-use-before-define addedDoc: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] return pinnedDoc}], docPinned: [() => DocListCast(Doc.ActivePresentation?.data).length > presentationCounter, () => { presentationCounter++ // eslint-disable-next-line no-use-before-define return pinnedDoc2}], }); const editLink = InfoState("Want to make your link visible? Click 'show link'.", { docCreated: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] return this.tutorialStates.makePresentation}], }, 'show-link.gif'); const madeLink = InfoState("You made your first link! You can view your links by selecting the blue dot.", { linkViewed: [() => DocButtonState.Instance.LinkEditorDocView, () => { docCounter = this._props.childDocs().length return editLink}], }, 'dash-following-link.gif'); const startedLink = InfoState("Now click the highlighted link icon on your other document.", { linkAdd: [() => Doc.Links(lastDocCreated)?.length > linkCounter, () => { linkCounter += 1 return madeLink }] }, 'dash-create-link-board.gif'); this.tutorialStates.movedDoc = InfoState("Great moves! Try creating a second document.", { docCreated: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] return this.tutorialStates.multipleDocs}], }, 'dash-colon-menu.gif'); // prettier-ignore this.tutorialStates.start = InfoState("Welcome to Dash! Click anywhere and begin typing ':' to create your first document.", { docCreated: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] return this.tutorialStates.movedDoc}] }, undefined, [skipToLinksButton, skipToPinsButton]) // Information on created nested collections const createdMarquee = InfoState("Next, right click and drag a square to create the collection", { // eslint-disable-next-line no-use-before-define marqueeMade: [() => this._props.childDocs().length < docCounter, () => { docCounter -= 1 return ending}], }, 'dash-create-collection-marquee.gif') const marqueeSelection = InfoState("Want an easier way to make a collection of docs? First add two docs you want to make a collection of", { marqueeMade: [() => this._props.childDocs().length > docCounter, () => { docCounter += 1 lastDocCreated = this._props.childDocs()[this.props.childDocs().length - 1] return createdMarquee}] }) // Explanation of importing const easierImport = InfoState("Or, for easier access, you can drag any of the accepted file types from your computer or a webpage and drop it into your dashboard. This includes images, videos, audio, pdfs, and more!", { }, 'dash-', [this.createNextButton(ending)]) this.tutorialStates.importFile = InfoState("Want to learn how to import a file? Import using the import menu on the left hand side", { }, 'dash-import.gif', [this.createNextButton(easierImport)]) // Editing documents // Accessed by right-clicking anywhere on the target document or selecting the three bars menu at the bottom of the document chrome const extraContentsOfDoc = InfoState("Lastly, all documents also have a context-sensitive toolbar. The toolbar contents vary depending on the document type.", { }, 'context-toolbar.png', [this.createNextButton(ending)]) const contentsofDoc = InfoState("You can access the context of a doc through right-clicking anywhere on the target document or selecting the three bars menu at the bottom of the document chrome", { }, 'dash-context-menu.gif', [this.createNextButton(extraContentsOfDoc)]) const propertiesofDoc = InfoState("You can also access the properties of a doc through the double arrows in the top right or the single arrow on the right edge of the screen", { }, 'dash-properties-pane.gif', [this.createNextButton(contentsofDoc)]) this.tutorialStates.editingDocuments = InfoState("Want to learn how to edit a document? Either left or right click the document", { }, 'document-chrome.png', [this.createNextButton(propertiesofDoc)]) return this.tutorialStates.start }; render() { if (!this.currState) return null; return ( ); } }