Skip to content

Commit fbaf530

Browse files
Add web favorite quick request UI
1 parent 85224fa commit fbaf530

File tree

1 file changed

+78
-29
lines changed

1 file changed

+78
-29
lines changed

src/routes/$slug/index.tsx

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ type PendingViewerRequestState = {
166166
action: "submit" | "remove";
167167
songId?: string;
168168
query?: string;
169-
requestMode?: "catalog" | "random" | "choice";
169+
requestMode?: "catalog" | "random" | "favorite" | "choice";
170170
requestKind?: "regular" | "vip";
171171
requestedPath?: RequestPathOption | null;
172172
itemId?: string;
@@ -1192,6 +1192,14 @@ function PublicChannelPage() {
11921192
replaceExisting: boolean;
11931193
itemId?: string;
11941194
}
1195+
| {
1196+
action: "submit";
1197+
requestMode: "favorite";
1198+
requestKind: "regular" | "vip";
1199+
vipTokenCost?: number;
1200+
replaceExisting: boolean;
1201+
itemId?: string;
1202+
}
11951203
) => {
11961204
const response = await fetch(`/api/channel/${slug}/viewer-request`, {
11971205
method: "POST",
@@ -1206,10 +1214,14 @@ function PublicChannelPage() {
12061214
requestMode: "catalog",
12071215
requestedPath: input.requestedPath,
12081216
}
1209-
: {
1210-
query: input.query,
1211-
requestMode: input.requestMode,
1212-
}),
1217+
: "query" in input
1218+
? {
1219+
query: input.query,
1220+
requestMode: input.requestMode,
1221+
}
1222+
: {
1223+
requestMode: input.requestMode,
1224+
}),
12131225
requestKind: input.requestKind,
12141226
vipTokenCost: input.vipTokenCost,
12151227
replaceExisting: input.replaceExisting,
@@ -1715,15 +1727,25 @@ function PublicChannelPage() {
17151727
replaceExisting={effectiveViewerReplaceExisting}
17161728
mutationIsPending={viewerRequestMutation.isPending}
17171729
pendingViewerRequest={pendingViewerRequest}
1718-
onSubmit={(query, requestMode, requestKind) =>
1719-
viewerRequestMutation.mutate({
1720-
action: "submit",
1721-
query,
1722-
requestMode,
1723-
requestKind,
1724-
replaceExisting: effectiveViewerReplaceExisting,
1725-
itemId: editingViewerRequest?.id,
1726-
})
1730+
onSubmit={(requestMode, requestKind, query) =>
1731+
viewerRequestMutation.mutate(
1732+
requestMode === "favorite"
1733+
? {
1734+
action: "submit",
1735+
requestMode,
1736+
requestKind,
1737+
replaceExisting: effectiveViewerReplaceExisting,
1738+
itemId: editingViewerRequest?.id,
1739+
}
1740+
: {
1741+
action: "submit",
1742+
query: query ?? "",
1743+
requestMode,
1744+
requestKind,
1745+
replaceExisting: effectiveViewerReplaceExisting,
1746+
itemId: editingViewerRequest?.id,
1747+
}
1748+
)
17271749
}
17281750
onCancelEdit={handleCancelViewerRequestEdit}
17291751
/>
@@ -2717,17 +2739,20 @@ function ViewerSpecialRequestControls(props: {
27172739
mutationIsPending: boolean;
27182740
pendingViewerRequest: PendingViewerRequestState;
27192741
onSubmit: (
2720-
query: string,
2721-
requestMode: "random" | "choice",
2722-
requestKind: "regular" | "vip"
2742+
requestMode: "random" | "favorite" | "choice",
2743+
requestKind: "regular" | "vip",
2744+
query?: string
27232745
) => void;
27242746
onCancelEdit: () => void;
27252747
}) {
27262748
const { t } = useLocaleTranslation("playlist");
27272749
const [artistQuery, setArtistQuery] = useState("");
2728-
const [requestMode, setRequestMode] = useState<"random" | "choice">("random");
2750+
const [requestMode, setRequestMode] = useState<
2751+
"random" | "favorite" | "choice"
2752+
>("random");
27292753
const [requestKind, setRequestKind] = useState<"regular" | "vip">("regular");
27302754
const normalizedQuery = artistQuery.trim();
2755+
const favoriteModeSelected = requestMode === "favorite";
27312756
const isViewerReady =
27322757
props.viewerStateLoading ||
27332758
props.viewerState != null ||
@@ -2767,17 +2792,22 @@ function ViewerSpecialRequestControls(props: {
27672792
});
27682793
const helperText =
27692794
selectedDisabledReason ||
2770-
(normalizedQuery.length >= 2
2771-
? requestMode === "random"
2772-
? t("specialRequest.randomHelp")
2773-
: t("specialRequest.choiceHelp")
2774-
: null);
2795+
(favoriteModeSelected
2796+
? t("specialRequest.favoriteHelp")
2797+
: normalizedQuery.length >= 2
2798+
? requestMode === "random"
2799+
? t("specialRequest.randomHelp")
2800+
: t("specialRequest.choiceHelp")
2801+
: null);
2802+
const pendingQuery = props.pendingViewerRequest?.query?.trim() ?? "";
27752803
const submitPending =
27762804
props.mutationIsPending &&
27772805
props.pendingViewerRequest?.action === "submit" &&
27782806
props.pendingViewerRequest.requestMode === requestMode &&
27792807
props.pendingViewerRequest.requestKind === requestKind &&
2780-
props.pendingViewerRequest.query?.trim() === normalizedQuery;
2808+
(favoriteModeSelected
2809+
? pendingQuery.length === 0
2810+
: pendingQuery === normalizedQuery);
27812811
const compactToggleClass =
27822812
"h-8 min-w-[4.5rem] px-2.5 text-[11px] tracking-[0.05em] shadow-none";
27832813

@@ -2813,10 +2843,15 @@ function ViewerSpecialRequestControls(props: {
28132843
</Label>
28142844
<Input
28152845
id="viewer-special-request-artist"
2816-
value={artistQuery}
2846+
value={favoriteModeSelected ? "" : artistQuery}
28172847
onChange={(event) => setArtistQuery(event.target.value)}
2818-
placeholder={t("specialRequest.artistPlaceholder")}
2848+
placeholder={
2849+
favoriteModeSelected
2850+
? t("specialRequest.favoritePlaceholder")
2851+
: t("specialRequest.artistPlaceholder")
2852+
}
28192853
className="h-9 px-3"
2854+
disabled={favoriteModeSelected}
28202855
/>
28212856
</div>
28222857

@@ -2845,6 +2880,16 @@ function ViewerSpecialRequestControls(props: {
28452880
>
28462881
{t("specialRequest.choice")}
28472882
</Button>
2883+
<Button
2884+
type="button"
2885+
size="sm"
2886+
variant={requestMode === "favorite" ? "secondary" : "ghost"}
2887+
className={cn(compactToggleClass, "w-auto")}
2888+
aria-pressed={requestMode === "favorite"}
2889+
onClick={() => setRequestMode("favorite")}
2890+
>
2891+
{t("specialRequest.favorite")}
2892+
</Button>
28482893
</div>
28492894
</div>
28502895

@@ -2882,7 +2927,11 @@ function ViewerSpecialRequestControls(props: {
28822927
variant="secondary"
28832928
className="h-9 min-w-[6.5rem] px-3 shadow-none"
28842929
onClick={() =>
2885-
props.onSubmit(normalizedQuery, requestMode, requestKind)
2930+
props.onSubmit(
2931+
requestMode,
2932+
requestKind,
2933+
favoriteModeSelected ? undefined : normalizedQuery
2934+
)
28862935
}
28872936
disabled={!!selectedDisabledReason || props.mutationIsPending}
28882937
>
@@ -3290,7 +3339,7 @@ function ManageSearchSongActions(props: {
32903339

32913340
function getViewerSpecialActionDisabledReason(input: {
32923341
query: string;
3293-
requestMode: "random" | "choice";
3342+
requestMode: "random" | "favorite" | "choice";
32943343
requestKind: "regular" | "vip";
32953344
requestsOpen?: boolean;
32963345
viewerState: ViewerRequestStateData["viewer"];
@@ -3299,7 +3348,7 @@ function getViewerSpecialActionDisabledReason(input: {
32993348
editingRequest: EnrichedPublicPlaylistItem | null;
33003349
t: (key: string, options?: Record<string, unknown>) => string;
33013350
}) {
3302-
if (input.query.length < 2) {
3351+
if (input.requestMode !== "favorite" && input.query.length < 2) {
33033352
return input.t("specialRequest.artistMin");
33043353
}
33053354

0 commit comments

Comments
 (0)