Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions ui/components/AnchorErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ export interface AnchorErrorBoundaryProps {
onError?: (error: AnchorKitError, errorInfo: React.ErrorInfo) => void;
/** Optional label shown in the default fallback (e.g. "Anchor Feed", "Price Widget") */
componentLabel?: string;
/** When any value in this array changes, the error state is automatically reset */
resetKeys?: unknown[];
}

interface State {
hasError: boolean;
error: AnchorKitError | null;
prevResetKeys: unknown[] | undefined;
}

// ─── Normalise any thrown value into an AnchorKitError ────────────────────────
Expand Down Expand Up @@ -225,11 +228,29 @@ export class AnchorErrorBoundary extends Component<
> {
constructor(props: AnchorErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
this.state = { hasError: false, error: null, prevResetKeys: props.resetKeys };
this.reset = this.reset.bind(this);
}

static getDerivedStateFromError(raw: unknown): State {
static getDerivedStateFromProps(
props: AnchorErrorBoundaryProps,
state: State,
): Partial<State> | null {
if (
state.hasError &&
props.resetKeys !== undefined &&
state.prevResetKeys !== undefined &&
props.resetKeys.some((key, i) => !Object.is(key, state.prevResetKeys![i]))
) {
return { hasError: false, error: null, prevResetKeys: props.resetKeys };
}
if (props.resetKeys !== state.prevResetKeys) {
return { prevResetKeys: props.resetKeys };
}
return null;
}

static getDerivedStateFromError(raw: unknown): Partial<State> {
return { hasError: true, error: normaliseError(raw) };
}

Expand Down
10 changes: 8 additions & 2 deletions ui/components/ApiRequestPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import './ApiRequestPanel.css';

export interface ApiRequestPanelProps {
Expand Down Expand Up @@ -35,6 +35,11 @@ export const ApiRequestPanel: React.FC<ApiRequestPanelProps> = ({
);
const [jsonError, setJsonError] = useState<string | null>(null);

useEffect(() => {
setEditableBody(requestBody ? formatJson(requestBody) : '');
setJsonError(null);
}, [requestBody]);

const validateJson = (value: string): boolean => {
if (!value.trim()) {
setJsonError(null);
Expand Down Expand Up @@ -74,7 +79,8 @@ export const ApiRequestPanel: React.FC<ApiRequestPanelProps> = ({

const generateCurl = (): string => {
const headerFlags = Object.entries(headers)
.map(([key, value]) => `-H "${key}: ${value}"`)
.map(([key, value]) => `-H "${key}: ${value.replace(/"/g, '\\"')}"`)

.join(' \\\n ');

let curl = `curl -X ${method} \\\n "${endpoint}"`;
Expand Down
6 changes: 2 additions & 4 deletions ui/hooks/useTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import { useEffect, useState } from "react";
* element to enable CSS custom property theming.
*/
export function useTheme(override?: boolean): boolean {
const [sysDark, setSysDark] = useState<boolean>(() => {
if (typeof window === "undefined") return false;
return window.matchMedia("(prefers-color-scheme: dark)").matches;
});
const [sysDark, setSysDark] = useState<boolean>(false);

const isDark = override !== undefined ? override : sysDark;

useEffect(() => {
const mq = window.matchMedia("(prefers-color-scheme: dark)");
setSysDark(mq.matches);
const handler = (e: MediaQueryListEvent) => setSysDark(e.matches);
mq.addEventListener("change", handler);
return () => mq.removeEventListener("change", handler);
Expand Down