feat(feed): reshuffle button for the "surprise me" sort
The backend shuffle sort already accepts a seed param. Add a circular reshuffle button next to the sort control (shown only when shuffle is active) that re-rolls the seed and re-queries the feed; selecting shuffle also seeds a fresh order instead of the deterministic seed-0 one. The seed lives in FeedFilters but is intentionally kept out of the URL state.
This commit is contained in:
parent
ecea6b403c
commit
581faeaa34
5 changed files with 43 additions and 11 deletions
|
|
@ -8,6 +8,7 @@ import {
|
||||||
EyeOff,
|
EyeOff,
|
||||||
GripVertical,
|
GripVertical,
|
||||||
Pencil,
|
Pencil,
|
||||||
|
RefreshCw,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
@ -48,6 +49,9 @@ const SORT_IDS = [
|
||||||
|
|
||||||
const SHOW_IDS = ["unwatched", "in_progress", "all", "watched", "saved", "hidden"];
|
const SHOW_IDS = ["unwatched", "in_progress", "all", "watched", "saved", "hidden"];
|
||||||
|
|
||||||
|
// Fresh shuffle token for the "surprise me" sort.
|
||||||
|
const rollSeed = () => Math.floor(Math.random() * 1_000_000_000);
|
||||||
|
|
||||||
const DATE_PRESETS: { days: number; key: string }[] = [
|
const DATE_PRESETS: { days: number; key: string }[] = [
|
||||||
{ days: 1, key: "24h" },
|
{ days: 1, key: "24h" },
|
||||||
{ days: 7, key: "1week" },
|
{ days: 7, key: "1week" },
|
||||||
|
|
@ -215,9 +219,19 @@ export default function Sidebar({
|
||||||
);
|
);
|
||||||
case "sort":
|
case "sort":
|
||||||
return (
|
return (
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
<select
|
<select
|
||||||
value={filters.sort}
|
value={filters.sort}
|
||||||
onChange={(e) => setFilters({ ...filters, sort: e.target.value })}
|
onChange={(e) => {
|
||||||
|
const sort = e.target.value;
|
||||||
|
// Seed shuffle on selection so it lands on a fresh order, not the
|
||||||
|
// deterministic seed-0 one every time.
|
||||||
|
setFilters({
|
||||||
|
...filters,
|
||||||
|
sort,
|
||||||
|
seed: sort === "shuffle" ? rollSeed() : undefined,
|
||||||
|
});
|
||||||
|
}}
|
||||||
className="w-full bg-card border border-border rounded-lg px-2 py-1.5 text-sm outline-none focus:border-accent"
|
className="w-full bg-card border border-border rounded-lg px-2 py-1.5 text-sm outline-none focus:border-accent"
|
||||||
>
|
>
|
||||||
{SORT_IDS.map((id) => (
|
{SORT_IDS.map((id) => (
|
||||||
|
|
@ -226,6 +240,17 @@ export default function Sidebar({
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
{filters.sort === "shuffle" && (
|
||||||
|
<button
|
||||||
|
onClick={() => setFilters({ ...filters, seed: rollSeed() })}
|
||||||
|
title={t("sidebar.reshuffle")}
|
||||||
|
aria-label={t("sidebar.reshuffle")}
|
||||||
|
className="shrink-0 p-1.5 rounded-lg border border-border bg-card text-fg shadow-sm hover:border-accent hover:text-accent active:translate-y-px transition"
|
||||||
|
>
|
||||||
|
<RefreshCw className="w-4 h-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
case "date":
|
case "date":
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
"from": "Von",
|
"from": "Von",
|
||||||
"to": "Bis",
|
"to": "Bis",
|
||||||
"clearDates": "Daten löschen",
|
"clearDates": "Daten löschen",
|
||||||
|
"reshuffle": "Neu mischen",
|
||||||
"widget": {
|
"widget": {
|
||||||
"show": "Anzeigen",
|
"show": "Anzeigen",
|
||||||
"sort": "Sortierung",
|
"sort": "Sortierung",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
"from": "From",
|
"from": "From",
|
||||||
"to": "To",
|
"to": "To",
|
||||||
"clearDates": "clear dates",
|
"clearDates": "clear dates",
|
||||||
|
"reshuffle": "Reshuffle",
|
||||||
"widget": {
|
"widget": {
|
||||||
"show": "Show",
|
"show": "Show",
|
||||||
"sort": "Sort",
|
"sort": "Sort",
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
"from": "Ettől",
|
"from": "Ettől",
|
||||||
"to": "Eddig",
|
"to": "Eddig",
|
||||||
"clearDates": "dátumok törlése",
|
"clearDates": "dátumok törlése",
|
||||||
|
"reshuffle": "Újrakeverés",
|
||||||
"widget": {
|
"widget": {
|
||||||
"show": "Megjelenítés",
|
"show": "Megjelenítés",
|
||||||
"sort": "Rendezés",
|
"sort": "Rendezés",
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ export interface FeedFilters {
|
||||||
tagMode: "or" | "and";
|
tagMode: "or" | "and";
|
||||||
q: string;
|
q: string;
|
||||||
sort: string;
|
sort: string;
|
||||||
|
// Re-roll token for the "shuffle" sort; bumped by the reshuffle button so the
|
||||||
|
// feed re-queries with a fresh order. Ignored by every other sort.
|
||||||
|
seed?: number;
|
||||||
includeNormal: boolean;
|
includeNormal: boolean;
|
||||||
includeShorts: boolean;
|
includeShorts: boolean;
|
||||||
includeLive: boolean;
|
includeLive: boolean;
|
||||||
|
|
@ -153,6 +156,7 @@ function feedQuery(f: FeedFilters, offset: number, limit: number): string {
|
||||||
p.set("tag_mode", f.tagMode);
|
p.set("tag_mode", f.tagMode);
|
||||||
if (f.q) p.set("q", f.q);
|
if (f.q) p.set("q", f.q);
|
||||||
p.set("sort", f.sort);
|
p.set("sort", f.sort);
|
||||||
|
if (f.sort === "shuffle" && f.seed) p.set("seed", String(f.seed));
|
||||||
p.set("show_normal", String(f.includeNormal));
|
p.set("show_normal", String(f.includeNormal));
|
||||||
p.set("include_shorts", String(f.includeShorts));
|
p.set("include_shorts", String(f.includeShorts));
|
||||||
p.set("include_live", String(f.includeLive));
|
p.set("include_live", String(f.includeLive));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue