Ein Namensfeld für alles, oben rechts
Bisher fragten Vorschlag und Wertung getrennt nach dem Namen — einmal in der Filmansicht, einmal über der Liste. Jetzt steht er einmal in der Reiterleiste und gilt für beides. Wer keinen einträgt, bleibt anonym. Dazu: make stream geht jetzt an den Server und beendet vorher ein laufendes OBS. Der lokale Weg heißt make stream-local. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013XVnRop5c3or3B8xE2hpgZ
This commit is contained in:
@@ -224,6 +224,13 @@ const gesehenZahl = computed(
|
||||
|
||||
// Wer den Abend leitet, darf drehen und die Runde leeren. Kein Login —
|
||||
// die Tastenkombination reicht, die Seite steht ohnehin nur Freunden offen.
|
||||
// Ein Name für alles: Vorschläge und Wertungen. Ohne Namen läuft es
|
||||
// anonym, das ist ausdrücklich erlaubt.
|
||||
const NAME_SCHLUESSEL = 'filmabend_name'
|
||||
const name = ref(localStorage.getItem(NAME_SCHLUESSEL) || '')
|
||||
|
||||
watch(name, (wert) => localStorage.setItem(NAME_SCHLUESSEL, wert))
|
||||
|
||||
const HOST_SCHLUESSEL = 'filmabend_host'
|
||||
const host = ref(localStorage.getItem(HOST_SCHLUESSEL) === '1')
|
||||
|
||||
@@ -389,6 +396,13 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur))
|
||||
{{ tab.label }}
|
||||
<span v-if="tab.count !== null" class="count">{{ tab.count }}</span>
|
||||
</button>
|
||||
<input
|
||||
v-model.trim="name"
|
||||
type="text"
|
||||
class="name"
|
||||
placeholder="Dein Name"
|
||||
maxlength="40"
|
||||
/>
|
||||
<span v-if="host" class="host">Host</span>
|
||||
</nav>
|
||||
|
||||
@@ -405,6 +419,7 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur))
|
||||
<WatchedTab
|
||||
v-else-if="activeTab === 'watched'"
|
||||
:host="host"
|
||||
:name="name"
|
||||
@open="selected = $event"
|
||||
@changed="ladeGesehen"
|
||||
/>
|
||||
@@ -466,6 +481,7 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur))
|
||||
@open="selected = $event"
|
||||
:mein-vorschlag="meinVorschlag"
|
||||
:host="host"
|
||||
:name="name"
|
||||
:gesehen="gesehen.some((e) => e.movie_id === selected.id)"
|
||||
@suggested="nachVorschlag"
|
||||
@watched="ladeGesehen"
|
||||
@@ -512,9 +528,15 @@ onUnmounted(() => window.removeEventListener('keydown', tastatur))
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
|
||||
.tabs .name {
|
||||
width: 11rem;
|
||||
margin: 0 0 0.35rem auto;
|
||||
padding: 0.35rem 0.6rem;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.host {
|
||||
align-self: center;
|
||||
margin-left: auto;
|
||||
padding: 0.15rem 0.5rem;
|
||||
color: var(--accent);
|
||||
font-size: 0.7rem;
|
||||
|
||||
@@ -7,33 +7,35 @@ const props = defineProps({
|
||||
series: { type: Array, default: () => [] },
|
||||
meinVorschlag: { type: Object, default: null },
|
||||
host: { type: Boolean, default: false },
|
||||
name: { type: String, default: '' },
|
||||
gesehen: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'open', 'suggested', 'watched'])
|
||||
|
||||
const vorschlagen = ref({ zustand: 'bereit', name: '' })
|
||||
const vorschlagen = ref({ zustand: 'bereit' })
|
||||
|
||||
const schonVorgeschlagen = computed(
|
||||
() => props.meinVorschlag?.movie_id === props.movie.id
|
||||
)
|
||||
|
||||
async function abschicken() {
|
||||
vorschlagen.value = { ...vorschlagen.value, zustand: 'laeuft' }
|
||||
vorschlagen.value = { zustand: 'laeuft' }
|
||||
try {
|
||||
const resp = await fetch('/api/suggestions', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
movie_id: props.movie.id,
|
||||
name: vorschlagen.value.name || null,
|
||||
// Der Name steht oben rechts, einmal für alles
|
||||
name: props.name || null,
|
||||
}),
|
||||
})
|
||||
if (!resp.ok) throw new Error()
|
||||
vorschlagen.value = { ...vorschlagen.value, zustand: 'fertig' }
|
||||
vorschlagen.value = { zustand: 'fertig' }
|
||||
emit('suggested')
|
||||
} catch {
|
||||
vorschlagen.value = { ...vorschlagen.value, zustand: 'fehler' }
|
||||
vorschlagen.value = { zustand: 'fehler' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,12 +147,6 @@ onUnmounted(() => {
|
||||
Du hast „{{ meinVorschlag.title }}“ vorgeschlagen. Ein neuer
|
||||
Vorschlag ersetzt ihn.
|
||||
</span>
|
||||
<input
|
||||
v-model="vorschlagen.name"
|
||||
type="text"
|
||||
placeholder="Dein Name (optional)"
|
||||
maxlength="40"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
:disabled="vorschlagen.zustand === 'laeuft'"
|
||||
@@ -361,12 +357,6 @@ h2 {
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.vorschlag input {
|
||||
width: 11rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.vorschlag button {
|
||||
padding: 0.45rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { posterUrl } from '../images'
|
||||
import { abonnieren } from '../live'
|
||||
|
||||
const props = defineProps({ host: Boolean })
|
||||
const props = defineProps({ host: Boolean, name: { type: String, default: '' } })
|
||||
const emit = defineEmits(['open', 'changed'])
|
||||
|
||||
const items = ref([])
|
||||
@@ -16,10 +16,6 @@ const sichtbar = computed(() =>
|
||||
const fehler = ref('')
|
||||
const laden = ref(true)
|
||||
|
||||
// Der Name gilt für alle Bewertungen und überlebt das Neuladen
|
||||
const NAME_SCHLUESSEL = 'filmabend_name'
|
||||
const name = ref(localStorage.getItem(NAME_SCHLUESSEL) || '')
|
||||
|
||||
const STERNE = [1, 2, 3, 4, 5]
|
||||
|
||||
// Beim Schweben füllen sich alle Sterne bis zum Zeiger, wie gewohnt
|
||||
@@ -45,14 +41,13 @@ async function laden_() {
|
||||
}
|
||||
|
||||
async function bewerten(item, sterne) {
|
||||
localStorage.setItem(NAME_SCHLUESSEL, name.value)
|
||||
// Sofort anzeigen, der Live-Kanal liefert gleich die richtigen Zahlen
|
||||
item.mine = sterne
|
||||
try {
|
||||
await fetch(`/api/watched/${item.id}/rating`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ stars: sterne, name: name.value || null }),
|
||||
body: JSON.stringify({ stars: sterne, name: props.name || null }),
|
||||
})
|
||||
} catch {
|
||||
fehler.value = 'Bewerten hat nicht geklappt.'
|
||||
@@ -114,16 +109,6 @@ onUnmounted(() => abbestellen?.())
|
||||
|
||||
<template>
|
||||
<div class="watched-tab">
|
||||
<div class="kopf">
|
||||
<input
|
||||
v-model="name"
|
||||
type="text"
|
||||
class="name"
|
||||
placeholder="Dein Name (optional)"
|
||||
maxlength="40"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p v-if="fehler" class="note error">{{ fehler }}</p>
|
||||
<p v-else-if="!laden && !sichtbar.length" class="note">
|
||||
Noch kein Film geschaut.
|
||||
@@ -223,19 +208,6 @@ onUnmounted(() => abbestellen?.())
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.kopf {
|
||||
display: flex;
|
||||
gap: 0.7rem;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.name {
|
||||
width: 14rem;
|
||||
padding: 0.55rem 0.8rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
|
||||
Reference in New Issue
Block a user