Compare commits
26 Commits
564f2d47b4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| bbb9d37ec7 | |||
| bcaaae90e9 | |||
| b4a037388a | |||
| c710845288 | |||
| 946548d001 | |||
| 7ed8895038 | |||
| 51697622a7 | |||
| 6b6da07ddf | |||
| 2403734bf3 | |||
| 9184d759f3 | |||
| 9c94934ff3 | |||
| e72e2e6800 | |||
| 65e81a7e6b | |||
| c23cb63817 | |||
| 5bdf1ed940 | |||
| 2a23f1dedc | |||
| da10b19960 | |||
| 5b0ef233c4 | |||
| 1d50bc3839 | |||
| 479dbe5c2d | |||
| 4e324502ae | |||
| d6cfdf66c6 | |||
| a64d9fea31 | |||
| b04a4732cf | |||
| bd71bce450 | |||
| cfb6b2609b |
@@ -8,3 +8,6 @@ wallpapers/
|
||||
frontend/node_modules/
|
||||
frontend/.next/
|
||||
frontend/src/data/types.ts
|
||||
backend/session_*.log
|
||||
backend/*.csv
|
||||
*.bak
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
-- sqlite3 -csv db.sqlite3 '.read export.sql' > export.csv
|
||||
SELECT
|
||||
CONCAT('https://open.spotify.com/track/', T.id) as spotify_id,
|
||||
T.name as track_name,
|
||||
A.name as artist_name,
|
||||
(CAST(T.duration_ms AS REAL) / 86400000) as duration,
|
||||
V.point_total,
|
||||
V.vote_count,
|
||||
CAST(V.point_total AS REAL) / CAST(V.vote_count AS REAL) as average_points,
|
||||
U.username as nominated_by
|
||||
FROM
|
||||
(
|
||||
SELECT track_id, SUM(points) as point_total, COUNT(*) as vote_count
|
||||
FROM playlist_vote
|
||||
GROUP BY track_id
|
||||
) AS V
|
||||
INNER JOIN playlist_track T ON V.track_id = T.id
|
||||
INNER JOIN (
|
||||
SELECT track_id, MIN(artist_id) as artist_id FROM playlist_track_artists GROUP BY track_id
|
||||
) PTA ON T.id = PTA.track_id
|
||||
INNER JOIN playlist_artist A ON PTA.artist_id = A.id
|
||||
INNER JOIN auth_user U ON T.nominated_by_id = U.id
|
||||
ORDER BY average_points DESC, vote_count DESC;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -73,8 +73,10 @@ class SessionConsumer(WebsocketConsumer):
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.session_name, {"type": "voted", "user": { "id": user.id, "username": user.username}, "track": track.id, "points": points}
|
||||
)
|
||||
if action == "pause":
|
||||
pass
|
||||
if action == "start":
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.session_name, {"type": "announce.start"}
|
||||
)
|
||||
if action == "next":
|
||||
random.seed(session.seed)
|
||||
tracks = Track.objects.order_by("?")
|
||||
@@ -83,23 +85,37 @@ class SessionConsumer(WebsocketConsumer):
|
||||
for track in tracks:
|
||||
if next:
|
||||
session.current_track = track
|
||||
next = False
|
||||
break
|
||||
if session.current_track == track:
|
||||
next = True
|
||||
if next:
|
||||
# We didnt get a next track, so it must have been the last one
|
||||
session.current_track = None
|
||||
else:
|
||||
session.current_track = tracks.first()
|
||||
session.save()
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.session_name, {"type": "announce.track"}
|
||||
)
|
||||
if session.current_track is None:
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.session_name, {"type": "announce.done"}
|
||||
)
|
||||
else:
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
self.session_name, {"type": "announce.track"}
|
||||
)
|
||||
|
||||
|
||||
def announce(self, event):
|
||||
user = event["user"]
|
||||
self.send(text_data=json.dumps({"joined": user}))
|
||||
|
||||
# TODO: maybe this could be one function.
|
||||
def announce_track(self, event):
|
||||
self.send(text_data=json.dumps({"track": {}}))
|
||||
def announce_done(self, event):
|
||||
self.send(text_data=json.dumps({"done": {}}))
|
||||
def announce_start(self, event):
|
||||
self.send(text_data=json.dumps({"start": {}}))
|
||||
|
||||
def voted(self, event):
|
||||
user = event["user"]
|
||||
|
||||
@@ -7,4 +7,5 @@ urlpatterns = [
|
||||
path("session/<str:session_id>/test", views.session, name="session"),
|
||||
path("session/", views.SessionListView.as_view(), name="api-session-list"),
|
||||
path("session/<str:session_id>/", views.SessionDetailView.as_view(), name="api-session-detail"),
|
||||
path("session/<str:session_id>/info", views.SessionInfoView.as_view(), name="api-session-info"),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from django.shortcuts import render
|
||||
from django.db.models import Avg
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from playlist.models import Session, Profile, Track, Vote
|
||||
@@ -31,3 +32,11 @@ class SessionDetailView(APIView):
|
||||
def get(self, request, session_id):
|
||||
session = Session.objects.get(session_id=session_id)
|
||||
return Response(SessionSerializer(session).data)
|
||||
|
||||
|
||||
class SessionInfoView(APIView):
|
||||
def get(self, request, session_id):
|
||||
track_count = len(Track.objects.all())
|
||||
vote_count = len(Vote.objects.all())
|
||||
average_vote = Vote.objects.all().aggregate(Avg("points", default=0))['points__avg']
|
||||
return Response({'track_count': track_count, 'vote_count': vote_count, 'average_vote': average_vote})
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 787 KiB |
@@ -23,26 +23,22 @@ const pageConfig: Record<
|
||||
string,
|
||||
{
|
||||
showAuthStatus: boolean;
|
||||
showSessionChecker: boolean;
|
||||
showNav: boolean;
|
||||
showQuota: boolean;
|
||||
}
|
||||
> = {
|
||||
"/nominations": {
|
||||
showAuthStatus: true,
|
||||
showSessionChecker: false,
|
||||
showQuota: true,
|
||||
showNav: navToggle,
|
||||
},
|
||||
"/mobile-voting": {
|
||||
showAuthStatus: false,
|
||||
showSessionChecker: false,
|
||||
showQuota: false,
|
||||
showNav: false,
|
||||
},
|
||||
"/lobby": {
|
||||
showAuthStatus: true,
|
||||
showSessionChecker: false,
|
||||
showQuota: true,
|
||||
showNav: navToggle,
|
||||
},
|
||||
@@ -60,7 +56,7 @@ function LayoutContent({ children }: { children: React.ReactNode }) {
|
||||
|
||||
return (
|
||||
<>
|
||||
{config.showSessionChecker && <SessionChecker />}
|
||||
<SessionChecker />
|
||||
{config.showAuthStatus && <AuthStatus />}
|
||||
{children}
|
||||
{config.showNav && <NavigationBar />}
|
||||
|
||||
+193
-58
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Markazi_Text, Nunito } from "next/font/google";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useAuth } from "react-oidc-context";
|
||||
@@ -8,6 +9,7 @@ import { useSession } from "muzak/contexts/SessionContext";
|
||||
import { getUser, api, Track } from "muzak/data/fetcher";
|
||||
import getSessionId from "muzak/data/session";
|
||||
import { useSpotifyIframe } from "muzak/contexts/SpotifyIframe";
|
||||
import { useAnimation } from "muzak/contexts/AnimationContext";
|
||||
|
||||
const nunito = Nunito({
|
||||
weight: "900",
|
||||
@@ -28,7 +30,13 @@ export default function Lobby() {
|
||||
const [spotifyId, setSpotifyId] = useState<string | null>(null);
|
||||
const auth = useAuth();
|
||||
const user = getUser();
|
||||
const router = useRouter();
|
||||
const controller = useSpotifyIframe();
|
||||
const [revealed, setRevealed] = useState(false);
|
||||
const [playingPercentage, setPlayingPercentage] = useState(0);
|
||||
const [secondsLeft, setSecondsLeft] = useState(30);
|
||||
const [state, setState] = useState("lobby"); // lobby, playing, waiting, loading
|
||||
const { isAnimate, setIsAnimate } = useAnimation();
|
||||
|
||||
// Main message handler and dispatcher
|
||||
function onMessage(e) {
|
||||
@@ -38,9 +46,13 @@ export default function Lobby() {
|
||||
if (key === "joined") {
|
||||
addUser(data[key]);
|
||||
} else if (key == "track") {
|
||||
setIsAnimate(false);
|
||||
setTimeout(() => setIsAnimate(true), 1000);
|
||||
getTrack();
|
||||
} else if (key == "voted") {
|
||||
voteUser(data[key]);
|
||||
} else if (key == "done") {
|
||||
router.push("/post-game");
|
||||
}
|
||||
}
|
||||
console.log(data);
|
||||
@@ -77,14 +89,21 @@ export default function Lobby() {
|
||||
|
||||
function startSession() {
|
||||
setSessionState("voting");
|
||||
setState("loading");
|
||||
getTrack();
|
||||
sessionSocket.send(
|
||||
JSON.stringify({
|
||||
action: "start",
|
||||
token: user.access_token,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function addUser(user: object) {
|
||||
const newUser = {
|
||||
id: user["id"],
|
||||
name: user["username"],
|
||||
avatar: `https://i.pravatar.cc/150?img=${user["id"]}`,
|
||||
avatar: `https://api.dicebear.com/9.x/bottts-neutral/svg?seed=${user["id"]}`,
|
||||
hidden: false,
|
||||
};
|
||||
|
||||
@@ -116,7 +135,7 @@ export default function Lobby() {
|
||||
{
|
||||
id: userId,
|
||||
name: userName,
|
||||
avatar: `https://i.pravatar.cc/150?img=${userId}`,
|
||||
avatar: `https://api.dicebear.com/9.x/bottts-neutral/svg?seed=${userId}`,
|
||||
},
|
||||
];
|
||||
});
|
||||
@@ -133,7 +152,7 @@ export default function Lobby() {
|
||||
{
|
||||
id: userId,
|
||||
name: userName,
|
||||
avatar: `https://i.pravatar.cc/150?img=${userId}`,
|
||||
avatar: `https://api.dicebear.com/9.x/bottts-neutral/svg?seed=${userId}`,
|
||||
},
|
||||
];
|
||||
});
|
||||
@@ -151,6 +170,7 @@ export default function Lobby() {
|
||||
|
||||
async function getTrack() {
|
||||
setSongLoading(true);
|
||||
setState("loading");
|
||||
setUserList((prevUserList) => {
|
||||
return prevUserList.map((user) => {
|
||||
return { ...user, hidden: false };
|
||||
@@ -173,6 +193,9 @@ export default function Lobby() {
|
||||
}
|
||||
|
||||
async function nextTrack() {
|
||||
console.log("nextTrack");
|
||||
setState("loading");
|
||||
setRevealed(false);
|
||||
setSongLoading(true);
|
||||
sessionSocket.send(
|
||||
JSON.stringify({
|
||||
@@ -182,19 +205,64 @@ export default function Lobby() {
|
||||
);
|
||||
}
|
||||
|
||||
async function endPlay() {
|
||||
controller?.seek(100);
|
||||
}
|
||||
|
||||
// Spotify Player stuff
|
||||
useEffect(() => {
|
||||
if (controller) {
|
||||
console.log("Loaded track:", spotifyId);
|
||||
controller.loadUri(`spotify:track:${spotifyId}`);
|
||||
controller.play();
|
||||
// Note: do not set playing state here, but later when its actually playing.
|
||||
console.log(controller._listeners);
|
||||
if (controller._listeners["playback_update"].length === 0) {
|
||||
controller.addListener("playback_update", (e) => {
|
||||
updatePlayback(e);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log("Spotify player not initialized");
|
||||
}
|
||||
}, [controller, spotifyId]);
|
||||
function updatePlayback(e) {
|
||||
let duration = e.data.duration;
|
||||
let playingPosition = e.data.position;
|
||||
const playingSeconds = Math.floor((duration - playingPosition) / 1000);
|
||||
setPlayingPercentage((playingPosition / duration) * 100);
|
||||
setSecondsLeft((s) => {
|
||||
setState((state) => {
|
||||
if (playingSeconds >= 1) {
|
||||
return "playing";
|
||||
}
|
||||
if (state == "playing" && s <= 0) {
|
||||
console.log("track ended");
|
||||
return "waiting";
|
||||
}
|
||||
return state;
|
||||
});
|
||||
return playingSeconds;
|
||||
});
|
||||
}
|
||||
async function playTrack() {
|
||||
controller.play();
|
||||
}
|
||||
async function pauseTrack() {
|
||||
controller.togglePlay();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("useEffect state:", state);
|
||||
if (state == "waiting") {
|
||||
setRevealed(true);
|
||||
setTimeout(() => {
|
||||
setState("next");
|
||||
}, 12000);
|
||||
} else if (state == "next") {
|
||||
nextTrack();
|
||||
}
|
||||
}, [state]);
|
||||
|
||||
if (sessionState == "lobby") {
|
||||
return (
|
||||
@@ -217,7 +285,8 @@ export default function Lobby() {
|
||||
</div>
|
||||
<button
|
||||
onClick={startSession}
|
||||
className="rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
className="rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400 disabled:bg-yellow-900 disabled:text-gray-400"
|
||||
disabled={sessionId === null}
|
||||
>
|
||||
START
|
||||
</button>
|
||||
@@ -265,14 +334,18 @@ export default function Lobby() {
|
||||
<main className="flex min-h-screen flex-col items-center justify-between gap-4 p-24">
|
||||
<div className="flex w-full items-start justify-between text-center">
|
||||
<div className="flex-1"></div>
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
<div className="flex h-[170px] w-[170px] items-center justify-center rounded-full border-2 bg-orange-400">
|
||||
<div className="text-5xl text-white drop-shadow-sm drop-shadow-black">
|
||||
<button onClick={getTrack}>30</button>
|
||||
<div className="flex-1"></div>
|
||||
<div className="flex min-h-[170px] flex-1 items-center justify-center">
|
||||
{(state == "playing" || state == "waiting") && (
|
||||
<div
|
||||
className={`flex h-[170px] w-[170px] items-center justify-center rounded-full border-2 ${state == "playing" ? "bg-orange-400" : ""} ${state == "waiting" ? "bg-green-400" : ""}`}
|
||||
>
|
||||
<div className="text-5xl text-white drop-shadow-sm drop-shadow-black">
|
||||
{secondsLeft}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-row items-center justify-end gap-4">
|
||||
<button
|
||||
className="w-30 rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
@@ -280,15 +353,15 @@ export default function Lobby() {
|
||||
>
|
||||
PLAY
|
||||
</button>
|
||||
<Link
|
||||
href="/pause"
|
||||
<button
|
||||
onClick={pauseTrack}
|
||||
className="w-30 rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
>
|
||||
PAUSE
|
||||
</Link>
|
||||
</button>
|
||||
<button
|
||||
className="w-30 rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
onClick={nextTrack}
|
||||
onClick={endPlay}
|
||||
>
|
||||
NEXT
|
||||
</button>
|
||||
@@ -296,64 +369,111 @@ export default function Lobby() {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="flex flex-row items-center justify-center gap-x-32">
|
||||
<div className="grid min-w-52 grid-flow-row grid-cols-3 items-start justify-start gap-4 rounded bg-blue-950/80 p-4">
|
||||
{noList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[120px] w-[120px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
width={75}
|
||||
height={75}
|
||||
/>
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
<div className="flex flex-row items-center justify-center gap-x-36">
|
||||
<div className="relative w-fit overflow-hidden">
|
||||
<div className="grid min-h-[516px] min-w-[516px] grid-flow-row grid-cols-3 place-items-center items-start justify-center gap-4 rounded bg-blue-950/80 px-4 pt-10">
|
||||
{noList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[120px] w-[120px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}{" "}
|
||||
))}{" "}
|
||||
</div>
|
||||
<div
|
||||
className={`absolute inset-0 flex items-center justify-center rounded bg-red-800 text-5xl font-bold text-white shadow-[inset_0_0_20px_rgba(0,0,0,0.3)] transition-transform duration-1000 ease-in-out ${
|
||||
revealed ? "-translate-y-full" : "translate-y-0"
|
||||
}`}
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0) 20%, rgba(0,0,0,0.2) 80%, rgba(0,0,0,0.2)),
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
rgba(255,255,255,0.1),
|
||||
rgba(255,255,255,0.1) 30px,
|
||||
rgba(0,0,0,0) 30px,
|
||||
rgba(0,0,0,0) 60px
|
||||
)`,
|
||||
clipPath:
|
||||
"polygon(0 0, 100% 0, 100% 98%, 95% 100%, 90% 98%, 85% 100%, 80% 98%, 75% 100%, 70% 98%, 65% 100%, 60% 98%, 55% 100%, 50% 98%, 45% 100%, 40% 98%, 35% 100%, 30% 98%, 25% 100%, 20% 98%, 15% 100%, 10% 98%, 5% 100%, 0 98%)",
|
||||
}}
|
||||
>
|
||||
Meh
|
||||
</div>
|
||||
</div>
|
||||
{!songLoading && currentSong ? (
|
||||
<div className="rounded-md bg-black p-2">
|
||||
<Image
|
||||
src={currentSong.album_cover}
|
||||
alt={currentSong.name}
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
<button onClick={nextTrack}>
|
||||
<Image
|
||||
src={currentSong.album_cover}
|
||||
alt={currentSong.name}
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md bg-black p-2">
|
||||
<div className="rounded-md border-8 border-black bg-blue-950/80 py-[1px]">
|
||||
<Image
|
||||
src="/meowl.png"
|
||||
alt="Loading.."
|
||||
src="/cdspin.gif"
|
||||
alt="Loading..."
|
||||
width={500}
|
||||
height={500}
|
||||
className="rounded-md"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid min-w-52 grid-flow-row grid-cols-3 items-start justify-start gap-4 rounded bg-blue-950/80 p-4">
|
||||
{yesList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[120px] w-[120px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
width={75}
|
||||
height={75}
|
||||
/>
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
<div className="relative w-fit overflow-hidden">
|
||||
<div className="grid min-h-[516px] min-w-[516px] grid-flow-row grid-cols-3 place-items-center items-start justify-center gap-4 rounded bg-blue-950/80 px-4 pt-10">
|
||||
{yesList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[120px] w-[120px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img
|
||||
src={user.avatar}
|
||||
alt={user.name}
|
||||
width={100}
|
||||
height={100}
|
||||
/>
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}{" "}
|
||||
))}{" "}
|
||||
</div>
|
||||
<div
|
||||
className={`absolute inset-0 flex items-center justify-center rounded bg-red-800 text-5xl font-bold text-white shadow-[inset_0_0_20px_rgba(0,0,0,0.3)] transition-transform duration-1000 ease-in-out ${
|
||||
revealed ? "-translate-y-full" : "translate-y-0"
|
||||
}`}
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0) 20%, rgba(0,0,0,0.2) 80%, rgba(0,0,0,0.2)),
|
||||
repeating-linear-gradient(
|
||||
90deg,
|
||||
rgba(255,255,255,0.1),
|
||||
rgba(255,255,255,0.1) 30px,
|
||||
rgba(0,0,0,0) 30px,
|
||||
rgba(0,0,0,0) 60px
|
||||
)`,
|
||||
clipPath:
|
||||
"polygon(0 0, 100% 0, 100% 98%, 95% 100%, 90% 98%, 85% 100%, 80% 98%, 75% 100%, 70% 98%, 65% 100%, 60% 98%, 55% 100%, 50% 98%, 45% 100%, 40% 98%, 35% 100%, 30% 98%, 25% 100%, 20% 98%, 15% 100%, 10% 98%, 5% 100%, 0 98%)",
|
||||
}}
|
||||
>
|
||||
Yeah
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!songLoading && currentSong && (
|
||||
{!songLoading && currentSong ? (
|
||||
<div className="flex flex-col items-center justify-start">
|
||||
<h2
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
@@ -366,10 +486,25 @@ export default function Lobby() {
|
||||
{currentSong.name}
|
||||
</h2>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-start">
|
||||
<h2
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
Loading
|
||||
</h2>
|
||||
<h2
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
...
|
||||
</h2>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-start justify-start gap-10 rounded bg-blue-950/80 p-4">
|
||||
<div
|
||||
className={`flex min-h-33 min-w-1 flex-row items-start justify-start gap-10 rounded p-4 ${userList.some((u) => !u.hidden) ? "bg-blue-950/80" : "bg-transparent p-0"} `}
|
||||
>
|
||||
{userList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
|
||||
@@ -43,10 +43,19 @@ export default function MobileVoting() {
|
||||
console.log(sessionSocket?.readyState);
|
||||
await waitForOpenSocket();
|
||||
console.log("socket is open");
|
||||
sessionSocket.onclose = function (e) {
|
||||
console.log("Chat socket closed unexpectedly");
|
||||
setYesHighlight(false);
|
||||
setNoHighlight(false);
|
||||
setVote(0.5);
|
||||
setSessionId(null);
|
||||
setTimeout(() => {
|
||||
setSession();
|
||||
}, 2000);
|
||||
};
|
||||
sessionSocket.send(
|
||||
JSON.stringify({
|
||||
action: "login",
|
||||
userr: user.profile.nickname,
|
||||
token: user.access_token,
|
||||
}),
|
||||
);
|
||||
@@ -57,13 +66,13 @@ export default function MobileVoting() {
|
||||
console.log(e);
|
||||
let data = JSON.parse(e.data);
|
||||
for (let key in data) {
|
||||
if (key == "track") {
|
||||
if (key == "track" || key == "start") {
|
||||
setYesHighlight(false);
|
||||
setNoHighlight(false);
|
||||
setVote(0.5);
|
||||
}
|
||||
}
|
||||
console.log(data);
|
||||
//}, 1000);
|
||||
}
|
||||
|
||||
// Initialize WebSocket connection when sessionId changes
|
||||
@@ -75,7 +84,6 @@ export default function MobileVoting() {
|
||||
);
|
||||
socket.onmessage = onMessage;
|
||||
setSessionSocket(socket);
|
||||
//setTimeout(() => connect(), 2000);
|
||||
}
|
||||
}, [sessionId]);
|
||||
|
||||
@@ -100,7 +108,7 @@ export default function MobileVoting() {
|
||||
}, [auth]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionSocket?.readyState == WebSocket.OPEN) {
|
||||
if (vote != 0.5 && sessionSocket?.readyState == WebSocket.OPEN) {
|
||||
sessionSocket.send(
|
||||
JSON.stringify({
|
||||
action: "vote",
|
||||
@@ -112,7 +120,7 @@ export default function MobileVoting() {
|
||||
}, [vote]);
|
||||
|
||||
return (
|
||||
<main className="m-3 h-screen rounded bg-blue-950/80 p-8 sm:p-14 md:p-20">
|
||||
<main className="m-0 h-screen rounded bg-blue-950/80 p-8 sm:p-14 md:p-20">
|
||||
<div className="flex flex-row items-center justify-between gap-8 rounded-md">
|
||||
<button
|
||||
className={`flex aspect-square w-1/2 max-w-[200px] items-center rounded bg-black/20 p-0 text-center font-bold text-white ${noHighlight ? "ring-5 ring-amber-400" : "ring-0 ring-black/20"}`}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
"use client";
|
||||
import { Nunito } from "next/font/google";
|
||||
import Link from "next/link";
|
||||
|
||||
const nunito = Nunito({
|
||||
weight: "900",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export default function Pause() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
<div>
|
||||
<h1
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
PAUZE
|
||||
</h1>
|
||||
</div>
|
||||
<img
|
||||
src="https://c.tenor.com/uWx6oZf6NnsAAAAC/tenor.gif"
|
||||
className="w-1/3 rounded-full"
|
||||
/>
|
||||
<Link
|
||||
href="/voting"
|
||||
className="rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
>
|
||||
HERVATTEN
|
||||
</Link>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,60 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { api } from "muzak/data/fetcher";
|
||||
import { useSession } from "muzak/contexts/SessionContext";
|
||||
import { Nunito } from "next/font/google";
|
||||
const nunito = Nunito({
|
||||
weight: "900",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export default function PostGame() {
|
||||
const { sessionId, setSessionId } = useSession();
|
||||
const [trackCount, setTrackCount] = useState(0);
|
||||
const [voteCount, setVoteCount] = useState(0);
|
||||
const [averageVote, setAverageVote] = useState(0);
|
||||
|
||||
async function getInfo() {
|
||||
const { data: info } = await api.sessions.info({ session_id: sessionId });
|
||||
console.log(info);
|
||||
setTrackCount(info["track_count"]);
|
||||
setVoteCount(info["vote_count"]);
|
||||
setAverageVote(info["average_vote"]);
|
||||
}
|
||||
useEffect(() => {
|
||||
getInfo();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
||||
<h1>Post-game</h1>
|
||||
<p>Bedankt voor het stemmen!</p>
|
||||
<p>;)</p>
|
||||
<p
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
Bedankt voor het stemmen!
|
||||
</p>
|
||||
<div className="flex flex-col text-center">
|
||||
<p
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
Jullie hebben {voteCount} stemmen uitgebracht op {trackCount} nummers.
|
||||
</p>
|
||||
<p
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
Gemiddeld vonden jullie {(averageVote * 100).toFixed(0)}% van de
|
||||
nummers best oké.
|
||||
</p>
|
||||
<p
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
Applaus voor jezelf!
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
Tot HDcon!
|
||||
</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Nunito } from "next/font/google";
|
||||
import users from "muzak/data/users.json";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { api, Track } from "muzak/data/fetcher";
|
||||
|
||||
const nunito = Nunito({
|
||||
weight: "900",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export default function Lobby() {
|
||||
const [partySize, setPartySize] = useState(8);
|
||||
const [userList, setUserList] = useState([]);
|
||||
const [yesVoteList, setYesVoteList] = useState([]);
|
||||
const [noVoteList, setNoVoteList] = useState([]);
|
||||
|
||||
const [currentSong, setCurrentSong] = useState<Track>();
|
||||
const [songLoading, setSongLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
populateUserList();
|
||||
}, [partySize]);
|
||||
|
||||
async function getTrack() {
|
||||
setSongLoading(true);
|
||||
const { data: track } = await api.tracks.vote({});
|
||||
setCurrentSong(track);
|
||||
console.log("I got a track!", track.name);
|
||||
setSongLoading(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentSong && !songLoading) getTrack();
|
||||
}, []);
|
||||
|
||||
function populateUserList() {
|
||||
const mappedUsers = users.slice(0, partySize).map((user) => ({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
avatar: user.avatar,
|
||||
host: user.host,
|
||||
}));
|
||||
setUserList(mappedUsers);
|
||||
}
|
||||
|
||||
// function handleYesVote(user) {
|
||||
// setYesVoteList([...yesVoteList, user]);
|
||||
// }
|
||||
|
||||
// function handleNoVote(user) {
|
||||
// setNoVoteList([...noVoteList, user]);
|
||||
// }
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-between gap-4 p-24">
|
||||
<div className="flex w-full items-start justify-between text-center">
|
||||
<div className="flex-1"></div>
|
||||
<div className="flex flex-1 items-center justify-center">
|
||||
<div className="flex h-[170px] w-[170px] items-center justify-center rounded-full border-2 bg-orange-400">
|
||||
<div className="text-5xl text-white drop-shadow-sm drop-shadow-black">
|
||||
<button onClick={getTrack}>30</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 flex-row items-center justify-end gap-4">
|
||||
<Link
|
||||
href="/pause"
|
||||
className="w-30 rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
>
|
||||
PAUSE
|
||||
</Link>
|
||||
<Link
|
||||
href="/voting"
|
||||
className="w-30 rounded-lg border-2 border-black bg-yellow-500 p-4 text-2xl font-extrabold text-blue-600 hover:bg-yellow-400"
|
||||
>
|
||||
NEXT
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="flex flex-row items-center justify-center gap-x-32">
|
||||
<div className="grid min-w-52 grid-flow-row grid-cols-3 items-start justify-start gap-4 rounded bg-blue-950/80 p-4">
|
||||
{userList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[120px] w-[120px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img src={user.avatar} alt={user.name} width={75} height={75} />
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
))}{" "}
|
||||
</div>
|
||||
{!songLoading && currentSong ? (
|
||||
<div className="rounded-md bg-black p-2">
|
||||
<Image
|
||||
src={currentSong.album_cover}
|
||||
alt={currentSong.name}
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-md bg-black p-2">
|
||||
<Image
|
||||
src="/meowl.png"
|
||||
alt="Loading.."
|
||||
width={500}
|
||||
height={500}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="grid min-w-52 grid-flow-row grid-cols-3 items-start justify-start gap-4 rounded bg-blue-950/80 p-4">
|
||||
{userList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[120px] w-[120px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img src={user.avatar} alt={user.name} width={75} height={75} />
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
))}{" "}
|
||||
</div>
|
||||
</div>
|
||||
{!songLoading && currentSong && (
|
||||
<div className="flex flex-col items-center justify-start">
|
||||
<h2
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
{currentSong.artist}
|
||||
</h2>
|
||||
<h2
|
||||
className={`${nunito.className} text-outline text-[42px] tracking-tighter text-yellow-500`}
|
||||
>
|
||||
{currentSong.name}
|
||||
</h2>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row items-start justify-start gap-10 rounded bg-blue-950/80 p-4">
|
||||
{userList.map((user) => (
|
||||
<div
|
||||
key={user.id}
|
||||
className="flex h-[100px] w-[100px] flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img src={user.avatar} alt={user.name} width={75} height={75} />
|
||||
<div className="mt-1 min-w-[90px] rounded-3xl px-3 text-center text-3xl font-semibold text-white">
|
||||
{user.name}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -11,44 +11,28 @@ import getSessionId from "muzak/data/session";
|
||||
export default function SessionChecker() {
|
||||
const auth = useAuth();
|
||||
const router = useRouter();
|
||||
const path = usePathname();
|
||||
|
||||
const [isSession, setIsSession] = useState(false);
|
||||
const [link, setLink] = useState("/lobby");
|
||||
const { sessionId, setSessionId } = useSession();
|
||||
|
||||
// async function getSessions() {
|
||||
// if (!auth.isLoading && !auth.error && auth.isAuthenticated) {
|
||||
// const { data: sessions } = await api?.sessions?.list({});
|
||||
// console.log(sessions);
|
||||
// if (sessions?.length > 0) {
|
||||
// setIsSession(true);
|
||||
// const user = getUser();
|
||||
// setSessionId(sessions[0].session_id);
|
||||
// if (sessions[0].host === user?.profile?.email) {
|
||||
// setLink("/lobby");
|
||||
// } else {
|
||||
// setLink("/session");
|
||||
// }
|
||||
// } else {
|
||||
// setIsSession(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
async function getSessions() {
|
||||
if (!auth.isLoading && !auth.error && auth.isAuthenticated) {
|
||||
const { data: sessions } = await api?.sessions?.list({});
|
||||
console.log(sessions);
|
||||
if (sessions?.length > 0) {
|
||||
const user = getUser();
|
||||
setSessionId(sessions[0].session_id);
|
||||
if (sessions[0].host !== user?.profile?.email) {
|
||||
if (path !== "/mobile-voting") {
|
||||
router.push("/mobile-voting");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
//getSessionId();
|
||||
console.log("hheufheufh");
|
||||
}, [auth]);
|
||||
|
||||
if (sessionId) {
|
||||
return (
|
||||
<Link
|
||||
href={link}
|
||||
className="bold text-l absolute top-2 left-2 rounded-[50%] bg-red-500 p-3 font-bold text-white"
|
||||
>
|
||||
Session available!
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
getSessions();
|
||||
}, [auth, path]);
|
||||
return <> </>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserManager } from "oidc-client-ts";
|
||||
import { UserManager, WebStorageStateStore } from "oidc-client-ts";
|
||||
import { AuthProviderProps } from "react-oidc-context";
|
||||
|
||||
const ORIGIN_URI = globalThis?.window?.location.origin;
|
||||
@@ -17,8 +17,15 @@ const userConfig: AuthProviderProps = {
|
||||
post_logout_redirect_uri: ORIGIN_URI,
|
||||
response_mode: "query",
|
||||
revokeTokensOnSignout: true,
|
||||
staleStateAgeInSeconds: 60000,
|
||||
};
|
||||
|
||||
if (globalThis?.window != undefined) {
|
||||
userConfig.userStore = new WebStorageStateStore({
|
||||
store: window.localStorage,
|
||||
});
|
||||
}
|
||||
|
||||
export const userManager = new UserManager(userConfig);
|
||||
|
||||
// Some handling of token expiration.
|
||||
|
||||
@@ -13,7 +13,7 @@ export const useSpotifyIframe = () => {
|
||||
document.body.appendChild(script);
|
||||
|
||||
script.onload = () => {
|
||||
window.onSpotifyIframeApiReady = (IFrameAPI) => {
|
||||
(window as any).onSpotifyIframeApiReady = (IFrameAPI) => {
|
||||
const element = document.getElementById(elementId);
|
||||
const options = {
|
||||
width: 400,
|
||||
|
||||
@@ -9,6 +9,11 @@ export function getUser() {
|
||||
let oidcStorage = window?.sessionStorage.getItem(
|
||||
`oidc.user:${oidcConfig.authority}:${oidcConfig.client_id}`,
|
||||
);
|
||||
if (!oidcStorage) {
|
||||
oidcStorage = window?.localStorage.getItem(
|
||||
`oidc.user:${oidcConfig.authority}:${oidcConfig.client_id}`,
|
||||
);
|
||||
}
|
||||
if (!oidcStorage) {
|
||||
return null;
|
||||
}
|
||||
@@ -87,6 +92,10 @@ export const api = {
|
||||
},
|
||||
sessions: {
|
||||
get: fetcher.path("/voting/session/{session_id}/").method("get").create(),
|
||||
info: fetcher
|
||||
.path("/voting/session/{session_id}/info")
|
||||
.method("get")
|
||||
.create(),
|
||||
list: fetcher.path("/voting/session/").method("get").create(),
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user