mirror of
https://github.com/jcreek/EstimationPoker.git
synced 2026-07-13 19:13:44 +00:00
feat(#23): triggering fireworks when everyone's on board 🎆
This commit is contained in:
@@ -0,0 +1,102 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount, onDestroy } from 'svelte';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
type Rocket = {
|
||||||
|
id: number;
|
||||||
|
x: string;
|
||||||
|
y: string;
|
||||||
|
colour: string;
|
||||||
|
}
|
||||||
|
const rockets = writable<Rocket[]>([]);
|
||||||
|
let rocketIntervalId;
|
||||||
|
let rocketCount: number = 0;
|
||||||
|
|
||||||
|
function randomLocation(): { x: string; y: string;} {
|
||||||
|
return {
|
||||||
|
x: `${Math.random() * window.innerWidth - window.innerWidth / 2}px`,
|
||||||
|
y: `${Math.random() * window.innerHeight - window.innerHeight / 2}px`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomColour(): string {
|
||||||
|
return `hsl(${Math.floor(Math.random() * 361)}, 100%, 50%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomTimer(): number {
|
||||||
|
return Math.floor(Math.random() * (150 - 30 + 1)) + 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupRocket(): Rocket {
|
||||||
|
const colour = randomColour();
|
||||||
|
const { x, y } = randomLocation();
|
||||||
|
const newRocket: Rocket = { id: rocketCount++, x, y, colour };
|
||||||
|
return newRocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
function launchRockets() {
|
||||||
|
const newRocket = setupRocket();
|
||||||
|
rockets.update((currentRockets) => {
|
||||||
|
if (currentRockets.length > 25) {
|
||||||
|
// only keep 100 rockets
|
||||||
|
currentRockets.shift();
|
||||||
|
}
|
||||||
|
return [...currentRockets, newRocket]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
rocketIntervalId = setInterval(launchRockets, randomTimer());
|
||||||
|
setTimeout(()=>{ clearInterval(rocketIntervalId) }, 13000)
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
clearInterval(rocketIntervalId);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.rocket {
|
||||||
|
--x: 0;
|
||||||
|
--y: 0;
|
||||||
|
background-color: rebeccapurple;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
height: 5px;
|
||||||
|
width: 5px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rocket.move {
|
||||||
|
animation: move 1000ms linear forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move {
|
||||||
|
to {
|
||||||
|
transform: translate(var(--x), var(--y));
|
||||||
|
}
|
||||||
|
|
||||||
|
95% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="launch-pad">
|
||||||
|
{#each $rockets as rocket (rocket.id)}
|
||||||
|
<span
|
||||||
|
class="rocket move"
|
||||||
|
style="
|
||||||
|
--x: {rocket.x};
|
||||||
|
--y: {rocket.y};
|
||||||
|
background: {rocket.colour};
|
||||||
|
"
|
||||||
|
></span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
const changesLog = [
|
const changesLog = [
|
||||||
{ timestamp: '2024-01-05T00:00:00', message: 'Added the ability to kick a user' },
|
{ timestamp: '2024-01-05T00:00:00', message: 'Added the ability to kick a user' },
|
||||||
{ timestamp: '2024-01-05T00:00:00', message: 'Added update notes' },
|
{ timestamp: '2024-01-05T00:00:00', message: 'Added update notes' }
|
||||||
];
|
];
|
||||||
|
|
||||||
let newChanges = [];
|
let newChanges = [];
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
import EstimateGroupsList from '../../../components/EstimateGroupsList.svelte';
|
import EstimateGroupsList from '../../../components/EstimateGroupsList.svelte';
|
||||||
import Modal from '../../../components/Modal.svelte';
|
import Modal from '../../../components/Modal.svelte';
|
||||||
import Estimates from '../../../components/Estimates.svelte';
|
import Estimates from '../../../components/Estimates.svelte';
|
||||||
|
import FireworkShow from '../../../components/FireworkShow.svelte';
|
||||||
|
|
||||||
import { connectToWebSocket, sendMessage, generateId } from '../estimation.js';
|
import { connectToWebSocket, sendMessage, generateId } from '../estimation.js';
|
||||||
|
|
||||||
@@ -89,7 +90,9 @@
|
|||||||
let showEstimates = false;
|
let showEstimates = false;
|
||||||
let disableEstimates: boolean = false;
|
let disableEstimates: boolean = false;
|
||||||
let audioElement;
|
let audioElement;
|
||||||
|
let fireworks;
|
||||||
let selectedCardSet;
|
let selectedCardSet;
|
||||||
|
let showFireworks = false;
|
||||||
|
|
||||||
function closeModal() {
|
function closeModal() {
|
||||||
showModal = false;
|
showModal = false;
|
||||||
@@ -142,6 +145,16 @@
|
|||||||
sendMessage(socket, { roomId: data.roomId, type: 'restart-estimation' });
|
sendMessage(socket, { roomId: data.roomId, type: 'restart-estimation' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function areEstimatesSame(estimateGroups: { [key: number | string]: string[] }): boolean {
|
||||||
|
const estimates = Object.keys(estimateGroups);
|
||||||
|
const users = Object.values(estimateGroups);
|
||||||
|
const allUsers = users.flat();
|
||||||
|
|
||||||
|
if (allUsers.length > 2 && estimates.length === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onMessageReceived(message) {
|
function onMessageReceived(message) {
|
||||||
if (message.type === 'user-joined') {
|
if (message.type === 'user-joined') {
|
||||||
sendMessage(socket, { type: 'get-user-estimates' });
|
sendMessage(socket, { type: 'get-user-estimates' });
|
||||||
@@ -166,10 +179,17 @@
|
|||||||
users = users.filter((user) => user.userId !== message.userId);
|
users = users.filter((user) => user.userId !== message.userId);
|
||||||
} else if (message.type === 'estimation-closed') {
|
} else if (message.type === 'estimation-closed') {
|
||||||
estimateGroups = message.groupedEstimates;
|
estimateGroups = message.groupedEstimates;
|
||||||
|
showFireworks = areEstimatesSame(estimateGroups);
|
||||||
|
if (showFireworks) {
|
||||||
|
fireworks.play();
|
||||||
|
}
|
||||||
showRestartButton = true;
|
showRestartButton = true;
|
||||||
showEstimates = true;
|
showEstimates = true;
|
||||||
disableEstimates = true;
|
disableEstimates = true;
|
||||||
} else if (message.type === 'estimation-restarted') {
|
} else if (message.type === 'estimation-restarted') {
|
||||||
|
showFireworks = false;
|
||||||
|
fireworks.pause();
|
||||||
|
fireworks.currentTime = 0;
|
||||||
estimateGroups = {};
|
estimateGroups = {};
|
||||||
showRestartButton = false;
|
showRestartButton = false;
|
||||||
showEstimates = false;
|
showEstimates = false;
|
||||||
@@ -244,17 +264,21 @@
|
|||||||
<EstimateGroupsList {estimateGroups} />
|
<EstimateGroupsList {estimateGroups} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if showFireworks}
|
||||||
|
<FireworkShow />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<audio src="/call-to-attention-50-percent-volume.mp3" bind:this={audioElement} />
|
<audio src="/call-to-attention-50-percent-volume.mp3" bind:this={audioElement} />
|
||||||
|
<audio src="/fireworks.mp3" bind:this={fireworks} />
|
||||||
|
|
||||||
{#if showTooltip && newChanges.length > 0}
|
{#if showTooltip && newChanges.length > 0}
|
||||||
<div id="tooltip-container" on:click={toggleTooltip}>
|
<div id="tooltip-container" on:click={toggleTooltip}>
|
||||||
<h3>Latest Updates</h3>
|
<h3>Latest Updates</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{#each newChanges as change (change.timestamp)}
|
{#each newChanges as change (change.timestamp)}
|
||||||
<li>{new Date(change.timestamp).toLocaleDateString()}: {change.message}</li>
|
<li>{new Date(change.timestamp).toLocaleDateString()}: {change.message}</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -344,12 +368,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#tooltip-container ul {
|
#tooltip-container ul {
|
||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tooltip-container ul li {
|
#tooltip-container ul li {
|
||||||
text-indent: -10px;
|
text-indent: -10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user