From 0c970368645eaab76a9d985ee680b80f0d3a2d92 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Sun, 17 May 2026 08:58:55 -0700 Subject: [PATCH] Address cookie editing PR feedback --- apps/yaak-client/components/CookieDialog.tsx | 123 +++++++++++++----- .../components/core/KeyValueRow.tsx | 14 +- .../components/core/SettingRow.tsx | 1 + crates-tauri/yaak-app-client/src/ws_ext.rs | 30 ++++- packages/ui/src/components/Table.tsx | 6 +- 5 files changed, 132 insertions(+), 42 deletions(-) diff --git a/apps/yaak-client/components/CookieDialog.tsx b/apps/yaak-client/components/CookieDialog.tsx index b21c813b..cced7e8c 100644 --- a/apps/yaak-client/components/CookieDialog.tsx +++ b/apps/yaak-client/components/CookieDialog.tsx @@ -24,6 +24,7 @@ import { EventDetailHeader } from "./core/EventViewer"; import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; import { EmptyStateText } from "./EmptyStateText"; import { PlainInput } from "./core/PlainInput"; +import { Select } from "./core/Select"; import { showAlert } from "../lib/alert"; interface Props { @@ -38,6 +39,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { const [selectedCookieKey, setSelectedCookieKey] = useState(null); const [editingCookieKey, setEditingCookieKey] = useState(null); const [draftCookie, setDraftCookie] = useState(null); + const [draftExpiresInput, setDraftExpiresInput] = useState(""); const filteredCookies = useMemo(() => { return cookieJar?.cookies.filter((cookie) => cookieMatchesFilter(cookie, filter)) ?? []; }, [cookieJar?.cookies, filter]); @@ -56,6 +58,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { setSelectedCookieKey(null); setEditingCookieKey(NEW_COOKIE_KEY); setDraftCookie(newCookieDraft()); + setDraftExpiresInput(""); }; const handleEditCookie = () => { @@ -65,6 +68,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { setEditingCookieKey(cookieKey(selectedCookie)); setDraftCookie(selectedCookie); + setDraftExpiresInput(cookieExpiresInputValue(selectedCookie)); }; const handleCancelEdit = () => { @@ -73,6 +77,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { } setEditingCookieKey(null); setDraftCookie(null); + setDraftExpiresInput(""); }; const handleCloseDetails = () => { @@ -89,7 +94,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { return; } - const nextCookie = normalizeCookie(draftCookie); + let nextCookie = normalizeCookie(draftCookie); if (nextCookie.name.trim().length === 0) { showAlert({ id: "invalid-cookie-name", @@ -99,6 +104,20 @@ export const CookieDialog = ({ cookieJarId }: Props) => { return; } + if (nextCookie.expires !== "SessionEnd") { + const expires = cookieExpiresFromInput(draftExpiresInput); + if (expires == null) { + showAlert({ + id: "invalid-cookie-expires", + title: "Invalid Cookie", + body: "Cookie expiration must be a valid date.", + }); + return; + } + + nextCookie = { ...nextCookie, expires }; + } + const nextCookieKey = cookieKey(nextCookie); const nextCookies = cookieJar.cookies.filter((cookie) => { const key = cookieKey(cookie); @@ -112,6 +131,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { setSelectedCookieKey(nextCookieKey); setEditingCookieKey(null); setDraftCookie(null); + setDraftExpiresInput(""); }; if (cookieJar == null) { @@ -156,6 +176,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { layout="vertical" storageKey="cookie-dialog-details" defaultRatio={0.65} + className="-mx-2" minHeightPx={10} firstSlot={({ style }) => filteredCookies.length === 0 ? ( @@ -163,7 +184,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { No cookies match the current filter. ) : ( - +
Name @@ -185,6 +206,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { setSelectedCookieKey(null); setEditingCookieKey(null); setDraftCookie(null); + setDraftExpiresInput(""); patchModel(cookieJar, { cookies: [] }); }} /> @@ -208,9 +230,12 @@ export const CookieDialog = ({ cookieJarId }: Props) => { setSelectedCookieKey(key); setEditingCookieKey(null); setDraftCookie(null); + setDraftExpiresInput(""); }} > - {c.name} + + {c.name} + {c.value} @@ -231,7 +256,7 @@ export const CookieDialog = ({ cookieJarId }: Props) => { /> {c.sameSite} - + { if (editingCookieKey === key) { setEditingCookieKey(null); setDraftCookie(null); + setDraftExpiresInput(""); } patchModel(cookieJar, { cookies: cookieJar.cookies.filter( @@ -298,7 +324,12 @@ export const CookieDialog = ({ cookieJarId }: Props) => { onClose={handleCloseDetails} /> {isEditingCookie ? ( - + ) : ( )} @@ -354,17 +385,21 @@ function CookieDetails({ cookie }: { cookie: Cookie }) { function CookieEditor({ cookie, + expiresInputValue, onChange, + onExpiresInputChange, }: { cookie: Cookie; + expiresInputValue: string; onChange: (cookie: Cookie) => void; + onExpiresInputChange: (value: string) => void; }) { const sessionCookie = cookie.expires === "SessionEnd"; return (
- + onChange({ ...cookie, value })} /> - + onChange(cookieWithDomain(cookie, domain))} /> - + + onChange={(checked) => { + if (checked) { + onChange({ ...cookie, expires: "SessionEnd" }); + return; + } + + const expiresInput = + cookieExpiresFromInput(expiresInputValue) == null + ? defaultCookieExpiresInputValue() + : expiresInputValue; + + onExpiresInputChange(expiresInput); onChange({ ...cookie, - expires: checked - ? "SessionEnd" - : cookieExpiresFromInput(defaultCookieExpiresInputValue()), - }) - } + expires: cookieExpiresFromInput(expiresInput)!, + }); + }} /> onChange({ ...cookie, expires: cookieExpiresFromInput(value) })} + onChange={(value) => { + onExpiresInputChange(value); + + const expires = cookieExpiresFromInput(value); + if (expires != null) { + onChange({ ...cookie, expires }); + } + }} />
{cookieSize(cookie)} - + onChange({ ...cookie, httpOnly })} /> - + onChange({ ...cookie, secure })} /> - - + size="xs" + className="w-full" + options={[ + { label: "n/a", value: "" }, + { label: "Lax", value: "Lax" }, + { label: "Strict", value: "Strict" }, + { label: "None", value: "None" }, + ]} + onChange={(sameSite) => onChange({ ...cookie, - sameSite: - event.target.value === "" ? null : (event.target.value as Cookie["sameSite"]), + sameSite: sameSite === "" ? null : (sameSite as Cookie["sameSite"]), }) } - > - - - - - + /> @@ -454,9 +509,7 @@ function CookieEditor({ } function CookieKeyValueRow({ labelClassName, ...props }: ComponentProps) { - return ( - - ); + return ; } function CookieTextInput({ @@ -581,10 +634,10 @@ function defaultCookieExpiresInputValue() { return new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(); } -function cookieExpiresFromInput(value: string): Cookie["expires"] { +function cookieExpiresFromInput(value: string): Cookie["expires"] | null { const time = new Date(value).getTime(); if (!Number.isFinite(time)) { - return "SessionEnd"; + return null; } return { AtUtc: `${Math.floor(time / 1000)}` }; diff --git a/apps/yaak-client/components/core/KeyValueRow.tsx b/apps/yaak-client/components/core/KeyValueRow.tsx index f7b4dbd9..cd911131 100644 --- a/apps/yaak-client/components/core/KeyValueRow.tsx +++ b/apps/yaak-client/components/core/KeyValueRow.tsx @@ -34,6 +34,7 @@ interface KeyValueRowProps { children: ReactNode; rightSlot?: ReactNode; leftSlot?: ReactNode; + align?: "top" | "middle"; labelClassName?: string; labelColor?: "secondary" | "primary" | "info"; enableCopy?: boolean; @@ -45,6 +46,7 @@ export function KeyValueRow({ children, rightSlot, leftSlot, + align = "top", labelColor = "secondary", labelClassName, enableCopy, @@ -69,7 +71,9 @@ export function KeyValueRow({ <>
- tr:not(:last-child)>td]:border-b [&>tr:not(:last-child)>td]:border-b-surface-highlight", + "[&>tr:not(:last-child):not([data-table-spacer])>td]:border-b", + "[&>tr:not(:last-child):not([data-table-spacer])>td]:border-b-surface-highlight", )} > + + {children} );
{label} +
{leftSlot ?? } {children} diff --git a/apps/yaak-client/components/core/SettingRow.tsx b/apps/yaak-client/components/core/SettingRow.tsx index afc3e524..28a8732f 100644 --- a/apps/yaak-client/components/core/SettingRow.tsx +++ b/apps/yaak-client/components/core/SettingRow.tsx @@ -95,6 +95,7 @@ export function SettingRow({
); } + export function SettingValue({ actions, className, diff --git a/crates-tauri/yaak-app-client/src/ws_ext.rs b/crates-tauri/yaak-app-client/src/ws_ext.rs index 5ac1d913..936756cc 100644 --- a/crates-tauri/yaak-app-client/src/ws_ext.rs +++ b/crates-tauri/yaak-app-client/src/ws_ext.rs @@ -248,11 +248,16 @@ pub async fn cmd_ws_connect( } } - // Add cookies to WS HTTP Upgrade - if let (true, Some(id)) = (resolved_settings.send_cookies, cookie_jar_id) { - let cookie_jar = app_handle.db().get_cookie_jar(&id)?; - let store = CookieStore::from_cookies(cookie_jar.cookies); + let mut cookie_jar = + match (resolved_settings.send_cookies || resolved_settings.store_cookies, cookie_jar_id) { + (true, Some(id)) => Some(app_handle.db().get_cookie_jar(id)?), + _ => None, + }; + let cookie_store = + cookie_jar.as_ref().map(|jar| CookieStore::from_cookies(jar.cookies.clone())); + // Add cookies to WS HTTP Upgrade + if let (true, Some(store)) = (resolved_settings.send_cookies, cookie_store.as_ref()) { // Convert WS URL -> HTTP URL because our cookie store matches based on // Path/HttpOnly/Secure attributes even though WS upgrades are HTTP requests let http_url = convert_ws_url_to_http(&url); @@ -329,6 +334,23 @@ pub async fn cmd_ws_connect( }) .collect::>(); + if let (true, Some(cookie_jar), Some(store)) = + (resolved_settings.store_cookies, cookie_jar.as_mut(), cookie_store.as_ref()) + { + let set_cookie_headers = response + .headers() + .into_iter() + .filter(|(name, _)| name.as_str().eq_ignore_ascii_case("set-cookie")) + .filter_map(|(_, value)| value.to_str().ok().map(ToString::to_string)) + .collect::>(); + + if !set_cookie_headers.is_empty() { + store.store_cookies_from_response(&convert_ws_url_to_http(&url), &set_cookie_headers); + cookie_jar.cookies = store.get_all_cookies(); + app_handle.db().upsert_cookie_jar(cookie_jar, &UpdateSource::Background)?; + } + } + let connection = app_handle.db().upsert_websocket_connection( &WebsocketConnection { state: WebsocketConnectionState::Connected, diff --git a/packages/ui/src/components/Table.tsx b/packages/ui/src/components/Table.tsx index d06b6230..a0977a4e 100644 --- a/packages/ui/src/components/Table.tsx +++ b/packages/ui/src/components/Table.tsx @@ -34,9 +34,13 @@ export function TableBody({ children, className }: { children: ReactNode; classN
+