Improving Events Log Readability
To give more context to the events log messages, I will add a new type of message before all events of the same round. This will make it clear which round the events took place when reading the events log.
I want to keep the HTML rendering as simple as possible so I plan to add a new type of message to the events log store which is read by the related Alpine component. I start by updating the LiveView HTML template for the game view component with conditional logic to check for the message type. These types don’t yet exist on the messages in the store so I set the conditional check for rendering the original event message format to only check that it isn’t a round marker type message. This change will allow the existing logic to work regardless of having a message type set and keep things in a working state while I iterate on this feature.
<div
id="events-log"
class="flex-1 p-[0.5cqi] pt-0
text-left text-[1.5cqi] leading-none
bg-blue-50 border-slate-400 border-[0.25cqi] border-t-[0.2cqi]
overflow-y-auto flex flex-col-reverse"
x-data
>
<div class="space-y-[0.5cqi]">
<template x-for="message in $store.eventsLog">
<div>
<template x-if="message.type === 'ROUND_MARKER'">
<p class="p-[0.35cqi] break-words" x-text="`Round ${message.round}`"></p>
</template>
<template x-if="messge.type !== 'ROUND_MARKER'">
<p class="bg-blue-200 p-[0.35cqi] rounded break-words" x-text="message.text">
</p>
</template>
</div>
</template>
</div>
</div>
Pausing to refactor
There are currently two cases where game events are converted to event messages and added to the events log. The logic for inserting a round separator message in specific positions in the events log data store will need to be added to both of these cases. There is already some duplication of code for ordering game events and converting them to event messages so I will refactor these similar cases to wrap the duplicate logic.
The first case of adding events to the log is when the player joins the session and the events log is set with the initial batch of existing events from the server.
The second case is during a new round server event which will push new game events to be appended to the log.
I don’t really see a need to have a distinction between setMessages
and appendMessages
functions for the data store since setMessages
is only setting the events to an empty store which is no different than appending a list of messages to an empty store.
I create a new module to encapsulate the use case for taking game event objects and appending relevant event messages to the log. The exported function will handle ordering the game events, converting them to event messages, and appending messages to the log. After the refactor, I will add the logic for inserting round separator messages at the correct positions which will only need to be updated in one place.
import convertEventsToMessages from './convertGameEventsToMessages'
/**
* @typedef {import('../data/GameEvents').GameEvents} GameEvents
* @typedef {import('../../adapters/store/typedefs').Store} Store
*/
/**
* @param {Object} dependencies
* @param {Store} dependencies.store
* @returns {function(GameEvents, Number): void}
*/
export default function createAddGameEventsToLog({ store }) {
return function addGameEventsToLog(events, playerId) {
const eventMessages = convertEventsToMessages(events, playerId)
if (eventMessages.length) {
eventMessages.sort((a, b) => a.eventId - b.eventId)
store.eventsLog.appendMessages(eventMessages)
}
}
}
I replace the duplicate logic in the two server event handlers with a call to the new addGameEventsToLog
function and confirm the event messages are still rendering correctly for both cases by running the existing feature tests for the events log.
The convertEventsToMessages
module was called by the two server event handlers, but is now only called by the new addGameEventsToLog
module so I move the contents of the former module into the latter.
Adding the feature
I update addGameEventsToLog
to add a type
property with a value of GAME_EVENT
on all event messages appended to the events log Alpine store.
I then update the recently added conditional rendering in the game view template to show the regular event log message if message.type === 'GAME_EVENT'
instead of checking that it is not a ROUND_MARKER
.
I then implement the logic for inserting round marker messages to the list before they are appended to the events log store.
// …
function insertRoundMarkers(messages, currentMessage) {
if (currentMessage === null) {
return messages
}
const previousMessage = messages[messages.length - 1]
const { round } = currentMessage
const marker = { type: 'ROUND_MARKER', round }
if (!previousMessage || round > previousMessage.round) {
return [...messages, marker, currentMessage]
}
return [...messages, currentMessage]
}
export default function createAddGameEventsToLog({ store }) {
return function addGameEventsToLog(events, playerId) {
const eventMessages = events
.sort((a, b) => a.id - b.id)
.map((event) => convertGameEventToMessage(event, playerId))
.reduce(insertRoundMarkers, [])
if (eventMessages.length) {
store.eventsLog.appendMessages(eventMessages)
}
}
}
The events log is now showing the round marker messages to visually separate groups of events which happen on the same round!