/** * Navigation for Petros Moris "Oracle" */ import React, { Component } from 'react' import { connect } from 'react-redux' import './petros.nav.css' import { history } from "site/store" import { preloadImage } from "app/utils" import actions from "site/actions" import { generateTransform } from 'app/views/tile/tile.utils' import PetrosText from "./petros.text" const RESET_STATE = { ready: false, hovering: null, textActive: false, textDone: false, textOpacity: 0.0, iconFade: false, text: null, } const NAV_GLYPH = { 0: { left: 6, right: 1 }, 1: { left: 6, right: 1 }, 2: { left: 2, right: 3 }, 3: { left: 1, right: 5 }, 4: { left: 3, right: 4 }, 5: { left: 5, right: 7 }, 6: { left: 4, right: 6 }, 7: { left: 7, right: 2 }, } const MOVEMENT = "movement" const LOOP = "loop" const INITIAL_VIEW = { 1: MOVEMENT, 2: LOOP, 3: LOOP, 4: MOVEMENT, 5: MOVEMENT, 6: MOVEMENT, 7: LOOP, } // set this to 0.1 in development, 1.0 otherwise :) const FASTFORWARD = 0.1 const LOOP_TIMEOUT = 6000 * FASTFORWARD const MOVEMENT_TIMEOUT = 40000 * FASTFORWARD const TEXT_LOAD_TIMEOUT = 15000 * FASTFORWARD const TEXT_SHOW_TIMEOUT = 20 const TEXT_HIDE_TIMEOUT = 15000 *FASTFORWARD const SUBTITLE_COUNT = 12 const TIME_PER_WORD = 250 class PetrosNav extends Component { state = { ...RESET_STATE, glyphTransform: {}, } constructor(props) { super(props) this.handleEnter = this.handleEnter.bind(this) this.handleLeave = this.handleLeave.bind(this) this.handleClickText = this.handleClickText.bind(this) this.textComplete = this.textComplete.bind(this) this.navigate = this.navigate.bind(this) } componentDidMount() { if (this.props.interactive) { this.load() } } componentDidUpdate(prevProps) { if ( (this.props.interactive && this.props.interactive !== prevProps.interactive) || this.props.location.pathname !== prevProps.location.pathname ) { this.load(prevProps.match && prevProps.match.params) } if (this.props.page && this.props.page !== prevProps.page && this.props.page.path.match("petros-")) { this.findGlyphPosition() } } load(lastParams) { const { page_name } = this.props.match.params const page_partz = page_name.split("-") const isPetros = page_partz[0] === 'petros' const index = parseInt(page_partz[1]) if (!isPetros) { clearTimeout(this.readyTimeout) clearTimeout(this.textTimeout) this.setState({ ...RESET_STATE, index: 0, // setting this unloads the component }) return } preloadImage(`/thelastmuseum/static/media/last-museum/petros-moris/OracleTextButton${index}-White.png`) preloadImage(`/thelastmuseum/static/media/last-museum/petros-moris/OracleTextButton${index}.png`) preloadImage(`/thelastmuseum/static/media/last-museum/petros-moris/NavBW${index}.png`) preloadImage(`/thelastmuseum/static/media/last-museum/petros-moris/NavB${index}.png`) this.setState({ ...RESET_STATE, index }) this.readyTimeout = setTimeout(() => { this.setState({ ready: true, }) }, INITIAL_VIEW[index] === MOVEMENT ? MOVEMENT_TIMEOUT : LOOP_TIMEOUT) } findGlyphPosition() { const { page } = this.props const bounds = { width: window.innerWidth, height: window.innerHeight } const videoBounds = { width: page.tiles[0].settings.width, height: page.tiles[0].settings.height, } page.tiles.some(tile => { if (tile.settings.popup_group === "glyph") { let transform = generateTransform(tile, null, bounds, videoBounds) transform = transform.replace(/scale(\d+\.\d+)/g, "") this.setState({ glyphTransform: { transform } }) } }) } handleEnter(event) { const side = event.target.className.match(/-(\w+)/)[1] this.setState({ hovering: side }) } handleLeave(event) { this.setState({ hovering: false }) } /** Start text generation sequence */ handleClickText() { if (this.state.textActive || this.state.text) return this.setState({ textActive: true, hovering: false, textOpacity: 0.0, text: null }) // Turn on the glyph setTimeout(() => { actions.site.setPopups({ ...this.props.popups, glyph: true, }) }, 500) // Fetch the subtitles const subtitle_index = 1 const subtitle_choice = randint(SUBTITLE_COUNT) + 1 fetch(`/thelastmuseum/static/media/last-museum/petros-moris/texts/${subtitle_index}/${subtitle_choice}.txt`, { method: 'GET', }) .then(res => res.text()) .then(text => this.setState({ text: text.trim() })) this.textTimeout = setTimeout(() => { this.setState({ textActive: false, textDone: true, textOpacity: 0.0, }) // Turn off the glyph actions.site.setPopups({ ...this.props.popups, glyph: false, }) this.textTimeout = setTimeout(() => { this.setState({ textOpacity: 1.0, iconFade: true }) this.textTimeout = setTimeout(() => { this.setState({ textOpacity: 0.0 }) }, TEXT_HIDE_TIMEOUT) }, TEXT_SHOW_TIMEOUT) }, TEXT_LOAD_TIMEOUT) } /** Navigate using the links at the bottom */ navigate() { const { index, textActive } = this.state const leftIndex = Math.max(1, index - 1) const rightIndex = Math.min(7, index + 1) if (textActive) return const side = event.target.className.match(/-(\w+)/)[1] // if navigating to the left, just navigate. if (side === "left") { if (index !== leftIndex) { history.push(`/thelastmuseum/petros-${leftIndex}`) } } else if (side === "right") { // if this page starts with the loop, switch to movement // this will autoadvance when the video is done (set in tile settings) if (INITIAL_VIEW[index] === LOOP) { actions.site.setPopups({ ...this.props.popups, movement: true, }) this.setState({ ready: false, iconFade: false, textOpacity: 0 }) clearTimeout(this.textTimeout) } else { // otherwise, just advance immediately. history.push(`/thelastmuseum/petros-${rightIndex}`) } } } render() { const { index, ready, hovering, iconFade, textActive, textDone, textOpacity, text, glyphTransform, } = this.state if (!this.props.interactive || (!index)) return null const leftIndex = NAV_GLYPH[index].left const rightIndex = NAV_GLYPH[index].right return (