Skip to content

Commit

Permalink
Properly scope page state tracking based on path
Browse files Browse the repository at this point in the history
  • Loading branch information
hopsoft committed May 17, 2024
1 parent f2fdcd5 commit 8e4766c
Show file tree
Hide file tree
Showing 8 changed files with 23 additions and 27 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,11 +504,9 @@ The code above will be expanded to this HTML.

Several things happen when you use `turbo_boost[:remember]` to track page state.

1. A command is dispatched whenever the value of a registered attribute changes.
1. The server updates tracked state and notifies the client.
1. Subsequent requests forward the current state to the server.
1. Server side rendering honors and reflects the current page state.
1. After a DOM update, the client verifies the page state and will restore attribute values _(if necessary)_.
1. The client builds the current page state before emitting requests to the server.
1. The server uses the page state when rendering the response.
1. The client client verifies the page state and restores attribute values _(if necessary)_ after the DOM updates.

This feature works with all attributes, including aria, data, and custom attributes.

Expand Down
2 changes: 1 addition & 1 deletion app/assets/builds/@turbo-boost/commands.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions app/assets/builds/@turbo-boost/commands.js.map

Large diffs are not rendered by default.

12 changes: 5 additions & 7 deletions app/javascript/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@ const Commands = {
}

function buildCommandPayload(id, element) {
const commandName = element.getAttribute(schema.commandAttribute)

return {
id, //----------------------------------------------------------- Uniquely identifies the command invocation
name: element.getAttribute(schema.commandAttribute), //---------- Command name
elementId: element.id.length > 0 ? element.id : null, //--------- ID of the element that triggered the command
elementAttributes: elements.buildAttributePayload(element), //--- Attributes of the element that triggered the command
startedAt: Date.now(), //---------------------------------------- Start time of when the command was invoked
id, //---------------------------------------------------------- Uniquely identifies the command invocation
name: element.getAttribute(schema.commandAttribute), //--------- Command name
elementId: element.id.length > 0 ? element.id : null, //-------- ID of the element that triggered the command
elementAttributes: elements.buildAttributePayload(element), //-- Attributes of the element that triggered the command
startedAt: Date.now(), //--------------------------------------- Start time of when the command was invoked
state: {
page: state.buildPageState(),
signed: state.signed,
Expand Down
15 changes: 10 additions & 5 deletions app/javascript/state/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,34 @@
import observable from './observable'
import page from './page'
import storage from './storage'
import { dispatch, stateEvents, turboEvents } from '../events'
import { dispatch, stateEvents } from '../events'

const key = 'TurboBoost::State'
const stub = { page: {}, signed: null, unsigned: {} }

let signed = null // signed state <string>
let unsigned = {} // unsigned state (optimistic) <object>
let restored = null // restored state <null, object> - used when restoring state

const restore = () => {
const saved = { ...stub, ...storage.find(key) }
page.restoreState(saved.page)
const path = window.location.pathname
saved.page[path] = saved.page[path] || {}
console.log('state.restore', saved)
page.restoreState(saved.page[path])
}

const save = () => {
const saved = { ...stub, ...storage.find(key) }
const fresh = {
signed: signed || saved.signed,
unsigned: { ...saved.unsigned, ...unsigned },
page: { ...saved.page, ...page.buildState() }
page: { ...saved.page }
}

console.log('save state', fresh)
const path = window.location.pathname
fresh.page[path] = { ...fresh.page[path], ...page.buildState() }

console.log('state.save', fresh)
storage.save(key, fresh)
}

Expand Down
4 changes: 0 additions & 4 deletions app/javascript/state/page.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import schema from '../schema.js'
import { dispatch, commandEvents, stateEvents } from '../events.js'

let timeout

const updateElement = (id, attribute, value, attempts = 1) => {
if (attempts > 20) return
Expand All @@ -25,7 +22,6 @@ const buildState = () => {
}

const restoreState = (state = {}) => {
console.log('restoreState', state)
for (const [id, attributes] of Object.entries(state)) {
for (const [attribute, value] of Object.entries(attributes)) updateElement(id, attribute, value)
}
Expand Down
1 change: 1 addition & 0 deletions bin/docker/run/local
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fi
# Dependencies
# ============================================================================================================
npm install
rm /mnt/external/.playwright
if [ ! -f "/mnt/external/.playwright" ]; then
yes | npx playwright@latest install chromium --with-deps
touch /mnt/external/.playwright
Expand Down
2 changes: 0 additions & 2 deletions test/dummy/db/schema.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# frozen_string_literal: true

# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
Expand Down

0 comments on commit 8e4766c

Please sign in to comment.