update
This commit is contained in:
@@ -19,16 +19,26 @@ log = logging.getLogger("events")
|
|||||||
PUFFER = 16
|
PUFFER = 16
|
||||||
|
|
||||||
_abonnenten: set[asyncio.Queue] = set()
|
_abonnenten: set[asyncio.Queue] = set()
|
||||||
|
# Welche Sitzung hinter einer Verbindung steckt — daraus ergibt sich,
|
||||||
|
# wer gerade online ist: eine offene Seite mit gewähltem Namen
|
||||||
|
_sitzungen: dict[asyncio.Queue, str] = {}
|
||||||
|
|
||||||
|
|
||||||
def anmelden():
|
def anmelden(session_id=None):
|
||||||
queue = asyncio.Queue(maxsize=PUFFER)
|
queue = asyncio.Queue(maxsize=PUFFER)
|
||||||
_abonnenten.add(queue)
|
_abonnenten.add(queue)
|
||||||
|
if session_id:
|
||||||
|
_sitzungen[queue] = session_id
|
||||||
return queue
|
return queue
|
||||||
|
|
||||||
|
|
||||||
def abmelden(queue):
|
def abmelden(queue):
|
||||||
_abonnenten.discard(queue)
|
_abonnenten.discard(queue)
|
||||||
|
_sitzungen.pop(queue, None)
|
||||||
|
|
||||||
|
|
||||||
|
def sitzungen_online():
|
||||||
|
return set(_sitzungen.values())
|
||||||
|
|
||||||
|
|
||||||
def anzahl():
|
def anzahl():
|
||||||
@@ -60,3 +70,5 @@ async def stream(queue, request):
|
|||||||
yield ": ping\n\n"
|
yield ": ping\n\n"
|
||||||
finally:
|
finally:
|
||||||
abmelden(queue)
|
abmelden(queue)
|
||||||
|
# Die anderen Seiten sollen den grünen Punkt ausmachen
|
||||||
|
senden("users")
|
||||||
|
|||||||
@@ -564,8 +564,10 @@ letzter_dreh = {"ergebnis": None, "start": 0.0, "teilnehmer": []}
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/api/events")
|
@app.get("/api/events")
|
||||||
async def ereignisse(request: Request):
|
async def ereignisse(request: Request, filmabend_sid: str | None = Cookie(None)):
|
||||||
queue = events.anmelden()
|
queue = events.anmelden(filmabend_sid)
|
||||||
|
# Die anderen Seiten sollen den grünen Punkt anmachen
|
||||||
|
events.senden("users")
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
events.stream(queue, request),
|
events.stream(queue, request),
|
||||||
media_type="text/event-stream",
|
media_type="text/event-stream",
|
||||||
@@ -833,8 +835,11 @@ def runde(response: Response, filmabend_sid: str | None = Cookie(None)):
|
|||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
staende = punkte.alle()
|
staende = punkte.alle()
|
||||||
leer = punkte.stand(0)
|
leer = punkte.stand(0)
|
||||||
|
online = users.online_ids(events.sitzungen_online())
|
||||||
return {
|
return {
|
||||||
"items": [{**u, **staende.get(u["id"], leer)} for u in users.alle()],
|
"items": [
|
||||||
|
{**u, **staende.get(u["id"], leer), "online": u["id"] in online} for u in users.alle()
|
||||||
|
],
|
||||||
"mine": users.aktuell(sid),
|
"mine": users.aktuell(sid),
|
||||||
"host": users.ist_host(sid),
|
"host": users.ist_host(sid),
|
||||||
}
|
}
|
||||||
@@ -995,6 +1000,7 @@ def name_waehlen(
|
|||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
if user_id is None:
|
if user_id is None:
|
||||||
users.abwaehlen(sid)
|
users.abwaehlen(sid)
|
||||||
|
events.senden("users")
|
||||||
return {"mine": None}
|
return {"mine": None}
|
||||||
|
|
||||||
film = users.schutz_von(user_id)
|
film = users.schutz_von(user_id)
|
||||||
@@ -1009,6 +1015,7 @@ def name_waehlen(
|
|||||||
benutzer = users.waehlen(sid, user_id)
|
benutzer = users.waehlen(sid, user_id)
|
||||||
if not benutzer:
|
if not benutzer:
|
||||||
raise HTTPException(status_code=404, detail="Unbekannter Name")
|
raise HTTPException(status_code=404, detail="Unbekannter Name")
|
||||||
|
events.senden("users")
|
||||||
return {"mine": benutzer}
|
return {"mine": benutzer}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,11 @@ def host_setzen(session_id, an):
|
|||||||
return an
|
return an
|
||||||
|
|
||||||
|
|
||||||
|
def online_ids(session_ids):
|
||||||
|
"""Die Namen hinter offenen Verbindungen — wer gerade da ist."""
|
||||||
|
return {besitzer(sid) for sid in session_ids} - {None}
|
||||||
|
|
||||||
|
|
||||||
def besitzer(session_id):
|
def besitzer(session_id):
|
||||||
"""Die user_id dieser Sitzung, oder None ohne gewählten Namen.
|
"""Die user_id dieser Sitzung, oder None ohne gewählten Namen.
|
||||||
|
|
||||||
|
|||||||
@@ -46,3 +46,23 @@ def test_abwaehlen_macht_wieder_zum_zuschauer(client, film):
|
|||||||
# Lesen geht weiter, Schreiben nicht mehr
|
# Lesen geht weiter, Schreiben nicht mehr
|
||||||
assert client.get("/api/suggestions").json()["mine"] is None
|
assert client.get("/api/suggestions").json()["mine"] is None
|
||||||
assert client.post("/api/suggestions", json={"movie_id": film(2)}).status_code == 403
|
assert client.post("/api/suggestions", json={"movie_id": film(2)}).status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_online_ist_wer_eine_seite_offen_hat(client, anmelden):
|
||||||
|
from app import events
|
||||||
|
|
||||||
|
anmelden(client, "Anna")
|
||||||
|
sid = client.cookies.get("filmabend_sid")
|
||||||
|
ich = client.get("/api/users").json()["mine"]["id"]
|
||||||
|
|
||||||
|
def online():
|
||||||
|
return {u["id"]: u["online"] for u in client.get("/api/users").json()["items"]}
|
||||||
|
|
||||||
|
assert online()[ich] is False
|
||||||
|
queue = events.anmelden(sid)
|
||||||
|
assert online()[ich] is True
|
||||||
|
# Namenswechsel im selben Browser: der alte Name ist offline
|
||||||
|
anmelden(client, "Ben")
|
||||||
|
assert online()[ich] is False
|
||||||
|
events.abmelden(queue)
|
||||||
|
assert True not in online().values()
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ const tabs = computed(() => [
|
|||||||
count: quellen.value.find((q) => q.id === quelle.value)?.count ?? null,
|
count: quellen.value.find((q) => q.id === quelle.value)?.count ?? null,
|
||||||
},
|
},
|
||||||
{ id: 'suche', label: 'Suche', count: null },
|
{ id: 'suche', label: 'Suche', count: null },
|
||||||
...(host.value ? [] : [{ id: 'merkliste', label: 'Sammlungen', count: null }]),
|
{ id: 'merkliste', label: 'Sammlungen', count: null },
|
||||||
{ id: 'wheel', label: 'Rad', count: null },
|
{ id: 'wheel', label: 'Rad', count: null },
|
||||||
{ id: 'stream', label: 'Stream', count: null },
|
{ id: 'stream', label: 'Stream', count: null },
|
||||||
...(angemeldet.value || host.value ? [{ id: 'clips', label: 'Clips', count: null }] : []),
|
...(angemeldet.value || host.value ? [{ id: 'clips', label: 'Clips', count: null }] : []),
|
||||||
@@ -571,6 +571,14 @@ onMounted(load)
|
|||||||
gap: 0.35rem;
|
gap: 0.35rem;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
/* Zehn Reiter passen nicht auf jeden Schirm — dann rollt die Leiste,
|
||||||
|
statt unter den Namen zu laufen */
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rechts {
|
.rechts {
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ onUnmounted(() => {
|
|||||||
<button type="button" :class="{ aktiv: mine?.id === u.id }" @click="waehlen(u)">
|
<button type="button" :class="{ aktiv: mine?.id === u.id }" @click="waehlen(u)">
|
||||||
<span class="zeile" :title="`Erfahrung: ${u.punkte - u.von} / ${u.bis - u.von}`">
|
<span class="zeile" :title="`Erfahrung: ${u.punkte - u.von} / ${u.bis - u.von}`">
|
||||||
<PersonName :name="u.name" :level="u.level" />
|
<PersonName :name="u.name" :level="u.level" />
|
||||||
<span v-if="u.geschuetzt" class="schloss" title="Mit Anmeldefilm">●</span>
|
<span v-if="u.online" class="online" title="Gerade da"></span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@@ -423,11 +423,15 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Der Punkt zeigt: hier fragt die Seite nach dem Anmeldefilm */
|
/* Der Punkt zeigt: hier fragt die Seite nach dem Anmeldefilm */
|
||||||
.schloss {
|
/* Grüner Punkt: eine Seite mit diesem Namen ist gerade offen */
|
||||||
margin-left: 0.3rem;
|
.online {
|
||||||
color: var(--muted);
|
display: inline-block;
|
||||||
font-size: 0.6rem;
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-left: 0.35rem;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
background: #3ddc84;
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640px), (pointer: coarse) {
|
@media (max-width: 640px), (pointer: coarse) {
|
||||||
|
|||||||
Reference in New Issue
Block a user