Skip to content

Commit

Permalink
Final changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Darguima committed Jan 21, 2025
1 parent 09fe583 commit cabdda0
Show file tree
Hide file tree
Showing 90 changed files with 5,038 additions and 523 deletions.
3 changes: 2 additions & 1 deletion assets/css/components.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import "components/avatar.css";
@import "components/field.css";
@import "components/dropdown.css";
@import "components/dropdown.css";
@import "components/coinflip.css";
105 changes: 105 additions & 0 deletions assets/css/components/coinflip.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
.coin {
position: relative;
margin: 0 auto;
width: 80px;
height: 80px;
perspective: 1000px; /* Add perspective to the parent element */
}

.coin div {
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: inset 0 0 45px rgba(255, 255, 255, 0.3), 0 12px 20px -10px rgba(0, 0, 0, 0.4);
background-size: contain;
position: absolute;
backface-visibility: hidden; /* Ensure backface is hidden */
}

.side-a {
background-image: url('/images/coin-void.svg');
z-index: 100;
}

.side-b {
background-image: url('/images/coin-dollar-sign.svg');
transform: rotateY(180deg); /* Rotate the back side */
}

.side-b-not-rotated {
background-image: url('/images/coin-dollar-sign.svg');
}

.coin {
transform-style: preserve-3d; /* Ensure 3D transformation */
transition: transform 1s ease-in;
}

.coin.heads {
animation: flipHeads 3s ease-out forwards;
}

.coin.tails {
animation: flipTails 3s ease-out forwards;
}

@keyframes flipHeads {
0% {
transform: rotateY(0);
scale: 1;
}
50% {
transform: rotateY(900deg); /* 2.5 full rotations */
scale: 1.2;
}
100% {
transform: rotateY(1800deg); /* 5 full rotations */
scale: 1;
}
}

@keyframes flipTails {
0% {
transform: rotateY(0);
scale: 1;
}
50% {
transform: rotateY(900deg); /* 2.5 full rotations */
scale: 1.2;
}
100% {
transform: rotateY(1980deg); /* 5.5 full rotations */
scale: 1;
}
}

@keyframes countdownAnimation {
0% {
transform: scale(1);
opacity: 1;
}
90% {
transform: scale(1.125);
opacity: 1;
}
100% {
transform: scale(1);
opacity: 1;
}
}

.countdown-animation {
animation: countdownAnimation infinite 1s ease-in-out;
}

.gray-overlay::after {
content: '';
position: absolute;
@apply rounded-md;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.3); /* Gray color with 50% opacity */
z-index: 1;
}
10 changes: 10 additions & 0 deletions assets/css/components/field.css
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
@apply block w-full border-gray-300 rounded-md shadow-sm focus:border-secondary-500 focus:ring-secondary-500 dark:border-gray-600 dark:focus:border-secondary-500 sm:text-sm disabled:cursor-not-allowed dark:text-gray-300 focus:outline-none;
}

/* Number inputs with no background */
.safira-number-input-without-bg {
border:none;
background-image:none;
background-color:transparent;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}

/* Switch */

.safira-switch {
Expand Down
5 changes: 4 additions & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import live_select from "live_select"
import { QrScanner, Wheel, Confetti, Countdown, Sorting } from "./hooks";
import { QrScanner, Wheel, Confetti, Countdown, Sorting, CoinFlip, Redirect, CredentialScene } from "./hooks";

let Hooks = {
QrScanner: QrScanner,
Wheel: Wheel,
Confetti: Confetti,
Countdown: Countdown,
Sorting: Sorting,
CoinFlip: CoinFlip,
Redirect: Redirect,
CredentialScene: CredentialScene,
...live_select
};

Expand Down
146 changes: 146 additions & 0 deletions assets/js/hooks/coinflip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
const ANIMATION_DELAYS = {
RESULT_DISPLAY: 500,
ANIMATION_DONE: 1000,
COUNTDOWN: 1000
};

const GAME_STATES = {
HEADS: 'heads',
TAILS: 'tails',
FINISHED: 'true'
};

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export const CoinFlip = {
mounted() {
this.initializeFromDOM();
this.setupEventListeners();
},

updated() {
this.initializeFromDOM();
},

initializeFromDOM() {
const data = this.getGameData();
this.initializeGame(data);
},

getGameData() {
const { dataset } = this.el;
return {
roomId: dataset.roomId,
streamId: dataset.streamId,
result: dataset.result,
finished: dataset.finished,
player1Id: dataset.player1Id,
player2Id: dataset.player2Id,
fee: dataset.fee
};
},

setupEventListeners() {
const { streamId, roomId } = this.getGameData();
const coin = document.getElementById(`${streamId}-coin`);

coin.addEventListener("animationend", async () => {
const { player1Id, player2Id, fee, result } = this.getGameData();

await delay(ANIMATION_DELAYS.RESULT_DISPLAY);
this.updateBetDisplay(streamId, player1Id, player2Id, fee, result);

await delay(ANIMATION_DELAYS.ANIMATION_DONE);
this.pushEvent("animation-done", { room_id: roomId });
});
},

initializeGame({ player1Id, player2Id, streamId, fee, result, finished }) {
const elements = this.getGameElements(streamId);
this.hideInitialElements(elements);

if (finished === GAME_STATES.FINISHED) {
this.handleFinishedGame(elements, result);
this.updateBetDisplay(streamId, player1Id, player2Id, fee, result);
return;
}

if (finished === 'false' && (result === GAME_STATES.HEADS || result === GAME_STATES.TAILS)) {
this.startCountdown(elements, result);
}
},

getGameElements(streamId) {
return {
coin: document.getElementById(`${streamId}-coin`),
counter: document.getElementById(`${streamId}-countdown`),
vsText: document.getElementById(`${streamId}-vs-text`)
};
},

hideInitialElements({ coin, counter, vsText }) {
coin.style.display = "none";
counter.style.display = "none";
},

async startCountdown({ vsText, counter, coin }, result) {
vsText.style.display = "none";
counter.style.display = "flex";

for (let i = 3; i > 0; i--) {
counter.textContent = i.toString();
counter.classList.add("countdown-animation");
await delay(ANIMATION_DELAYS.COUNTDOWN);
counter.classList.remove("countdown-animation");
}

counter.style.display = "none";
coin.style.display = "block";
coin.classList.add(result);
},

handleFinishedGame({ vsText, counter, coin }, result) {
vsText.style.display = "none";
counter.style.display = "none";
coin.style.display = "block";

if (result === GAME_STATES.HEADS) {
coin.children[1].hidden = true;
} else {
coin.children[0].hidden = true;
coin.children[1].style.transform = "rotateY(0deg)";
}
},

updateBetDisplay(streamId, player1Id, player2Id, fee, result) {
const elements = {
player1: {
card: document.getElementById(`${streamId}-${player1Id}-card`),
bet: document.getElementById(`${streamId}-${player1Id}-bet`)
},
player2: {
card: document.getElementById(`${streamId}-${player2Id}-card`),
bet: document.getElementById(`${streamId}-${player2Id}-bet`)
}
};

if (!elements.player1.bet || !elements.player2.bet) {
console.error("Bet elements not found");
return;
}

const calculateWinnings = (bet) => Math.floor(bet * 2 * (1 - fee));

if (result === GAME_STATES.HEADS) {
elements.player2.card.classList.add("gray-overlay");
elements.player1.bet.textContent = calculateWinnings(elements.player1.bet.dataset.bet);
elements.player2.bet.textContent = `-${elements.player2.bet.dataset.bet}`;
elements.player2.bet.classList.add("text-red-500");
} else {
elements.player1.card.classList.add("gray-overlay");
elements.player1.bet.textContent = `-${elements.player1.bet.dataset.bet}`;
elements.player1.bet.classList.add("text-red-500");
elements.player2.bet.textContent = calculateWinnings(elements.player2.bet.dataset.bet);
}
}
};
7 changes: 7 additions & 0 deletions assets/js/hooks/credential-scene.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import "../../vendor/credential-scene";

export const CredentialScene = {
mounted() {
window.initializeScene(this.el, this.el.dataset.attendee_name);
}
};
3 changes: 3 additions & 0 deletions assets/js/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ export { Wheel } from "./wheel.js";
export { Confetti } from "./confetti.js";
export { Sorting } from "./sorting.js";
export { Countdown } from "./countdown.js";
export { CoinFlip } from "./coinflip.js";
export { Redirect } from "./redirect.js";
export { CredentialScene } from "./credential-scene.js";
10 changes: 10 additions & 0 deletions assets/js/hooks/redirect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const Redirect = {
mounted() {
this.handleEvent("redirect", data => {
console.log(data)
setTimeout(() => {
window.location.href = data.url;
}, data.time);
});
}
}
1 change: 1 addition & 0 deletions assets/vendor/credential-scene.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ config :safira, SafiraWeb.Endpoint,
adapter: Bandit.PhoenixAdapter,
render_errors: [
formats: [html: SafiraWeb.ErrorHTML, json: SafiraWeb.ErrorJSON],
layout: {SafiraWeb.Layouts, :landing}
layout: {SafiraWeb.Layouts, :root}
],
pubsub_server: Safira.PubSub,
live_view: [signing_salt: "TzWGKiXG"]
Expand Down
3 changes: 2 additions & 1 deletion config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ config :safira, SafiraWeb.Endpoint,
patterns: [
~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$",
~r"priv/gettext/.*(po)$",
~r"lib/safira_web/(controllers|live|components)/.*(ex|heex)$"
~r"lib/safira_web/(controllers|live|components)/.*(ex|heex)$",
~r"lib/safira_web/.*(ex|heex)$"
]
]

Expand Down
Loading

0 comments on commit cabdda0

Please sign in to comment.