Skip to content

Commit 0846aea

Browse files
committed
Keep submit action targets literal
1 parent 59921f9 commit 0846aea

4 files changed

Lines changed: 55 additions & 8 deletions

File tree

pkgs/snap/src/react-native/snap-view-core.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
hasPendingSnapAction,
3030
markSnapEffectsPresented,
3131
optionalSnapStringArray,
32-
resolveSnapActionParams,
32+
resolveSnapActionParamsForAction,
3333
validateSnapActionTargetUrl,
3434
type SnapRenderState,
3535
} from "../render-state";
@@ -50,6 +50,10 @@ function validActionTarget(value: unknown): string | undefined {
5050
return validateSnapActionTargetUrl(target) ? undefined : target;
5151
}
5252

53+
function literalStringParam(value: unknown): string {
54+
return typeof value === "string" ? value : "";
55+
}
56+
5357
function recordValue(value: unknown): Record<string, unknown> | undefined {
5458
return value && typeof value === "object" && !Array.isArray(value)
5559
? (value as Record<string, unknown>)
@@ -267,15 +271,15 @@ export function SnapViewCoreInner({
267271
);
268272

269273
const handleAction = useCallback((name: unknown, params: unknown) => {
270-
const p = resolveSnapActionParams(params, stateRef.current);
274+
const p = resolveSnapActionParamsForAction(name, params, stateRef.current);
271275
const inputs = (stateRef.current.inputs ?? {}) as Record<string, JsonValue>;
272276
const h = handlersRef.current;
273277
let result: unknown;
274278
setActionPending(name, p);
275279

276280
switch (name) {
277281
case "submit":
278-
result = h.submit(String(p.target ?? ""), inputs);
282+
result = h.submit(literalStringParam(p.target), inputs);
279283
break;
280284
case "open_url": {
281285
const target = validActionTarget(p.target);

pkgs/snap/src/react/snap-view-core.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
hasPendingSnapAction,
1717
markSnapEffectsPresented,
1818
optionalSnapStringArray,
19-
resolveSnapActionParams,
19+
resolveSnapActionParamsForAction,
2020
validateSnapActionTargetUrl,
2121
type SnapRenderState,
2222
} from "../render-state";
@@ -46,6 +46,10 @@ function validActionTarget(value: unknown): string | undefined {
4646
return validateSnapActionTargetUrl(target) ? undefined : target;
4747
}
4848

49+
function literalStringParam(value: unknown): string {
50+
return typeof value === "string" ? value : "";
51+
}
52+
4953
function recordValue(value: unknown): Record<string, unknown> | undefined {
5054
return value && typeof value === "object" && !Array.isArray(value)
5155
? (value as Record<string, unknown>)
@@ -551,7 +555,11 @@ export function SnapViewCore({
551555

552556
const handleAction = useCallback(
553557
(name: unknown, params: unknown) => {
554-
const p = resolveSnapActionParams(params, stateRef.current);
558+
const p = resolveSnapActionParamsForAction(
559+
name,
560+
params,
561+
stateRef.current,
562+
);
555563
const inputs = (stateRef.current.inputs ?? {}) as Record<
556564
string,
557565
JsonValue
@@ -561,7 +569,7 @@ export function SnapViewCore({
561569

562570
switch (name) {
563571
case "submit":
564-
result = handlers.submit(String(p.target ?? ""), inputs);
572+
result = handlers.submit(literalStringParam(p.target), inputs);
565573
break;
566574
case "open_url": {
567575
const target = validActionTarget(p.target);

pkgs/snap/src/render-state.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,29 @@ export function resolveSnapActionParams(
189189
params: unknown,
190190
state: SnapRenderState,
191191
): Record<string, unknown> {
192-
if (!isRecord(params)) return {};
192+
const actionParams = getSnapActionParams(params);
193193

194194
const resolved: Record<string, unknown> = {};
195-
for (const [key, value] of Object.entries(params)) {
195+
for (const [key, value] of Object.entries(actionParams)) {
196196
resolved[key] = resolveActionParam(value, { stateModel: state });
197197
}
198198
return resolved;
199199
}
200200

201+
export function getSnapActionParams(params: unknown): Record<string, unknown> {
202+
if (!isRecord(params)) return {};
203+
return cloneSnapRenderState(params);
204+
}
205+
206+
export function resolveSnapActionParamsForAction(
207+
actionName: unknown,
208+
params: unknown,
209+
state: SnapRenderState,
210+
): Record<string, unknown> {
211+
if (actionName === "submit") return getSnapActionParams(params);
212+
return resolveSnapActionParams(params, state);
213+
}
214+
201215
export function optionalSnapStringArray(value: unknown): string[] | undefined {
202216
if (Array.isArray(value)) return value.map((item) => String(item));
203217
return value ? [String(value)] : undefined;

pkgs/snap/tests/render-state.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
markSnapEffectsPresented,
1010
optionalSnapStringArray,
1111
resolveSnapActionParams,
12+
resolveSnapActionParamsForAction,
1213
type SnapRenderState,
1314
validateSnapActionTargetUrl,
1415
} from "../src/render-state";
@@ -142,6 +143,26 @@ describe("snap render state", () => {
142143
});
143144
});
144145

146+
it("keeps submit targets literal while resolving client action params", () => {
147+
const params = {
148+
target: { $state: "/inputs/target" },
149+
};
150+
const state: SnapRenderState = {
151+
inputs: {
152+
target: "https://example.com/submit",
153+
},
154+
};
155+
156+
expect(resolveSnapActionParamsForAction("submit", params, state)).toEqual({
157+
target: { $state: "/inputs/target" },
158+
});
159+
expect(
160+
resolveSnapActionParamsForAction("open_url", params, state),
161+
).toEqual({
162+
target: "https://example.com/submit",
163+
});
164+
});
165+
145166
it("normalizes dynamic compose_cast embeds that resolve to one string", () => {
146167
const resolved = resolveSnapActionParams(
147168
{

0 commit comments

Comments
 (0)