update
This commit is contained in:
@@ -250,6 +250,25 @@ def sitzung(response: Response, sid):
|
|||||||
return neu
|
return neu
|
||||||
|
|
||||||
|
|
||||||
|
def mitglied(sid):
|
||||||
|
"""Wer keinen Namen gewählt hat, darf zusehen, aber nichts eintragen.
|
||||||
|
|
||||||
|
Vorschläge, Sammlungen, Wertungen und Wünsche gehören einer Person.
|
||||||
|
Ohne Namen hingen sie nur am Cookie: im geteilten Browser schriebe
|
||||||
|
jeder auf dasselbe Konto, und nach dem Löschen der Cookies wäre alles
|
||||||
|
weg. Die Knöpfe blendet die Oberfläche aus — das hier ist der Riegel.
|
||||||
|
|
||||||
|
Der Host braucht dafür ebenfalls einen Namen. Seine eigenen Knöpfe
|
||||||
|
(drehen, eintragen, aufräumen) laufen über andere Routen.
|
||||||
|
"""
|
||||||
|
ich = users.besitzer(sid)
|
||||||
|
if ich is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=403, detail="Dafür musst du oben deinen Namen wählen."
|
||||||
|
)
|
||||||
|
return ich
|
||||||
|
|
||||||
|
|
||||||
# Der letzte Dreh, damit eine Seite mitten in der Animation nachziehen kann
|
# Der letzte Dreh, damit eine Seite mitten in der Animation nachziehen kann
|
||||||
letzter_dreh = {"ergebnis": None, "start": 0.0}
|
letzter_dreh = {"ergebnis": None, "start": 0.0}
|
||||||
|
|
||||||
@@ -324,6 +343,7 @@ async def vorschlagen(
|
|||||||
filmabend_sid: str | None = Cookie(None),
|
filmabend_sid: str | None = Cookie(None),
|
||||||
):
|
):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
movie = await film_holen(movie_id)
|
movie = await film_holen(movie_id)
|
||||||
# Ein zweiter Vorschlag ersetzt den ersten, statt abgelehnt zu werden
|
# Ein zweiter Vorschlag ersetzt den ersten, statt abgelehnt zu werden
|
||||||
eintrag = suggestions.ersetzen(sid, movie, users.name_von(sid))
|
eintrag = suggestions.ersetzen(sid, movie, users.name_von(sid))
|
||||||
@@ -336,6 +356,7 @@ async def vorschlagen(
|
|||||||
@app.delete("/api/suggestions")
|
@app.delete("/api/suggestions")
|
||||||
def vorschlag_loeschen(response: Response, filmabend_sid: str | None = Cookie(None)):
|
def vorschlag_loeschen(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
entfernt = suggestions.loeschen(sid)
|
entfernt = suggestions.loeschen(sid)
|
||||||
if entfernt:
|
if entfernt:
|
||||||
events.senden("suggestions")
|
events.senden("suggestions")
|
||||||
@@ -366,6 +387,7 @@ def wunsch_anlegen(
|
|||||||
filmabend_sid: str | None = Cookie(None),
|
filmabend_sid: str | None = Cookie(None),
|
||||||
):
|
):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
liste = features.anlegen(sid, text)
|
liste = features.anlegen(sid, text)
|
||||||
if liste is None:
|
if liste is None:
|
||||||
raise HTTPException(status_code=400, detail="Kein Text")
|
raise HTTPException(status_code=400, detail="Kein Text")
|
||||||
@@ -397,6 +419,7 @@ def wunsch_abstimmen(
|
|||||||
):
|
):
|
||||||
"""Ein Klick setzt die Stimme, der nächste nimmt sie zurück."""
|
"""Ein Klick setzt die Stimme, der nächste nimmt sie zurück."""
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
steht = features.abstimmen(feature_id, sid)
|
steht = features.abstimmen(feature_id, sid)
|
||||||
if steht is None:
|
if steht is None:
|
||||||
raise HTTPException(status_code=404, detail="Wunsch nicht gefunden")
|
raise HTTPException(status_code=404, detail="Wunsch nicht gefunden")
|
||||||
@@ -421,6 +444,7 @@ def wunsch_anmerken(
|
|||||||
filmabend_sid: str | None = Cookie(None),
|
filmabend_sid: str | None = Cookie(None),
|
||||||
):
|
):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
if not features.anmerken(feature_id, sid, text):
|
if not features.anmerken(feature_id, sid, text):
|
||||||
raise HTTPException(status_code=400, detail="Kein Text oder Wunsch unbekannt")
|
raise HTTPException(status_code=400, detail="Kein Text oder Wunsch unbekannt")
|
||||||
events.senden("features")
|
events.senden("features")
|
||||||
@@ -451,6 +475,7 @@ async def merken(
|
|||||||
filmabend_sid: str | None = Cookie(None),
|
filmabend_sid: str | None = Cookie(None),
|
||||||
):
|
):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
return wishlist.merken(sid, await film_holen(movie_id))
|
return wishlist.merken(sid, await film_holen(movie_id))
|
||||||
|
|
||||||
|
|
||||||
@@ -461,6 +486,7 @@ def vergessen(
|
|||||||
filmabend_sid: str | None = Cookie(None),
|
filmabend_sid: str | None = Cookie(None),
|
||||||
):
|
):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
return {"removed": wishlist.vergessen(sid, movie_id)}
|
return {"removed": wishlist.vergessen(sid, movie_id)}
|
||||||
|
|
||||||
|
|
||||||
@@ -554,6 +580,7 @@ def bewerten(
|
|||||||
filmabend_sid: str | None = Cookie(None),
|
filmabend_sid: str | None = Cookie(None),
|
||||||
):
|
):
|
||||||
sid = sitzung(response, filmabend_sid)
|
sid = sitzung(response, filmabend_sid)
|
||||||
|
mitglied(sid)
|
||||||
stars = max(1, min(watched.MAX_STERNE, stars))
|
stars = max(1, min(watched.MAX_STERNE, stars))
|
||||||
# Der Name kommt aus der Runde, nicht aus der Anfrage
|
# Der Name kommt aus der Runde, nicht aus der Anfrage
|
||||||
eintrag = watched.bewerten(watched_id, sid, stars, users.name_von(sid), text)
|
eintrag = watched.bewerten(watched_id, sid, stars, users.name_von(sid), text)
|
||||||
|
|||||||
@@ -52,14 +52,36 @@ def frische_db(app_start):
|
|||||||
main.letzter_dreh.update(ergebnis=None, start=0.0)
|
main.letzter_dreh.update(ergebnis=None, start=0.0)
|
||||||
|
|
||||||
|
|
||||||
# Ein Client je Person — jeder hält sein eigenes Cookie
|
def _anmelden(client, name):
|
||||||
|
"""Namen anlegen, falls nötig, und für diesen Client wählen."""
|
||||||
|
antwort = client.post("/api/users", json={"name": name})
|
||||||
|
if antwort.status_code == 200:
|
||||||
|
user_id = antwort.json()["id"]
|
||||||
|
else:
|
||||||
|
runde = client.get("/api/users").json()["items"]
|
||||||
|
user_id = next(u["id"] for u in runde if u["name"] == name)
|
||||||
|
return client.post("/api/users/waehlen", json={"user_id": user_id}).json()["mine"]
|
||||||
|
|
||||||
|
|
||||||
|
# Ein Client je Person — jeder hält sein eigenes Cookie. Beide stehen in
|
||||||
|
# der Runde, denn ohne Namen darf niemand etwas eintragen.
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client():
|
def client():
|
||||||
return TestClient(main.app)
|
c = TestClient(main.app)
|
||||||
|
_anmelden(c, "Anna")
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client2():
|
def client2():
|
||||||
|
c = TestClient(main.app)
|
||||||
|
_anmelden(c, "Ben")
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def zuschauer():
|
||||||
|
"""Ohne gewählten Namen: darf alles lesen, nichts schreiben."""
|
||||||
return TestClient(main.app)
|
return TestClient(main.app)
|
||||||
|
|
||||||
|
|
||||||
@@ -78,14 +100,4 @@ def film():
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def anmelden():
|
def anmelden():
|
||||||
"""Client wählt einen Namen; legt ihn an, falls es ihn noch nicht gibt."""
|
"""Client wählt einen Namen; legt ihn an, falls es ihn noch nicht gibt."""
|
||||||
|
return _anmelden
|
||||||
def waehlen(client, name):
|
|
||||||
antwort = client.post("/api/users", json={"name": name})
|
|
||||||
if antwort.status_code == 200:
|
|
||||||
user_id = antwort.json()["id"]
|
|
||||||
else:
|
|
||||||
runde = client.get("/api/users").json()["items"]
|
|
||||||
user_id = next(u["id"] for u in runde if u["name"] == name)
|
|
||||||
return client.post("/api/users/waehlen", json={"user_id": user_id}).json()["mine"]
|
|
||||||
|
|
||||||
return waehlen
|
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ def test_namenswechsel_laesst_alte_eintraege_beim_alten_namen(client, anmelden,
|
|||||||
assert client.get("/api/suggestions").json()["mine"] == als_anna["id"]
|
assert client.get("/api/suggestions").json()["mine"] == als_anna["id"]
|
||||||
|
|
||||||
|
|
||||||
def test_ohne_namen_zaehlt_die_sitzung(client, client2, film):
|
def test_abwaehlen_macht_wieder_zum_zuschauer(client, film):
|
||||||
vorschlag = client.post("/api/suggestions", json={"movie_id": film(1)}).json()
|
client.post("/api/suggestions", json={"movie_id": film(1)})
|
||||||
assert vorschlag["name"] is None
|
client.post("/api/users/waehlen", json={"user_id": None})
|
||||||
assert client.get("/api/suggestions").json()["mine"] == vorschlag["id"]
|
assert client.get("/api/users").json()["mine"] is None
|
||||||
assert client2.get("/api/suggestions").json()["mine"] is None
|
# Lesen geht weiter, Schreiben nicht mehr
|
||||||
|
assert client.get("/api/suggestions").json()["mine"] is None
|
||||||
|
assert client.post("/api/suggestions", json={"movie_id": film(2)}).status_code == 403
|
||||||
|
|||||||
74
backend/tests/test_zuschauer.py
Normal file
74
backend/tests/test_zuschauer.py
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
"""Der Zuschauer: kein Name gewählt, also nur mitlesen.
|
||||||
|
|
||||||
|
Gedacht für alle, die noch nicht in der Runde stehen. Sie sollen den
|
||||||
|
Katalog, das Rad und die Wertungen der anderen sehen, aber nichts
|
||||||
|
eintragen — sonst hinge ihr Kram allein am Cookie.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# Jede Zeile: wie die Anfrage aussieht, wenn ein Zuschauer sie stellt
|
||||||
|
SCHREIBEN = [
|
||||||
|
("post", "/api/suggestions", {"movie_id": 1}),
|
||||||
|
("delete", "/api/suggestions", None),
|
||||||
|
("post", "/api/wishlist", {"movie_id": 1}),
|
||||||
|
("delete", "/api/wishlist/1", None),
|
||||||
|
("post", "/api/features", {"text": "Dunkles Design"}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("methode,pfad,daten", SCHREIBEN)
|
||||||
|
def test_zuschauer_darf_nicht_schreiben(zuschauer, film, methode, pfad, daten):
|
||||||
|
film(1)
|
||||||
|
# DELETE nimmt bei TestClient keinen json-Parameter
|
||||||
|
anfrage = getattr(zuschauer, methode)
|
||||||
|
antwort = anfrage(pfad, json=daten) if daten is not None else anfrage(pfad)
|
||||||
|
assert antwort.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_zuschauer_darf_nicht_bewerten(zuschauer, client, film):
|
||||||
|
eintrag = client.post("/api/watched", json={"movie_id": film(1)}).json()
|
||||||
|
antwort = zuschauer.post(
|
||||||
|
f"/api/watched/{eintrag['id']}/rating", json={"stars": 5, "text": "stark"}
|
||||||
|
)
|
||||||
|
assert antwort.status_code == 403
|
||||||
|
assert zuschauer.get("/api/watched").json()[0]["count"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_zuschauer_darf_nicht_abstimmen_oder_ergaenzen(zuschauer, client):
|
||||||
|
wunsch = client.post("/api/features", json={"text": "Dunkles Design"}).json()[0]
|
||||||
|
assert zuschauer.post(f"/api/features/{wunsch['id']}/vote").status_code == 403
|
||||||
|
assert (
|
||||||
|
zuschauer.post(
|
||||||
|
f"/api/features/{wunsch['id']}/notes", json={"text": "dazu"}
|
||||||
|
).status_code
|
||||||
|
== 403
|
||||||
|
)
|
||||||
|
assert zuschauer.get("/api/features").json()[0]["votes"] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_zuschauer_darf_alles_lesen(zuschauer, client, film):
|
||||||
|
client.post("/api/suggestions", json={"movie_id": film(1)})
|
||||||
|
client.post("/api/wishlist", json={"movie_id": film(2)})
|
||||||
|
client.post("/api/features", json={"text": "Dunkles Design"})
|
||||||
|
client.post("/api/watched", json={"movie_id": film(3)})
|
||||||
|
|
||||||
|
for pfad in ("/api/movies", "/api/suggestions", "/api/wishlist",
|
||||||
|
"/api/features", "/api/watched", "/api/users", "/api/spin"):
|
||||||
|
assert zuschauer.get(pfad).status_code == 200
|
||||||
|
|
||||||
|
# Fremdes gehört ihm nicht
|
||||||
|
assert zuschauer.get("/api/suggestions").json()["mine"] is None
|
||||||
|
assert zuschauer.get("/api/wishlist").json()[0]["mine"] is False
|
||||||
|
assert zuschauer.get("/api/features").json()[0]["mine"] is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_host_kommt_ohne_namen_an_seine_knoepfe(zuschauer, client, film):
|
||||||
|
"""Den Hostmodus kennt nur der Browser — seine Routen bleiben offen."""
|
||||||
|
client.post("/api/suggestions", json={"movie_id": film(1)})
|
||||||
|
eintrag = zuschauer.post("/api/watched", json={"movie_id": film(2)}).json()
|
||||||
|
assert zuschauer.patch(
|
||||||
|
f"/api/watched/{eintrag['id']}", json={"hidden": True}
|
||||||
|
).status_code == 200
|
||||||
|
assert zuschauer.post("/api/spin").json()["started"] is True
|
||||||
|
assert zuschauer.delete("/api/suggestions/alle").status_code == 200
|
||||||
@@ -105,6 +105,10 @@ watch(activeTab, async () => {
|
|||||||
leiste.querySelector('.active')?.scrollIntoView({ inline: 'center', block: 'nearest' })
|
leiste.querySelector('.active')?.scrollIntoView({ inline: 'center', block: 'nearest' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Ohne gewählten Namen darf man alles lesen, aber nichts eintragen —
|
||||||
|
// für Zuschauer, die noch nicht in der Runde stehen
|
||||||
|
const angemeldet = computed(() => !!ich.value)
|
||||||
|
|
||||||
const selected = ref(null)
|
const selected = ref(null)
|
||||||
|
|
||||||
// Der Katalog liefert nur sechs Bildpfade je Film. Beim Öffnen holen wir
|
// Der Katalog liefert nur sechs Bildpfade je Film. Beim Öffnen holen wir
|
||||||
@@ -188,8 +192,8 @@ onMounted(load)
|
|||||||
>
|
>
|
||||||
Filter
|
Filter
|
||||||
</button>
|
</button>
|
||||||
<NamensWahl v-if="!host" :items="runde" :mine="ich" @waehlen="michSetzen" />
|
<span v-if="host" class="host">Host</span>
|
||||||
<span v-else class="host">Host</span>
|
<NamensWahl :items="runde" :mine="ich" @waehlen="michSetzen" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -236,6 +240,7 @@ onMounted(load)
|
|||||||
v-else-if="activeTab === 'wheel'"
|
v-else-if="activeTab === 'wheel'"
|
||||||
ref="rad"
|
ref="rad"
|
||||||
:host="host"
|
:host="host"
|
||||||
|
:angemeldet="angemeldet"
|
||||||
:merkliste="merkliste"
|
:merkliste="merkliste"
|
||||||
:mine="meinVorschlag?.id ?? null"
|
:mine="meinVorschlag?.id ?? null"
|
||||||
:gesehen-ids="gesehenIds"
|
:gesehen-ids="gesehenIds"
|
||||||
@@ -246,12 +251,14 @@ onMounted(load)
|
|||||||
<WatchedTab
|
<WatchedTab
|
||||||
v-else-if="activeTab === 'watched'"
|
v-else-if="activeTab === 'watched'"
|
||||||
:host="host"
|
:host="host"
|
||||||
|
:angemeldet="angemeldet"
|
||||||
@open="selected = $event"
|
@open="selected = $event"
|
||||||
@changed="ladeGesehen"
|
@changed="ladeGesehen"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<MerklisteTab
|
<MerklisteTab
|
||||||
v-else-if="activeTab === 'merkliste'"
|
v-else-if="activeTab === 'merkliste'"
|
||||||
|
:angemeldet="angemeldet"
|
||||||
:gesehen-ids="gesehenIds"
|
:gesehen-ids="gesehenIds"
|
||||||
:items="merkliste"
|
:items="merkliste"
|
||||||
:ich="ich"
|
:ich="ich"
|
||||||
@@ -264,6 +271,7 @@ onMounted(load)
|
|||||||
:items="wuensche"
|
:items="wuensche"
|
||||||
:ich="ich"
|
:ich="ich"
|
||||||
:host="host"
|
:host="host"
|
||||||
|
:angemeldet="angemeldet"
|
||||||
@changed="ladeWuensche"
|
@changed="ladeWuensche"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -347,6 +355,7 @@ onMounted(load)
|
|||||||
:series="series"
|
:series="series"
|
||||||
:mein-vorschlag="meinVorschlag"
|
:mein-vorschlag="meinVorschlag"
|
||||||
:host="host"
|
:host="host"
|
||||||
|
:angemeldet="angemeldet"
|
||||||
:gesehen="gesehen.some((e) => e.movie_id === selected.id)"
|
:gesehen="gesehen.some((e) => e.movie_id === selected.id)"
|
||||||
:gemerkt="merkliste.some((e) => e.mine && e.movie_id === selected.id)"
|
:gemerkt="merkliste.some((e) => e.mine && e.movie_id === selected.id)"
|
||||||
@close="selected = null"
|
@close="selected = null"
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { posterUrl } from '../images'
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
items: { type: Array, default: () => [] },
|
items: { type: Array, default: () => [] },
|
||||||
|
// Eine eigene Sammlung hat nur, wer in der Runde steht
|
||||||
|
angemeldet: Boolean,
|
||||||
ich: { type: Object, default: null },
|
ich: { type: Object, default: null },
|
||||||
gesehenIds: { type: Set, default: () => new Set() },
|
gesehenIds: { type: Set, default: () => new Set() },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ const props = defineProps({
|
|||||||
series: { type: Array, default: () => [] },
|
series: { type: Array, default: () => [] },
|
||||||
meinVorschlag: { type: Object, default: null },
|
meinVorschlag: { type: Object, default: null },
|
||||||
host: { type: Boolean, default: false },
|
host: { type: Boolean, default: false },
|
||||||
|
// Ohne gewählten Namen gibt es keine Sammlung und keinen Vorschlag
|
||||||
|
angemeldet: { type: Boolean, default: false },
|
||||||
gesehen: { type: Boolean, default: false },
|
gesehen: { type: Boolean, default: false },
|
||||||
gemerkt: { type: Boolean, default: false },
|
gemerkt: { type: Boolean, default: false },
|
||||||
})
|
})
|
||||||
@@ -213,7 +215,7 @@ onUnmounted(() => {
|
|||||||
Ähnliche finden
|
Ähnliche finden
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="!host"
|
v-if="!host && angemeldet"
|
||||||
type="button"
|
type="button"
|
||||||
class="merken"
|
class="merken"
|
||||||
:class="{ an: gemerkt }"
|
:class="{ an: gemerkt }"
|
||||||
@@ -221,7 +223,7 @@ onUnmounted(() => {
|
|||||||
>
|
>
|
||||||
{{ gemerkt ? 'In Sammlung' : 'Zur Sammlung' }}
|
{{ gemerkt ? 'In Sammlung' : 'Zur Sammlung' }}
|
||||||
</button>
|
</button>
|
||||||
<template v-if="!host">
|
<template v-if="!host && angemeldet">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="merken"
|
class="merken"
|
||||||
@@ -235,6 +237,11 @@ onUnmounted(() => {
|
|||||||
Hat nicht geklappt.
|
Hat nicht geklappt.
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- Ohne Namen fehlen hier zwei Knöpfe — sonst sucht man
|
||||||
|
vergeblich, warum -->
|
||||||
|
<p v-if="!host && !angemeldet" class="hinweis">
|
||||||
|
Zum Merken und Vorschlagen wähle oben rechts deinen Namen.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="host || gesehen" class="gesehen">
|
<div v-if="host || gesehen" class="gesehen">
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ const props = defineProps({
|
|||||||
items: { type: Array, default: () => [] },
|
items: { type: Array, default: () => [] },
|
||||||
laden: Boolean,
|
laden: Boolean,
|
||||||
host: Boolean,
|
host: Boolean,
|
||||||
|
// Der Würfel zieht aus der eigenen Sammlung — die gibt es nur mit Namen
|
||||||
|
angemeldet: Boolean,
|
||||||
merkliste: { type: Array, default: () => [] },
|
merkliste: { type: Array, default: () => [] },
|
||||||
mine: { type: Number, default: null },
|
mine: { type: Number, default: null },
|
||||||
gesehenIds: { type: Set, default: () => new Set() },
|
gesehenIds: { type: Set, default: () => new Set() },
|
||||||
@@ -51,7 +53,7 @@ async function wuerfeln() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<aside class="liste">
|
<aside class="liste">
|
||||||
<div v-if="!host" class="sammlung-wuerfeln">
|
<div v-if="!host && angemeldet" class="sammlung-wuerfeln">
|
||||||
<div class="schalter">
|
<div class="schalter">
|
||||||
<button type="button" :class="{ an: nurHorror }" @click="nurHorror = true">
|
<button type="button" :class="{ an: nurHorror }" @click="nurHorror = true">
|
||||||
Horror ({{ anzahlHorror }})
|
Horror ({{ anzahlHorror }})
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import { computed, nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
|
|||||||
import { posterUrl } from '../images'
|
import { posterUrl } from '../images'
|
||||||
import { abonnieren } from '../live'
|
import { abonnieren } from '../live'
|
||||||
|
|
||||||
const props = defineProps({ host: Boolean })
|
const props = defineProps({
|
||||||
|
host: Boolean,
|
||||||
|
// Eine Wertung gehört einer Person — ohne Namen gäbe es keine
|
||||||
|
angemeldet: Boolean,
|
||||||
|
})
|
||||||
const emit = defineEmits(['open', 'changed'])
|
const emit = defineEmits(['open', 'changed'])
|
||||||
|
|
||||||
const items = ref([])
|
const items = ref([])
|
||||||
@@ -231,7 +235,11 @@ onUnmounted(() => {
|
|||||||
<span v-else class="von">Noch keine Wertung</span>
|
<span v-else class="von">Noch keine Wertung</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="wertung" @mouseleave="schwebt = { id: null, wert: 0 }">
|
<div
|
||||||
|
v-if="angemeldet"
|
||||||
|
class="wertung"
|
||||||
|
@mouseleave="schwebt = { id: null, wert: 0 }"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
v-for="s in STERNE"
|
v-for="s in STERNE"
|
||||||
:key="s"
|
:key="s"
|
||||||
@@ -248,7 +256,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<!-- Erst nach den Sternen: ein Text ohne Note ergäbe keine Wertung -->
|
<!-- Erst nach den Sternen: ein Text ohne Note ergäbe keine Wertung -->
|
||||||
<button
|
<button
|
||||||
v-if="item.mine"
|
v-if="item.mine && angemeldet"
|
||||||
type="button"
|
type="button"
|
||||||
class="link"
|
class="link"
|
||||||
@click="rezensionOeffnen(item)"
|
@click="rezensionOeffnen(item)"
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import VorschlagsListe from './VorschlagsListe.vue'
|
|||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
host: Boolean,
|
host: Boolean,
|
||||||
|
angemeldet: Boolean,
|
||||||
// Beides hält die App schon aktuell — auch nach einem Namenswechsel
|
// Beides hält die App schon aktuell — auch nach einem Namenswechsel
|
||||||
merkliste: { type: Array, default: () => [] },
|
merkliste: { type: Array, default: () => [] },
|
||||||
mine: { type: Number, default: null },
|
mine: { type: Number, default: null },
|
||||||
@@ -171,6 +172,7 @@ defineExpose({ neu: laden_ })
|
|||||||
:items="items"
|
:items="items"
|
||||||
:laden="laden"
|
:laden="laden"
|
||||||
:host="host"
|
:host="host"
|
||||||
|
:angemeldet="angemeldet"
|
||||||
:merkliste="merkliste"
|
:merkliste="merkliste"
|
||||||
:mine="mine"
|
:mine="mine"
|
||||||
:gesehen-ids="gesehenIds"
|
:gesehen-ids="gesehenIds"
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ const props = defineProps({
|
|||||||
items: { type: Array, default: () => [] },
|
items: { type: Array, default: () => [] },
|
||||||
ich: { type: Object, default: null },
|
ich: { type: Object, default: null },
|
||||||
host: Boolean,
|
host: Boolean,
|
||||||
|
// Ohne gewählten Namen bleibt der Reiter zum Mitlesen da
|
||||||
|
angemeldet: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['changed'])
|
const emit = defineEmits(['changed'])
|
||||||
@@ -115,7 +117,7 @@ function datum(iso) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="wuensche-tab">
|
<div class="wuensche-tab">
|
||||||
<div class="neu">
|
<div v-if="angemeldet" class="neu">
|
||||||
<textarea
|
<textarea
|
||||||
v-model="entwurf"
|
v-model="entwurf"
|
||||||
rows="2"
|
rows="2"
|
||||||
@@ -144,6 +146,7 @@ function datum(iso) {
|
|||||||
type="button"
|
type="button"
|
||||||
class="stimme"
|
class="stimme"
|
||||||
:class="{ an: w.voted, hat: w.votes > 0 }"
|
:class="{ an: w.voted, hat: w.votes > 0 }"
|
||||||
|
:disabled="!angemeldet"
|
||||||
:title="w.voted ? 'Stimme zurücknehmen' : 'Dafür stimmen'"
|
:title="w.voted ? 'Stimme zurücknehmen' : 'Dafür stimmen'"
|
||||||
@click="abstimmen(w)"
|
@click="abstimmen(w)"
|
||||||
>
|
>
|
||||||
@@ -201,7 +204,7 @@ function datum(iso) {
|
|||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</p>
|
</p>
|
||||||
<div class="notiz-neu">
|
<div v-if="angemeldet" class="notiz-neu">
|
||||||
<input
|
<input
|
||||||
v-model="notizEntwurf[w.id]"
|
v-model="notizEntwurf[w.id]"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -375,11 +378,15 @@ h3 {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stimme:hover {
|
.stimme:hover:not(:disabled) {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stimme:disabled {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.stimme.hat {
|
.stimme.hat {
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user