frontispiece / apps / frontispiece-editor / src / view / story.ts
story.ts
Raw
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

import type { VisualInkLineOrCommand } from '@a-morphous/frontispiece-ink-processor/src/types'
import { Fragment } from 'preact'
import { useEffect, useRef, useState } from 'preact/hooks'
import { html, useInkHookState } from '../bootstrap/bootstrap'
import { useLayoutState } from '../state/layout-state'
import { advanceMaximally } from '../utils/advance-maximally'
import { LineViewer } from './line-viewer'

// TODO: implement a version where it doesn't necessarily advance line by line
export const InkStoryView = () => {
	const [showLog, setShowLog] = useState(false)
	const inkState = useInkHookState()
	const layoutState = useLayoutState()

	const ref = useRef<HTMLElement>()

	const advanceFunction = () => {
		if (layoutState.oneline.get()) {
			inkState.advance()

			// only hide older lines if we're in `oneline` mode
			if (layoutState.maxVisibleLines.get() > 0) {
				inkState.hideOlderLines(layoutState.maxVisibleLines.get())
			}
		} else {
			advanceMaximally(inkState)
		}

		if (layoutState.scrollAfterAdvance.get()) {
			setTimeout(() => {
				document.querySelector('#story .active').scrollIntoView({
					block: 'start',
					behavior: 'smooth',
				})
			}, 10)
		}
	}
	const lines = inkState.getVisibleLines({
		showHidden: showLog,
		showCommands: false,
	})

	useEffect(() => {
		if (inkState.getCanAdvance()) {
			advanceFunction()
		}
	}, [])

	const renderStory = () => {
		const activeSection = inkState.getActiveSection()
		return html`<div id="story">
			${lines.map((line) => {
				let isActive = false
				// hackety hack hack
				if (layoutState.oneline.get()) {
					if (line === inkState.getActiveLine()) {
						isActive = true
					}
				} else {
					if (activeSection.lines.indexOf(line as VisualInkLineOrCommand) >= 0) {
						isActive = true
					}
				}

				return html`<${LineViewer} line=${line} isActive=${isActive} />`
			})}
		</div>`
	}

	const renderAdvanceButton = () => {
		if (inkState.getCanAdvance()) {
			return html`<button
				id="continue-button"
				onclick=${() => {
					advanceFunction()
				}}
			>
				Continue
			</button>`
		}
		return null
	}

	const renderChoices = () => {
		if (inkState.getCanAdvance()) {
			return null
		}

		return html`<div id="choices">
			${inkState.getChoices().map((choice) => {
				return html`<button
					class="choice"
					onclick=${() => {
						if (layoutState.clearAfterChoice.get()) {
							inkState.hideAllVisible()
						}

						inkState.makeChoice(choice.index)

						if (layoutState.oneline.get()) {
							advanceFunction()
						} else {
							inkState.advance()
							advanceFunction()
						}
					}}
				>
					${choice.text}
				</button>`
			})}
		</div>`
	}

	const renderLogButton = () => {
		const logButtonText = showLog ? 'Hide Log' : 'Show Log'
		return html`<button
			onclick=${() => {
				setShowLog(!showLog)
			}}
		>
			${logButtonText}
		</button>`
	}

	return html`<${Fragment}>
		<div id="story-view">${renderStory()} ${renderAdvanceButton()} ${renderChoices()}</div>
		<div ref=${ref} id="scroll-pointer"></div>
	<//>`
}