mirror of
https://github.com/jcreek/EstimationPoker.git
synced 2026-07-13 02:53:46 +00:00
feat(#5): Add the ability to select pre-defined card sets
This commit is contained in:
@@ -1,28 +1,31 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let values: Array<number | string> = [1, 2, 3, 5, 8, 13, 21, '?'];
|
export let values: Array<number | string>;
|
||||||
export let onEstimateClick: (value: number | string) => void;
|
export let onEstimateClick: (value: number | string) => void;
|
||||||
export let disableEstimates: boolean = false;
|
export let disableEstimates: boolean = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div id="estimates-list">
|
<div id="estimates-list">
|
||||||
<div class="centered-container">
|
<div class="centered-container">
|
||||||
{#each values as value}
|
|
||||||
<button
|
{#if values !== undefined}
|
||||||
disabled={disableEstimates}
|
{#each values as value}
|
||||||
class="{disableEstimates ? 'estimate-card disabled' : 'estimate-card'}"
|
<button
|
||||||
on:click={() => onEstimateClick(value)}
|
disabled={disableEstimates}
|
||||||
on:keydown={(event) => {
|
class="{disableEstimates ? 'estimate-card disabled' : 'estimate-card'}"
|
||||||
if (event.key === 'Enter' || event.key === ' ') {
|
on:click={() => onEstimateClick(value)}
|
||||||
event.preventDefault();
|
on:keydown={(event) => {
|
||||||
onEstimateClick(value);
|
if (event.key === 'Enter' || event.key === ' ') {
|
||||||
}
|
event.preventDefault();
|
||||||
}}
|
onEstimateClick(value);
|
||||||
aria-label={`Estimate: ${value}`}
|
}
|
||||||
tabindex="0"
|
}}
|
||||||
>
|
aria-label={`Estimate: ${value}`}
|
||||||
<p class="value">{value}</p>
|
tabindex="0"
|
||||||
</button>
|
>
|
||||||
{/each}
|
<p class="value">{value}</p>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+36
-1
@@ -1,11 +1,26 @@
|
|||||||
<script>
|
<script>
|
||||||
import { generateId } from './estimation/estimation.js';
|
import { onMount } from 'svelte';
|
||||||
|
import { connectToWebSocket, sendMessage, generateId } from './estimation/estimation.js';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
|
||||||
let roomId = '';
|
let roomId = '';
|
||||||
|
let socket;
|
||||||
|
const cardSets = [
|
||||||
|
{ name: 'Fibonacci', values: [1, 2, 3, 5, 8, 13, 21, '?'], example: '(1, 2, 3, 5)' },
|
||||||
|
{
|
||||||
|
name: 'T-Shirt Sizing',
|
||||||
|
values: ['XS', 'S', 'M', 'L', 'XL', 'XXL', '?'],
|
||||||
|
example: '(XS, S, M, L)'
|
||||||
|
},
|
||||||
|
{ name: 'Powers of 2', values: [1, 2, 4, 8, 16, 32, '?'], example: '(1, 2, 4, 8)' },
|
||||||
|
{ name: 'Sequential', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, '?'], example: '(1, 2, 3, 4)' }
|
||||||
|
];
|
||||||
|
|
||||||
|
let selectedCardSet = cardSets[0];
|
||||||
|
|
||||||
function startARoom() {
|
function startARoom() {
|
||||||
const roomId = generateId();
|
const roomId = generateId();
|
||||||
|
sendMessage(socket, { roomId: roomId, type: 'create-room', cardSetName: selectedCardSet.name });
|
||||||
goto(`/estimation/${roomId}`);
|
goto(`/estimation/${roomId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,12 +31,32 @@
|
|||||||
goto(`/estimation/${roomId}`);
|
goto(`/estimation/${roomId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function onMessageReceived(message) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
socket = connectToWebSocket(null, onMessageReceived);
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (socket.readyState === WebSocket.OPEN) {
|
||||||
|
socket.send(JSON.stringify({ type: 'ping' }));
|
||||||
|
}
|
||||||
|
}, 45000); // send a ping every 45 seconds
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p>
|
<p>
|
||||||
Simply start a room and share the URL to everyone else who needs to join the estimation session.
|
Simply start a room and share the URL to everyone else who needs to join the estimation session.
|
||||||
</p>
|
</p>
|
||||||
|
<div class="dropdown">
|
||||||
|
<select on:change={(e) => (selectedCardSet = cardSets[e.target.selectedIndex])}>
|
||||||
|
{#each cardSets as cardSet, i}
|
||||||
|
<option value={i}>{cardSet.name} {cardSet.example}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<button class="button button-green" on:click={startARoom}>Start a room</button>
|
<button class="button button-green" on:click={startARoom}>Start a room</button>
|
||||||
<div class="join-room-container">
|
<div class="join-room-container">
|
||||||
<label for="room-id">Join a room:</label>
|
<label for="room-id">Join a room:</label>
|
||||||
|
|||||||
@@ -56,11 +56,12 @@
|
|||||||
let showModal = true;
|
let showModal = true;
|
||||||
const userId = generateId();
|
const userId = generateId();
|
||||||
let users: Array<User> = [];
|
let users: Array<User> = [];
|
||||||
let estimateGroups: { [key: number]: string[] } = {};
|
let estimateGroups: { [key: number | string]: string[] } = {};
|
||||||
let showRestartButton = false;
|
let showRestartButton = false;
|
||||||
let showEstimates = false;
|
let showEstimates = false;
|
||||||
let disableEstimates: boolean = false;
|
let disableEstimates: boolean = false;
|
||||||
let audioElement;
|
let audioElement;
|
||||||
|
let selectedCardSet;
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
showModal = false;
|
showModal = false;
|
||||||
@@ -104,6 +105,8 @@
|
|||||||
if (message.type === 'user-joined') {
|
if (message.type === 'user-joined') {
|
||||||
sendMessage(socket, { type: 'get-user-estimates' });
|
sendMessage(socket, { type: 'get-user-estimates' });
|
||||||
} else if (message.type === 'user-estimates') {
|
} else if (message.type === 'user-estimates') {
|
||||||
|
selectedCardSet = message.selectedCardSet.values;
|
||||||
|
|
||||||
message.users.forEach((user) => {
|
message.users.forEach((user) => {
|
||||||
let updatedUser = users.find((u) => u.userId === user.userId);
|
let updatedUser = users.find((u) => u.userId === user.userId);
|
||||||
if (updatedUser === undefined) {
|
if (updatedUser === undefined) {
|
||||||
@@ -187,7 +190,7 @@
|
|||||||
>Share Room Link</button
|
>Share Room Link</button
|
||||||
>
|
>
|
||||||
|
|
||||||
<Estimates {disableEstimates} onEstimateClick={handleEstimateClick} />
|
<Estimates values={selectedCardSet} {disableEstimates} onEstimateClick={handleEstimateClick} />
|
||||||
|
|
||||||
{#if Object.keys(estimateGroups).length > 0}
|
{#if Object.keys(estimateGroups).length > 0}
|
||||||
<EstimateGroupsList {estimateGroups} />
|
<EstimateGroupsList {estimateGroups} />
|
||||||
|
|||||||
@@ -1,12 +1,43 @@
|
|||||||
import { WebSocket } from 'ws';
|
import { WebSocket } from 'ws';
|
||||||
import { User } from './User';
|
import { User } from './User';
|
||||||
|
|
||||||
|
class CardSet {
|
||||||
|
name: string;
|
||||||
|
values: Array<string | number>;
|
||||||
|
example: string;
|
||||||
|
|
||||||
|
constructor(name: string, values: Array<string | number>, example: string) {
|
||||||
|
this.name = name;
|
||||||
|
this.values = values;
|
||||||
|
this.example = example;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cardSets: Array<CardSet> = [
|
||||||
|
{ name: 'Fibonacci', values: [1, 2, 3, 5, 8, 13, 21, '?'], example: '(1, 2, 3, 5)' },
|
||||||
|
{
|
||||||
|
name: 'T-Shirt Sizing',
|
||||||
|
values: ['XS', 'S', 'M', 'L', 'XL', 'XXL', '?'],
|
||||||
|
example: '(XS, S, M, L)'
|
||||||
|
},
|
||||||
|
{ name: 'Powers of 2', values: [1, 2, 4, 8, 16, 32, '?'], example: '(1, 2, 4, 8)' },
|
||||||
|
{ name: 'Sequential', values: [1, 2, 3, 4, 5, 6, 7, 8, 9, '?'], example: '(1, 2, 3, 4)' }
|
||||||
|
];
|
||||||
|
|
||||||
export class Room {
|
export class Room {
|
||||||
id: string;
|
id: string;
|
||||||
|
cardSet: CardSet;
|
||||||
users: Map<WebSocket, User>;
|
users: Map<WebSocket, User>;
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string, cardSetName: string) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
const cardSet = cardSets.find((cardSet) => cardSet.name === cardSetName);
|
||||||
|
if (cardSet) {
|
||||||
|
this.cardSet = cardSet;
|
||||||
|
} else {
|
||||||
|
this.cardSet = cardSets[0];
|
||||||
|
}
|
||||||
|
|
||||||
this.users = new Map();
|
this.users = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-6
@@ -1,6 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import { WebSocketServer, WebSocket } from 'ws';
|
import { WebSocketServer, WebSocket } from 'ws';
|
||||||
import { Room } from './classes/Room.js';
|
import { Room, cardSets } from './classes/Room.js';
|
||||||
import { User } from './classes/User.js';
|
import { User } from './classes/User.js';
|
||||||
import { JoinRoomMessage } from './classes/messages/JoinRoomMessage.js';
|
import { JoinRoomMessage } from './classes/messages/JoinRoomMessage.js';
|
||||||
import { ChangeEstimateMessage } from './classes/messages/ChangeEstimateMessage.js';
|
import { ChangeEstimateMessage } from './classes/messages/ChangeEstimateMessage.js';
|
||||||
@@ -62,23 +62,31 @@ wss.on('connection', (ws: WebSocket, req) => {
|
|||||||
|
|
||||||
ws.on('error', onSocketPostError);
|
ws.on('error', onSocketPostError);
|
||||||
|
|
||||||
|
function createRoom(roomId: string, cardSetName: string) {
|
||||||
|
room = new Room(roomId, cardSetName);
|
||||||
|
rooms.add(room);
|
||||||
|
}
|
||||||
|
|
||||||
ws.on('message', (message) => {
|
ws.on('message', (message) => {
|
||||||
const data = JSON.parse(message.toString());
|
const data = JSON.parse(message.toString());
|
||||||
|
|
||||||
if (data.type === 'join-room') {
|
if (data.type === 'create-room') {
|
||||||
|
const roomId = data.roomId;
|
||||||
|
const cardSetName = data.cardSetName;
|
||||||
|
createRoom(roomId, cardSetName);
|
||||||
|
} else if (data.type === 'join-room') {
|
||||||
const joinRoomMessage: JoinRoomMessage = data as JoinRoomMessage;
|
const joinRoomMessage: JoinRoomMessage = data as JoinRoomMessage;
|
||||||
const { roomId, userId, name } = joinRoomMessage;
|
const { roomId, userId, name } = joinRoomMessage;
|
||||||
room = getRoomById(roomId);
|
room = getRoomById(roomId);
|
||||||
|
|
||||||
// Create room if it doesn't already exist
|
// Create room if it doesn't already exist
|
||||||
if (!room) {
|
if (!room) {
|
||||||
room = new Room(roomId);
|
createRoom(roomId, data.cardSetName);
|
||||||
rooms.add(room);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the user in the room
|
// Create the user in the room
|
||||||
const user = new User(userId, name);
|
const user = new User(userId, name);
|
||||||
room.addUser(ws, user);
|
room!.addUser(ws, user);
|
||||||
|
|
||||||
broadcastToRoom(roomId, { type: 'user-joined', message: `${name} has joined the room` });
|
broadcastToRoom(roomId, { type: 'user-joined', message: `${name} has joined the room` });
|
||||||
} else if (data.type === 'select-estimate') {
|
} else if (data.type === 'select-estimate') {
|
||||||
@@ -144,7 +152,7 @@ wss.on('connection', (ws: WebSocket, req) => {
|
|||||||
} else if (data.type === 'get-user-estimates') {
|
} else if (data.type === 'get-user-estimates') {
|
||||||
if (room) {
|
if (room) {
|
||||||
const users = room.getUsers();
|
const users = room.getUsers();
|
||||||
broadcastToRoom(room.id, { type: 'user-estimates', users });
|
broadcastToRoom(room.id, { type: 'user-estimates', users, selectedCardSet: room.cardSet });
|
||||||
}
|
}
|
||||||
} else if (data.type === 'trigger-emoji') {
|
} else if (data.type === 'trigger-emoji') {
|
||||||
const { cardId, emoji } = data;
|
const { cardId, emoji } = data;
|
||||||
|
|||||||
Reference in New Issue
Block a user