diff --git a/backend/app/views/auth/api.py b/backend/app/views/auth/api.py index 75da842..56bc1b9 100644 --- a/backend/app/views/auth/api.py +++ b/backend/app/views/auth/api.py @@ -78,7 +78,7 @@ async def read_users_me( @router.get("/anon") -async def get_qnon_user( +async def get_anon_user( anon_user: Annotated[schemas.AnonUser, Depends(services.get_anon_user)] ) -> schemas.AnonUser: return anon_user diff --git a/backend/app/views/queue/services.py b/backend/app/views/queue/services.py index a51fbc8..b1471dc 100644 --- a/backend/app/views/queue/services.py +++ b/backend/app/views/queue/services.py @@ -5,7 +5,7 @@ from uuid import UUID import redis import asyncio -from ...dependencies import get_db, get_pubsub +from ...dependencies import get_db, get_pubsub, get_redis from ...db import models from ..auth import services as auth_services @@ -43,7 +43,6 @@ def get_detailed_queue( db: Annotated[Session, Depends(get_db)], ) -> schemas.QueueDetail: q = db.query(models.Queue).filter(models.Queue.id == queue_id).first() - print("\n\n", queue_id, "\n\n", flush=True) if q: return schemas.QueueDetail( id=q.id, @@ -63,10 +62,11 @@ def get_detailed_queue( ) -def join_queue( +async def join_queue( queue_id: UUID, client: Annotated[auth_schemas.AnonUser, Depends(auth_services.get_anon_user)], db: Annotated[Session, Depends(get_db)], + r: Annotated[redis.client.Redis, Depends(get_redis)], ) -> schemas.QueueUser: q = get_queue_by_id(queue_id, db) if q: @@ -78,6 +78,7 @@ def join_queue( ) db.add(new_qu) db.commit() + await r.publish(str(queue_id), "updated") return new_qu raise HTTPException( status_code=status.HTTP_409_CONFLICT, @@ -96,9 +97,8 @@ async def set_queue_listener( ) -> schemas.QueueDetail: await ps.subscribe(str(queue_id)) async for m in ps.listen(): - print(m, flush=True) if m.get("data", None) == b"updated": - print("UPDATED", flush=True) break + await ps.unsubscribe() new_queue = get_detailed_queue(queue_id=queue_id, db=db) return new_queue diff --git a/frontend/app/src/components/queue/ApproveQueueJoinCard.tsx b/frontend/app/src/components/queue/ApproveQueueJoinCard.tsx index fbb136a..95ed689 100644 --- a/frontend/app/src/components/queue/ApproveQueueJoinCard.tsx +++ b/frontend/app/src/components/queue/ApproveQueueJoinCard.tsx @@ -1,9 +1,8 @@ -import React, { useState } from "react"; +import React, { useContext } from "react"; import "../styles.css"; -import { Button, Divider, Input, Spin } from "antd"; +import { Button, Spin } from "antd"; import { ArrowLeftOutlined, - CameraOutlined, FileTextOutlined, PlusOutlined, UserOutlined, @@ -11,13 +10,34 @@ import { import Title from "antd/es/typography/Title"; import tr from "../../config/translation"; import { useNavigate } from "react-router-dom"; -import { useGetQueueDetailQuery } from "../../slice/QueueApi"; +import { + useGetQueueDetailQuery, + useJoinQueueMutation, +} from "../../slice/QueueApi"; +import { MessageContext } from "../../App"; const ApproveQueueJoinCard = (props: { id: string }): JSX.Element => { const navigate = useNavigate(); - const { data, isFetching, isError } = useGetQueueDetailQuery(props.id, { - skip: !props.id, - }); + const messageApi = useContext(MessageContext); + + const { data, refetch, isFetching, isError } = useGetQueueDetailQuery( + props.id, + { + skip: !props.id, + } + ); + const [joinQueue, { isLoading }] = useJoinQueueMutation(); + + const onJoinButtonClick = () => { + joinQueue(props.id) + .unwrap() + .then(() => + messageApi.success(tr("Successfully joined queue ") + data.name) + ) + .then(() => navigate(`/queue/${props.id}`)) + .then(() => refetch()) + .catch((e) => messageApi.error(tr(e.data.detail))); + }; return (
@@ -48,7 +68,11 @@ const ApproveQueueJoinCard = (props: { id: string }): JSX.Element => { {" "} {data?.participants?.remaining} / {data?.participants?.total}

- + + +
)} diff --git a/frontend/app/src/components/queue/QueueCard.tsx b/frontend/app/src/components/queue/QueueCard.tsx index 5526d89..a86459f 100644 --- a/frontend/app/src/components/queue/QueueCard.tsx +++ b/frontend/app/src/components/queue/QueueCard.tsx @@ -13,11 +13,15 @@ import tr from "../../config/translation"; import { Link, useParams } from "react-router-dom"; import { useSelector } from "react-redux"; import { StorePrototype } from "../../config/store"; +import AnonUserCard from "../user/AnonUserCard"; const QueueCard = (): JSX.Element => { const user = useSelector((state: StorePrototype) => state.auth.user); const { queueId } = useParams(); - const { data, isFetching } = useGetQueueDetailQuery(queueId); + const { data, isFetching } = useGetQueueDetailQuery(queueId, { + skip: !queueId, + }); + return (
{ {data?.participants?.remaining} / {data?.participants?.total}

+
+ + {tr("Queue participants")} + + {data?.participants.users_list.map((v) => { + return ; + })} +
); diff --git a/frontend/app/src/components/styles.css b/frontend/app/src/components/styles.css index c7769db..8e9fe4f 100644 --- a/frontend/app/src/components/styles.css +++ b/frontend/app/src/components/styles.css @@ -101,3 +101,25 @@ .queue-info > * { text-align: left; } + +.anon-circle { + width: 50px; + height: 50px; + line-height: 50px; + border-radius: 50%; + text-align: center; +} + +.anon-card { + display: flex; + flex-flow: row nowrap; + align-items: center; + animation: 0.3s ease-out 0s 1 cardPopup; + background: #001d39; + margin-top: 0.5rem; + margin-right: 1rem; + margin-left: 1rem; + border-radius: 10px; + padding: 1rem; + border: 2px solid #00d8a4; +} diff --git a/frontend/app/src/components/user/AnonUserCard.tsx b/frontend/app/src/components/user/AnonUserCard.tsx index 11212b0..25cd5d4 100644 --- a/frontend/app/src/components/user/AnonUserCard.tsx +++ b/frontend/app/src/components/user/AnonUserCard.tsx @@ -1,7 +1,45 @@ import React from "react"; +import { AnonUser } from "../../slice/AuthApi"; +import "../styles.css"; +import tr from "../../config/translation"; +import Title from "antd/es/typography/Title"; +import Paragraph from "antd/es/typography/Paragraph"; -const UUIDToColor = (uuid: string): string => {}; - -const AnonUserCard = (): JSX.Element => { - return; +const UUIDToColor = (uuid: string): string => { + return ( + "#" + + uuid.split("-").reduce((store: string, v: string) => { + return store + v.substring(0, 1); + }, "") + + uuid.substring(uuid.length - 1) + ); }; + +const getProfileText = (name: string): string => { + return name.substring(0, 1); +}; + +const AnonUserCard = (props: { + user: AnonUser; + backlight?: "self" | "active" | undefined; +}): JSX.Element => { + return ( +
+
+ {props.user.name + ? getProfileText(props.user.name) + : props.user.id.substring(0, 2)} +
+

+ {props.user.name + ? props.user.name + : tr("Anonymous") + " #" + props.user.id.substring(0, 4)} +

+
+ ); +}; + +export default AnonUserCard; diff --git a/frontend/app/src/slice/AuthApi.ts b/frontend/app/src/slice/AuthApi.ts index d583d58..b1d5ff4 100644 --- a/frontend/app/src/slice/AuthApi.ts +++ b/frontend/app/src/slice/AuthApi.ts @@ -24,6 +24,11 @@ export type TokenResponse = { token_type: string; }; +export type AnonUser = { + id: string; + name: string; +}; + export const AuthApi = createApi({ reducerPath: "AuthApi", baseQuery: fetchBaseQuery({ diff --git a/frontend/app/src/slice/QueueApi.ts b/frontend/app/src/slice/QueueApi.ts index 5520764..462a41f 100644 --- a/frontend/app/src/slice/QueueApi.ts +++ b/frontend/app/src/slice/QueueApi.ts @@ -1,6 +1,7 @@ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; import { baseUrl } from "../config/baseUrl"; import { RootState } from "../config/store"; +import { AnonUser } from "./AuthApi"; export type CreateQueueRequest = { name: string; @@ -13,6 +14,17 @@ export type Queue = { description: string | null; }; +export type QueueDetail = { + id: string; + name: string; + description: string | null; + participants: { + total: number; + remaining: number; + users_list: [AnonUser]; + }; +}; + export const QueueApi = createApi({ reducerPath: "QueueApi", baseQuery: fetchBaseQuery({ @@ -30,14 +42,14 @@ export const QueueApi = createApi({ }, }), endpoints: (builder) => ({ - getQueues: builder.query({ + getQueues: builder.query<[Queue], undefined>({ query: () => "/", }), - getQueueDetail: builder.query({ + getQueueDetail: builder.query({ query: (queueId: string | undefined) => `/${queueId}`, }), joinQueue: builder.mutation({ - query: (queueId: string) => `/${queueId}/join`, + query: (queueId: string) => ({ url: `/${queueId}/join`, method: "POST" }), }), createQueue: builder.mutation({ query: (data: CreateQueueRequest) => ({ @@ -52,5 +64,6 @@ export const QueueApi = createApi({ export const { useGetQueuesQuery, useGetQueueDetailQuery, + useJoinQueueMutation, useCreateQueueMutation, } = QueueApi;