mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-09-10 03:41:52 +02:00
feat(client): offer the request a response was sent from
When the selected response's version no longer matches the live request, the response header grows a "Request Changed" control with View Diff and Restore. It follows the GraphQL editor's shape — a state-labelled dropdown button rather than a new icon in the action row — because the label is the whole point: it has to say *that* something is different before it offers to do anything about it. Hidden when the two agree, and for responses recorded before versioning existed, which have no version and never will. The diff reuses the Git dialog's DiffViewer over YAML renderings of the two documents, so a request diff reads like a commit diff. Restore flushes pending edits first (the backend restores from the database, so a debounced keystroke would otherwise land on top of it) and calls wasUpdatedExternally afterwards, because the write came from this window and the store's echo suppression would leave open editors showing the old content. EditSessionTracker turns the boundaries only the frontend can see — switching requests, losing focus, closing, falling idle for 60s — into snapshot calls. It tracks no content of its own: content addressing already decides whether a boundary had anything behind it, which is what keeps this a timer and two assignments instead of a change-tracking system.
This commit is contained in:
@@ -30,6 +30,7 @@ import { EmptyStateText } from "./EmptyStateText";
|
||||
import { ErrorBoundary } from "./ErrorBoundary";
|
||||
import { HttpResponseTimeline } from "./HttpResponseTimeline";
|
||||
import { RecentHttpResponsesDropdown } from "./RecentHttpResponsesDropdown";
|
||||
import { RequestVersionDropdown } from "./RequestVersionDropdown";
|
||||
import { RequestBodyViewer } from "./RequestBodyViewer";
|
||||
import { ResponseCookies } from "./ResponseCookies";
|
||||
import { ResponseHeaders } from "./ResponseHeaders";
|
||||
@@ -263,13 +264,14 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
<div className="justify-self-end shrink-0">
|
||||
<HStack space={1} className="justify-self-end shrink-0">
|
||||
<RequestVersionDropdown response={activeResponse} />
|
||||
<RecentHttpResponsesDropdown
|
||||
responses={responses}
|
||||
activeResponse={activeResponse}
|
||||
onPinnedResponseId={setPinnedResponseId}
|
||||
/>
|
||||
</div>
|
||||
</HStack>
|
||||
</div>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import type { HttpResponse, RequestVersionComparison } from "@yaakapp-internal/models";
|
||||
import { Icon } from "@yaakapp-internal/ui";
|
||||
import { stringify } from "yaml";
|
||||
import { useRequestVersion } from "../hooks/useRequestVersion";
|
||||
import { showDialog } from "../lib/dialog";
|
||||
import { restoreRequestVersion } from "../lib/restoreRequestVersion";
|
||||
import { Button } from "./core/Button";
|
||||
import { DiffViewer } from "./core/Editor/DiffViewer";
|
||||
import { Dropdown } from "./core/Dropdown";
|
||||
|
||||
interface Props {
|
||||
response: Pick<HttpResponse, "requestId" | "versionId">;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offers the request a response was sent from, when that is no longer the
|
||||
* request you have.
|
||||
*
|
||||
* Hidden while the two agree, which is the overwhelmingly common case and the
|
||||
* one where there is nothing to say. Responses recorded before versioning
|
||||
* existed have no version and stay quiet forever.
|
||||
*/
|
||||
export function RequestVersionDropdown({ response }: Props) {
|
||||
const comparison = useRequestVersion(response.versionId, response.requestId);
|
||||
if (comparison.data == null || !comparison.data.differs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
items={[
|
||||
{
|
||||
label: "View Diff",
|
||||
leftSlot: <Icon icon="git_branch" />,
|
||||
onSelect: () => showRequestVersionDiff(comparison.data!),
|
||||
},
|
||||
{
|
||||
label: "Restore This Version",
|
||||
leftSlot: <Icon icon="history" />,
|
||||
onSelect: () => restoreRequestVersion(comparison.data!.version),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Button
|
||||
size="2xs"
|
||||
variant="border"
|
||||
color="notice"
|
||||
className="font-sans"
|
||||
title="This request has changed since this response was sent"
|
||||
forDropdown
|
||||
>
|
||||
Request Changed
|
||||
</Button>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
function showRequestVersionDiff(comparison: RequestVersionComparison) {
|
||||
showDialog({
|
||||
id: "request-version-diff",
|
||||
title: "Request Changes Since This Response",
|
||||
size: "full",
|
||||
noPadding: true,
|
||||
render: () => (
|
||||
<div className="h-full flex flex-col px-4 pb-4">
|
||||
<DiffViewer
|
||||
original={toYaml(comparison.version.document)}
|
||||
modified={toYaml(comparison.currentDocument)}
|
||||
className="flex-1 min-h-0"
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/** Matches how the Git dialog renders a model for diffing. */
|
||||
function toYaml(document: unknown): string {
|
||||
return stringify(document, { indent: 2, lineWidth: 0 });
|
||||
}
|
||||
Reference in New Issue
Block a user