Skip to content

Commit 62dc4d2

Browse files
Dean SoferDean Sofer
authored andcommitted
Switched layout to grid and ai-generated some boilerplate push notification code for firebase
1 parent ab1c270 commit 62dc4d2

File tree

10 files changed

+268
-43
lines changed

10 files changed

+268
-43
lines changed

functions/index.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// functions/src/index.ts (NO CHANGE - Admin SDK syntax is standard)
2+
3+
import * as functions from 'firebase-functions';
4+
import * as admin from 'firebase-admin';
5+
6+
admin.initializeApp();
7+
const db = admin.database();
8+
9+
// Listen for new moves in games
10+
// Assuming moves are stored like /games/{gameId}/moves/{moveId}
11+
// Or simpler for this example, let's say the 'lastMove' is updated at /games/{gameId}/lastMove
12+
export const sendMoveNotification = functions.database.ref('/games/{gameId}/lastMove')
13+
.onUpdate(async (change, context) => {
14+
const afterData = change.after.val();
15+
const gameId = context.params.gameId;
16+
17+
console.log(`Move updated in game ${gameId}:`, afterData);
18+
19+
// --- Your game logic here ---
20+
const nextPlayerId = afterData.nextPlayerId;
21+
const moveMessage = afterData.message || "A move was made!";
22+
const currentPlayerId = afterData.currentPlayerId;
23+
24+
if (!nextPlayerId || nextPlayerId === currentPlayerId) {
25+
console.log('No next player specified or same player, skipping notification.');
26+
return null;
27+
}
28+
29+
const recipientTokenSnapshot = await db.ref(`/users/${nextPlayerId}/fcmToken`).once('value');
30+
const recipientToken = recipientTokenSnapshot.val();
31+
32+
if (!recipientToken) {
33+
console.log(`No FCM token found for user ${nextPlayerId}.`);
34+
return null;
35+
}
36+
37+
// --- Construct and Send the FCM Message ---
38+
const payload = {
39+
notification: {
40+
title: `It's your turn in Game ${gameId}!`,
41+
body: moveMessage,
42+
},
43+
data: {
44+
gameId: gameId,
45+
type: 'new_move',
46+
}
47+
};
48+
49+
try {
50+
const response = await admin.messaging().sendToDevice(recipientToken, payload);
51+
console.log('Successfully sent message:', response);
52+
53+
if (response.results && response.results[0] && response.results[0].error) {
54+
console.error('Failure sending notification to', recipientToken, ':', response.results[0].error);
55+
}
56+
57+
return null;
58+
59+
} catch (error) {
60+
console.error('Error sending message:', error);
61+
return null;
62+
}
63+
});

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
"date-fns": "^4.1.0",
1818
"firebase": "^10.12.2",
1919
"firebaseui": "^6.1.0",
20-
"react": "^17.0.0 || ^18.0.0",
21-
"react-dom": "^17.0.0 || ^18.0.0",
20+
"react": "^19.0.0",
21+
"react-dom": "^19.0.0",
2222
"react-firebaseui": "^6.0.0",
2323
"styled-components": "^6.1.10"
2424
},

public/firebase-messaging-sw.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// TODO: Identify how to create service worker with VITE properly
2+
// --- IMPORTANT: Service Worker for Background Messages ---
3+
// Create a `firebase-messaging-sw.js` file in your public directory.
4+
// This file handles messages when your web app is not in the foreground.
5+
// It needs to import and initialize Firebase Messaging itself, using compat!
6+
7+
// public/firebase-messaging-sw.js (using compat)
8+
importScripts('https://www.gstatic.com/firebasejs/10.12.4/firebase-app-compat.js');
9+
importScripts('https://www.gstatic.com/firebasejs/10.12.4/firebase-messaging-compat.js');
10+
11+
// Your Firebase configuration
12+
const firebaseConfig = {
13+
// ... your config (should be same as client config)
14+
};
15+
16+
// Initialize Firebase using the compat API
17+
const firebaseApp = firebase.initializeApp(firebaseConfig); // Use firebase.initializeApp
18+
19+
// Retrieve firebase messaging instance using the compat API
20+
const messaging = firebaseApp.messaging(); // Use firebaseApp.messaging()
21+
22+
// Handle incoming messages in the background (using compat messaging)
23+
messaging.onBackgroundMessage((payload) => { // Use messaging.onBackgroundMessage
24+
console.log('Received background message ', payload);
25+
26+
const notificationTitle = payload.notification.title;
27+
const notificationOptions = {
28+
body: payload.notification.body,
29+
icon: '/firebase-logo.png' // Or your app icon
30+
};
31+
32+
self.registration.showNotification(notificationTitle, notificationOptions);
33+
});

src/Board/Board.css

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,22 @@
44
--primary-hover: #2727ff;
55
--secondary-hover: rgb(128, 128, 211);
66
user-select: none;
7-
display: flex;
8-
gap: 0;
7+
display: grid;
8+
grid-template-columns: repeat(15, 1fr);
9+
grid-template-rows: repeat(2, 50%);
910
height: 100%;
11+
width: 100%;
1012
flex-wrap: wrap;
1113
touch-action: none;
1214
.selected {
1315
--primary: red;
1416
--secondary: red;
1517
}
1618
> * {
17-
display: flex;
19+
height: 100%;
20+
width: 100%;
1821
align-items: center;
19-
flex-basis: calc(100% / 15);
20-
21-
@media (orientation: landscape) { /* landscape layout (tablet / computer) */
22-
max-width: calc(100% / 15);
23-
}
24-
25-
@media (orientation: portrait) { /* portrait layout (mobile) */
26-
max-height: calc(100% / 15);
27-
}
22+
display: flex;
2823
}
2924
}
3025

@@ -34,13 +29,21 @@
3429

3530
.bar {
3631
background: lightblue;
32+
justify-content: center;
33+
flex-direction: column;
3734
}
3835

3936
.home {
4037
background: lightblue;
38+
flex-direction: column;
39+
.piece {
40+
min-width: 0;
41+
min-height: 0;
42+
}
4143
}
4244

4345
.point {
46+
--point-piece-size: calc(100%/5.5);
4447
position: relative;
4548

4649
&::before {
@@ -61,7 +64,6 @@ order:
6164

6265
:nth-child(1 of .bar) {
6366
order: 3;
64-
justify-content: flex-end;
6567
}
6668
:nth-child(2 of .bar) {
6769
order: 8;
@@ -90,8 +92,10 @@ order:
9092
.home {
9193
height: 50%;
9294
}
95+
.piece {
96+
height: var(--point-piece-size);
97+
}
9398
.point {
94-
height: 50%;
9599
&:hover::before {
96100
/* border-top-color: var(--primary-hover) !important; */
97101
}
@@ -169,18 +173,22 @@ order:
169173
#board {
170174
flex-direction: column;
171175
flex-wrap: wrap-reverse;
176+
writing-mode: sideways-rl;
172177
}
173178
#toolbar {
174179
order: 6;
180+
writing-mode: horizontal-tb;
175181
}
176182
.dice {
177183
order: 1;
178184
}
179185
.home {
180186
width: 50%;
181187
}
188+
.piece {
189+
width: var(--point-piece-size);
190+
}
182191
.point {
183-
width: 50%;
184192

185193
&:nth-child(even):hover::before {
186194
/* border-right-color: var(--primary-hover) !important; */
@@ -193,10 +201,10 @@ order:
193201
top: 50%;
194202
transform: translateY(-50%);
195203
border-top: 3vmax solid transparent;
196-
border-bottom: 3vmax solid transparent;
204+
border-bottom: 3vmax solid transparent;
197205
}
198206
:nth-child(-n+12 of &) {/* top */
199-
flex-direction: row-reverse;
207+
flex-direction: column;
200208

201209
&::before {
202210
border-right: 40vmin solid var(--primary);
@@ -223,6 +231,7 @@ order:
223231
}
224232

225233
:nth-child(n+13 of &) {/* bottom */
234+
flex-direction: column-reverse;
226235
&::before {
227236
border-right: none;
228237
border-left: 40vmin solid var(--primary);

src/Board/Dice.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
order: 6;
33
cursor: pointer;
44
justify-content: center;
5+
flex-direction: column;
6+
57
img {
68
aspect-ratio: 1;
79
max-width: 100%;

src/Board/Piece.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
background: black;
99
} */
1010
aspect-ratio: 1;
11-
flex: 0 0 calc(100% / 6);
1211

1312
&[draggable="true"] {
1413
cursor: pointer;

src/Toolbar.css

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
#toolbar {
2-
gap: 5px;
3-
> a {
4-
font-weight: bold;
5-
font-size: 3em;
6-
}
7-
.avatar {
8-
width: 4em;
9-
}
2+
gap: 5px;
3+
4+
> a {
5+
font-weight: bold;
6+
font-size: 3em;
7+
}
8+
.avatar {
9+
width: 4em;
10+
}
1011

1112
@media (orientation: portrait) {
12-
max-width: 50%;
13-
h2 {
14-
text-overflow: ellipsis;
15-
overflow: hidden;
16-
}
13+
h2 {
14+
text-overflow: ellipsis;
15+
overflow: hidden;
16+
}
1717
}
1818

1919
@media (orientation: landscape) {
20-
max-height: 50%;
21-
h2 {
22-
text-overflow: ellipsis;
23-
overflow: hidden;
24-
writing-mode: vertical-rl;
25-
text-orientation: mixed;
26-
}
20+
flex-direction: column;
21+
22+
h2 {
23+
text-overflow: ellipsis;
24+
overflow: hidden;
25+
writing-mode: vertical-rl;
26+
text-orientation: mixed;
27+
}
2728
}
2829
}

src/firebase-messaging-setup.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// client/src/firebase-messaging-setup.ts (using compat)
2+
3+
// Import the compat versions of the SDKs you need
4+
import firebase from 'firebase/compat/app';
5+
import 'firebase/compat/messaging';
6+
import 'firebase/compat/database';
7+
import 'firebase/compat/auth';
8+
9+
// Your Firebase configuration (get this from your Firebase project settings)
10+
const firebaseConfig = {
11+
// ... your config
12+
};
13+
14+
// Initialize Firebase (using the compat app import)
15+
const firebaseApp = firebase.initializeApp(firebaseConfig);
16+
// Access services directly from the app instance or the global firebase object
17+
const messaging = firebaseApp.messaging(); // Or firebase.messaging() if using script tags
18+
const db = firebaseApp.database(); // Or firebase.database()
19+
const auth = firebaseApp.auth(); // Or firebase.auth()
20+
21+
22+
// Get the FCM registration token and save it to the database
23+
async function saveMessagingDeviceToken() {
24+
const userId = auth.currentUser?.uid;
25+
if (!userId) {
26+
console.log('User not logged in, cannot save token.');
27+
return;
28+
}
29+
30+
try {
31+
// Request permission (using the compat messaging instance)
32+
const permission = await Notification.requestPermission();
33+
if (permission === 'granted') {
34+
console.log('Notification permission granted.');
35+
36+
// Get token (using the compat messaging instance)
37+
// VAPID key is required for web push (generate in Firebase Console -> Project settings -> Cloud Messaging)
38+
// Note: getToken is part of the v9 web API, which compat wraps
39+
const currentToken = await messaging.getToken({ vapidKey: 'YOUR_VAPID_KEY_HERE' });
40+
41+
if (currentToken) {
42+
console.log('FCM registration token:', currentToken);
43+
// Save the token to your database (using compat database syntax)
44+
await db.ref(`/users/${userId}/fcmToken`).set(currentToken);
45+
console.log('FCM token saved to database.');
46+
} else {
47+
console.log('No registration token available. Request permission to generate one.');
48+
}
49+
} else {
50+
console.log('Unable to get notification permission.');
51+
}
52+
} catch (error) {
53+
console.error('An error occurred while retrieving token or setting it up.', error);
54+
}
55+
}
56+
57+
// Listen for incoming messages while the app is in the foreground (using compat messaging)
58+
// Note: onMessage is part of the v9 web API, which compat wraps
59+
messaging.onMessage((payload) => {
60+
console.log('Message received in foreground.', payload);
61+
// Display the notification to the user in the app UI
62+
if (payload.notification) {
63+
alert(`New move in game: ${payload.notification.body}`); // Simple example
64+
}
65+
});
66+
67+
// see public/firebase-messaging-sw.js
68+
69+
// Call this function when the user logs in or perhaps when the app loads if they are already logged in
70+
auth.onAuthStateChanged((user) => { // Using compat auth instance
71+
if (user) {
72+
saveMessagingDeviceToken();
73+
} else {
74+
// User is signed out
75+
console.log('User signed out, token not needed or remove token from DB');
76+
// Optional: remove the token from the database if the user signs out
77+
}
78+
});

src/index.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@ body,
2222
}
2323

2424
#root {
25-
padding:23px;
25+
/* padding:23px; */
2626
box-sizing: border-box;
27-
box-shadow:
27+
/* box-shadow:
2828
inset #19d4ff 0 0 0 5px,
2929
inset #18cdf7 0 0 0 1px,
3030
inset #53dfff 0 0 0 10px,
3131
inset #50d8f7 0 0 0 11px,
3232
inset #8ce9ff 0 0 0 16px,
3333
inset #88e2f7 0 0 0 17px,
3434
inset #c5f4ff 0 0 0 22px,
35-
inset #bfecf7 0 0 0 23px;
35+
inset #bfecf7 0 0 0 23px; */
3636
}
3737

3838
[onPointerUp], a {

0 commit comments

Comments
 (0)