From 437929a9f3b35119440a5913c465ca150a81762b Mon Sep 17 00:00:00 2001 From: Ezequiel Adrian Schwartzman Date: Tue, 24 Dec 2024 13:52:43 -0300 Subject: [PATCH] Added logs for turn, move, join, left, add and remove --- ui/src/GameSpace/GameSpace.svelte | 5 +- .../GameSpace/elements/PieceSource/grammar.ts | 2 +- .../elements/TurnTracker/Element.svelte | 46 +++- ui/src/GameSpace/topbar/ActivityLog.svelte | 228 +++++++++--------- ui/src/GameSpace/topbar/PeopleBar.svelte | 25 -- ui/src/store/gameSpaceStore.ts | 6 +- ui/src/store/grammar.ts | 95 ++++++-- ui/src/store/index.ts | 13 +- ui/src/store/presets/blank.ts | 4 +- ui/src/store/presets/checkers.ts | 4 +- ui/src/store/presets/chess.ts | 4 +- ui/src/store/presets/go.ts | 4 +- ui/src/store/presets/world.ts | 4 +- ui/src/store/types.ts | 28 ++- 14 files changed, 292 insertions(+), 176 deletions(-) diff --git a/ui/src/GameSpace/GameSpace.svelte b/ui/src/GameSpace/GameSpace.svelte index 9c32c49..c14a56b 100644 --- a/ui/src/GameSpace/GameSpace.svelte +++ b/ui/src/GameSpace/GameSpace.svelte @@ -94,7 +94,6 @@ function handleAddElementFromLibrary(element: LibraryElement, x?: number, y?: number) { const surfaceCoords = x && y ? gameSpace.getSurfaceCoordinates(x, y) : gameSpace.getCurrentCenter(); - console.log('SURFACE COORDs', surfaceCoords); if (surfaceCoords) { gameSpace.change({ type: 'add-element', @@ -206,7 +205,9 @@ onChangePlayersSlots={handlePlayersSlotsChange} /> {/if} - + {#if !$state.isLibraryItem} + + {/if} diff --git a/ui/src/store/gameSpaceStore.ts b/ui/src/store/gameSpaceStore.ts index 4e0824f..3cc6d26 100644 --- a/ui/src/store/gameSpaceStore.ts +++ b/ui/src/store/gameSpaceStore.ts @@ -141,7 +141,7 @@ export function createGameSpaceSynStore(synDoc: SynDoc) { await synDoc.change((state, _eph) => { console.time('Running deltas'); for (const delta of deltas) { - applyDelta(delta, state); + applyDelta(delta, state, { pubKey }); } console.timeEnd('Running deltas'); }, force); @@ -149,11 +149,11 @@ export function createGameSpaceSynStore(synDoc: SynDoc) { } function joinGame() { - change({ type: 'add-player', player: pubKey }); + change({ type: 'join-game' }); } function leaveGame() { - change({ type: 'remove-player', player: pubKey }); + change({ type: 'leave-game' }); } // ██████╗ ████████╗██╗ ██╗███████╗██████╗ diff --git a/ui/src/store/grammar.ts b/ui/src/store/grammar.ts index aa3cefd..e01f0ef 100644 --- a/ui/src/store/grammar.ts +++ b/ui/src/store/grammar.ts @@ -6,7 +6,8 @@ import { type AgentPubKeyB64, encodeHashToBase64 } from '@holochain/client'; import { colorSequence } from '~/lib/util'; import * as elements from '../GameSpace/elements'; -import type { GameSpace, GElement } from './types'; +import { LIBRARY } from './library'; +import type { GameSpace, GElement, LogType } from './types'; export type Delta = | { type: 'set-is-archived'; value: boolean } @@ -15,19 +16,21 @@ export type Delta = | { type: 'set-icon'; icon: string } | { type: 'set-players-slots'; playersSlots: GameSpace['playersSlots'] } | { type: 'set-is-stewarded'; isStewarded: boolean } - | { type: 'add-player'; player: AgentPubKeyB64 } - | { type: 'remove-player'; player: AgentPubKeyB64 } + | { type: 'join-game' } + | { type: 'leave-game' } | { type: 'add-element'; element: GElement } | { type: 'move-element'; uuid: string; x: number; y: number } | { type: 'resize-element'; uuid: string; width: number; height: number } | { type: 'rotate-element'; uuid: string; rotation: number } | { type: 'move-z'; uuid: string; z: 'top' | 'bottom' | 'up' | 'down' } | { type: 'update-element'; element: Partial } - | { type: 'remove-element'; uuid: string }; + | { type: 'remove-element'; uuid: string } + | { type: 'add-log'; log: { message: string; type: LogType; pubKey?: string } } + | { type: 'seen-activity-log' }; export function initialState(pubKey: string): GameSpace { return { - version: 8, + version: 9, name: 'Game Space', icon: '♟', creator: pubKey, @@ -43,10 +46,12 @@ export function initialState(pubKey: string): GameSpace { { color: colorSequence(3, 4), pubKey: null }, ], lastChangeAt: Date.now(), + activityLog: [], + notificationsConfigOverride: {}, }; } -export const applyDelta = (delta: Delta, $state: GameSpace) => { +export const applyDelta = (delta: Delta, $state: GameSpace, context: { pubKey: string }) => { switch (delta.type) { case 'set-is-archived': $state.isArchived = delta.value; @@ -60,38 +65,66 @@ export const applyDelta = (delta: Delta, $state: GameSpace) => { case 'set-icon': $state.icon = delta.icon; break; - case 'set-players-slots': + case 'set-players-slots': { + const incoming = delta.playersSlots.map((s) => s.pubKey); + const current = $state.playersSlots.map((s) => s.pubKey); + const added = incoming.filter((p) => !current.includes(p)); + const removed = current.filter((p) => !incoming.includes(p)); + if (added.length > 0) { + added.forEach((pubKey) => { + addLog({ message: 'joined the game', type: 'join', pubKey }); + }); + } + if (removed.length > 0) { + removed.forEach((pubKey) => { + addLog({ + message: context.pubKey !== pubKey ? 'was removed from the game' : 'left the game', + type: 'left', + pubKey, + }); + }); + } $state.playersSlots = delta.playersSlots; break; + } case 'set-is-stewarded': $state.isStewarded = delta.isStewarded; break; - case 'add-player': + case 'join-game': const freeSlot = $state.playersSlots.findIndex((p) => p.pubKey === null); - const alreadyPlaying = $state.playersSlots.find((p) => p.pubKey === delta.player); + const alreadyPlaying = $state.playersSlots.find((p) => p.pubKey === context.pubKey); if (freeSlot !== -1 && !alreadyPlaying) { - $state.playersSlots[freeSlot].pubKey = delta.player; + $state.playersSlots[freeSlot].pubKey = context.pubKey; } + addLog({ message: 'joined the game', type: 'join' }); break; - case 'remove-player': - const playerIndex = $state.playersSlots.findIndex((p) => p.pubKey === delta.player); + case 'leave-game': + const playerIndex = $state.playersSlots.findIndex((p) => p.pubKey === context.pubKey); if (playerIndex !== -1) { $state.playersSlots[playerIndex].pubKey = null; + addLog({ message: 'left the game', type: 'left' }); } + break; - case 'add-element': + case 'add-element': { const elements = $state.elements; const maxZ = elements.reduce((max, el) => (el.z > max ? el.z : max), 0); const elementToAdd = { ...delta.element, uuid: delta.element.uuid || uuidv1(), z: maxZ + 1 }; $state.elements.push(elementToAdd); + const label = getLabel(elementToAdd.type); + addLog({ type: 'add', message: `added ${label}`, elRef: elementToAdd.uuid }); break; + } case 'move-element': $state.elements.forEach((e) => { if (e.uuid === delta.uuid) { e.x = delta.x; e.y = delta.y; + const label = getLabel(e.type); + addLog({ message: `moved ${label}`, type: 'move', elRef: delta.uuid }); } }); + break; case 'resize-element': $state.elements.forEach((e) => { @@ -155,13 +188,45 @@ export const applyDelta = (delta: Delta, $state: GameSpace) => { e.z = i; // This actually mutates the element }); break; - case 'remove-element': + case 'remove-element': { const index = $state.elements.findIndex((e) => e.uuid === delta.uuid); if (index === -1) return; + const el = $state.elements[index]; $state.elements.splice(index, 1); + const label = getLabel(el.type); + addLog({ message: `removed ${label}`, type: 'remove', elRef: delta.uuid }); + + break; + } + case 'seen-activity-log': + $state.activityLog.forEach((l) => { + if (l.seenBy.indexOf(context.pubKey) === -1) { + l.seenBy.push(context.pubKey); + } + }); + // $state.activityLog = $state.activityLog.map((l) => ({ ...l, seenBy: [...l.seenBy, agentKey] })) + break; + case 'add-log': + addLog(delta.log); break; } + function getLabel(elType: string) { + // TODO: Maybe fix sometime that player pieces don't have a library item + return LIBRARY.find((l) => l.type === elType)?.label || 'Player Piece'; + } + + function addLog(log: { message: string; type: LogType; pubKey?: string; elRef?: string }) { + $state.activityLog.push({ + type: log.type, + message: log.message, + time: Date.now(), + seenBy: [context.pubKey], + agentKey: log.pubKey || context.pubKey, + elRef: log.elRef || null, + }); + } + for (let e in elements) { const El = elements[e]; if (typeof El['applyDelta'] === 'function') { @@ -170,5 +235,5 @@ export const applyDelta = (delta: Delta, $state: GameSpace) => { } $state.lastChangeAt = Date.now(); - $state.version = 8; + $state.version = 9; }; diff --git a/ui/src/store/index.ts b/ui/src/store/index.ts index 747205d..fa9bbf3 100644 --- a/ui/src/store/index.ts +++ b/ui/src/store/index.ts @@ -1,6 +1,17 @@ export { type Delta } from './grammar'; export { createRootStore, type RootStore, setContext, getContext } from './rootStore'; export { type GameSpaceSyn } from './gameSpaceStore'; -export type { GameSpace, LockConfig, GElementBase, GElement, PlayerSlot } from './types'; +export type { + GameSpace, + LockConfig, + GElementBase, + GElement, + PlayerSlot, + AgentKey, + NotificationsConfig, + DEFAULT_NOTIFICATIONS_CONFIG, + Log, + LogType, +} from './types'; export { type LibraryElement, createElement, LIBRARY } from './library'; export * as presets from './presets'; diff --git a/ui/src/store/presets/blank.ts b/ui/src/store/presets/blank.ts index 4858d37..02afd36 100644 --- a/ui/src/store/presets/blank.ts +++ b/ui/src/store/presets/blank.ts @@ -26,7 +26,9 @@ export default { ], isLibraryItem: true, isArchived: false, - version: 8, + version: 9, icon: '🟩', wals: [], + activityLog: [], + notificationsConfigOverride: {}, } as const satisfies GameSpace; diff --git a/ui/src/store/presets/checkers.ts b/ui/src/store/presets/checkers.ts index 8e3d7e2..4b6d17f 100644 --- a/ui/src/store/presets/checkers.ts +++ b/ui/src/store/presets/checkers.ts @@ -831,7 +831,9 @@ export default { ], isLibraryItem: true, isArchived: false, - version: 8, + version: 9, icon: '🔴', wals: [], + activityLog: [], + notificationsConfigOverride: {}, } as const satisfies GameSpace; diff --git a/ui/src/store/presets/chess.ts b/ui/src/store/presets/chess.ts index cb277fd..ef67700 100644 --- a/ui/src/store/presets/chess.ts +++ b/ui/src/store/presets/chess.ts @@ -831,7 +831,9 @@ export default { ], isLibraryItem: true, isArchived: false, - version: 8, + version: 9, icon: '♟', wals: [], + activityLog: [], + notificationsConfigOverride: {}, } as const satisfies GameSpace; diff --git a/ui/src/store/presets/go.ts b/ui/src/store/presets/go.ts index 1037462..521b6ee 100644 --- a/ui/src/store/presets/go.ts +++ b/ui/src/store/presets/go.ts @@ -143,7 +143,9 @@ export default { ], isLibraryItem: true, isArchived: false, - version: 8, + version: 9, icon: '⚪️', wals: [], + activityLog: [], + notificationsConfigOverride: {}, } as const satisfies GameSpace; diff --git a/ui/src/store/presets/world.ts b/ui/src/store/presets/world.ts index 2c8666e..f9d9db2 100644 --- a/ui/src/store/presets/world.ts +++ b/ui/src/store/presets/world.ts @@ -90,7 +90,9 @@ export default { ], isLibraryItem: true, isArchived: false, - version: 8, + version: 9, icon: '🗺', wals: [], + activityLog: [], + notificationsConfigOverride: {}, } as const satisfies GameSpace; diff --git a/ui/src/store/types.ts b/ui/src/store/types.ts index 23e1257..6fdcb83 100644 --- a/ui/src/store/types.ts +++ b/ui/src/store/types.ts @@ -2,7 +2,9 @@ import type { WeaveUrl } from '@theweave/api'; import * as elements from '~/GameSpace/elements'; -export const VERSION = 8; +export const VERSION = 9; + +export type AgentKey = string; export type GameSpace = { version: typeof VERSION; @@ -16,6 +18,30 @@ export type GameSpace = { isArchived: boolean; playersSlots: PlayerSlot[]; lastChangeAt: number; + activityLog: Log[]; + notificationsConfigOverride: Record; +}; + +export const DEFAULT_NOTIFICATIONS_CONFIG: NotificationsConfig = { + turn: true, + move: false, + join: true, + left: true, + add: false, + remove: false, +}; + +export type NotificationsConfig = Record; + +export type LogType = 'turn' | 'move' | 'join' | 'left' | 'add' | 'remove'; + +export type Log = { + message: string; + time: number; + seenBy: string[]; + type: LogType; + agentKey: AgentKey; + elRef: string | null; }; export type PlayerSlot = {