@farcaster/snap 1.15.3 → 1.15.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/components/action-button.d.ts +2 -1
- package/dist/react/components/action-button.js +16 -3
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +5 -5
- package/dist/react-native/components/snap-action-button.d.ts +1 -1
- package/dist/react-native/components/snap-action-button.js +19 -2
- package/dist/react-native/index.d.ts +3 -1
- package/dist/react-native/index.js +4 -2
- package/dist/ui/catalog.d.ts +1 -0
- package/dist/ui/catalog.js +5 -2
- package/package.json +1 -1
- package/src/react/components/action-button.tsx +25 -3
- package/src/react/index.tsx +8 -6
- package/src/react-native/components/snap-action-button.tsx +26 -4
- package/src/react-native/index.tsx +7 -3
- package/src/ui/catalog.ts +5 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export declare function SnapActionButton({ element
|
|
1
|
+
export declare function SnapActionButton({ element, emit, }: {
|
|
2
2
|
element: {
|
|
3
3
|
props: Record<string, unknown>;
|
|
4
|
+
on?: Record<string, unknown>;
|
|
4
5
|
};
|
|
5
6
|
emit: (name: string) => void;
|
|
6
7
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
+
import { ExternalLink } from "lucide-react";
|
|
4
5
|
import { Button } from "@neynar/ui/button";
|
|
5
6
|
import { cn } from "@neynar/ui/utils";
|
|
6
7
|
import { useSnapColors } from "../hooks/use-snap-colors.js";
|
|
7
8
|
import { ICON_MAP } from "./icon.js";
|
|
8
|
-
|
|
9
|
+
function isExternalLinkAction(on) {
|
|
10
|
+
if (!on)
|
|
11
|
+
return false;
|
|
12
|
+
const press = on.press;
|
|
13
|
+
if (!press || press.action !== "open_url")
|
|
14
|
+
return false;
|
|
15
|
+
return press.params?.isSnap !== true;
|
|
16
|
+
}
|
|
17
|
+
export function SnapActionButton({ element, emit, }) {
|
|
18
|
+
const { props } = element;
|
|
9
19
|
const label = String(props.label ?? "Action");
|
|
10
20
|
const variant = String(props.variant ?? "secondary");
|
|
11
21
|
const isPrimary = variant === "primary";
|
|
@@ -13,6 +23,7 @@ export function SnapActionButton({ element: { props }, emit, }) {
|
|
|
13
23
|
const colors = useSnapColors();
|
|
14
24
|
const [hovered, setHovered] = useState(false);
|
|
15
25
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
26
|
+
const showExternalIcon = isExternalLinkAction(element.on);
|
|
16
27
|
const style = isPrimary
|
|
17
28
|
? {
|
|
18
29
|
backgroundColor: hovered ? colors.accentHover : colors.accent,
|
|
@@ -20,9 +31,11 @@ export function SnapActionButton({ element: { props }, emit, }) {
|
|
|
20
31
|
borderColor: "transparent",
|
|
21
32
|
}
|
|
22
33
|
: {
|
|
23
|
-
backgroundColor: hovered
|
|
34
|
+
backgroundColor: hovered
|
|
35
|
+
? `color-mix(in srgb, ${colors.accent} 15%, transparent)`
|
|
36
|
+
: colors.muted,
|
|
24
37
|
color: colors.text,
|
|
25
38
|
borderColor: "transparent",
|
|
26
39
|
};
|
|
27
|
-
return (_jsx("div", { className: "w-full min-w-0 flex-1", children: _jsxs(Button, { type: "button", variant: isPrimary ? "default" : "secondary", className: cn("w-full gap-2"), style: style, onClick: () => emit("press"), onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), children: [Icon && _jsx(Icon, { size: 16 }), label] }) }));
|
|
40
|
+
return (_jsx("div", { className: "w-full min-w-0 flex-1", children: _jsxs(Button, { type: "button", variant: isPrimary ? "default" : "secondary", className: cn("w-full gap-2"), style: style, onClick: () => emit("press"), onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), children: [Icon && _jsx(Icon, { size: 16 }), label, showExternalIcon && (_jsx(ExternalLink, { size: 14, style: { opacity: 0.6 } }))] }) }));
|
|
28
41
|
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -12,7 +12,9 @@ export type SnapPage = {
|
|
|
12
12
|
};
|
|
13
13
|
export type SnapActionHandlers = {
|
|
14
14
|
submit: (target: string, inputs: Record<string, JsonValue>) => void;
|
|
15
|
-
open_url: (target: string
|
|
15
|
+
open_url: (target: string, options?: {
|
|
16
|
+
isSnap?: boolean;
|
|
17
|
+
}) => void;
|
|
16
18
|
open_mini_app: (target: string) => void;
|
|
17
19
|
view_cast: (params: {
|
|
18
20
|
hash: string;
|
package/dist/react/index.js
CHANGED
|
@@ -90,9 +90,7 @@ function SnapLoadingOverlay({ appearance, accentHex, active, }) {
|
|
|
90
90
|
zIndex: 10,
|
|
91
91
|
background: tint,
|
|
92
92
|
backdropFilter: active ? "blur(10px) saturate(1.05)" : "none",
|
|
93
|
-
WebkitBackdropFilter: active
|
|
94
|
-
? "blur(10px) saturate(1.05)"
|
|
95
|
-
: "none",
|
|
93
|
+
WebkitBackdropFilter: active ? "blur(10px) saturate(1.05)" : "none",
|
|
96
94
|
opacity: active ? 1 : 0,
|
|
97
95
|
pointerEvents: active ? "auto" : "none",
|
|
98
96
|
transition: "opacity 0.28s ease, backdrop-filter 0.28s ease",
|
|
@@ -182,7 +180,9 @@ export function SnapView({ snap, handlers, loading = false, appearance = "dark",
|
|
|
182
180
|
handlers.submit(String(p.target ?? ""), inputs);
|
|
183
181
|
break;
|
|
184
182
|
case "open_url":
|
|
185
|
-
handlers.open_url(String(p.target ?? "")
|
|
183
|
+
handlers.open_url(String(p.target ?? ""), {
|
|
184
|
+
isSnap: p.isSnap === true,
|
|
185
|
+
});
|
|
186
186
|
break;
|
|
187
187
|
case "open_mini_app":
|
|
188
188
|
handlers.open_mini_app(String(p.target ?? ""));
|
|
@@ -225,7 +225,7 @@ export function SnapView({ snap, handlers, loading = false, appearance = "dark",
|
|
|
225
225
|
break;
|
|
226
226
|
}
|
|
227
227
|
}, [handlers]);
|
|
228
|
-
return (_jsxs("div", { style: { position: "relative", width: "100%" }, children: [showConfetti && _jsx(ConfettiOverlay, {}, `confetti-${confettiEpochRef.current}`), _jsx(SnapLoadingOverlay, { appearance: appearance, accentHex: accentHex, active: loading }), _jsx("div", { style: previewSurfaceStyle, children: _jsx(SnapPreviewAccentProvider, { pageAccent: snap.theme?.accent, appearance: appearance, children: _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
|
|
228
|
+
return (_jsxs("div", { style: { position: "relative", width: "100%" }, children: [showConfetti && (_jsx(ConfettiOverlay, {}, `confetti-${confettiEpochRef.current}`)), _jsx(SnapLoadingOverlay, { appearance: appearance, accentHex: accentHex, active: loading }), _jsx("div", { style: previewSurfaceStyle, children: _jsx(SnapPreviewAccentProvider, { pageAccent: snap.theme?.accent, appearance: appearance, children: _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
|
|
229
229
|
applyStatePaths(stateRef.current, changes);
|
|
230
230
|
}, onAction: handleAction }, pageKey) }) })] }));
|
|
231
231
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { ComponentRenderProps } from "@json-render/react-native";
|
|
2
|
-
export declare function SnapActionButton({ element
|
|
2
|
+
export declare function SnapActionButton({ element, emit, }: ComponentRenderProps<Record<string, unknown>>): import("react").JSX.Element;
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Pressable, StyleSheet, Text, View } from "react-native";
|
|
3
|
+
import { ExternalLink } from "lucide-react-native";
|
|
3
4
|
import { useSnapPalette } from "../use-snap-palette.js";
|
|
4
5
|
import { useSnapTheme } from "../theme.js";
|
|
5
6
|
import { ICON_MAP } from "./snap-icon.js";
|
|
6
|
-
|
|
7
|
+
function isExternalLinkAction(on) {
|
|
8
|
+
if (!on)
|
|
9
|
+
return false;
|
|
10
|
+
const press = on.press;
|
|
11
|
+
if (!press || press.action !== "open_url")
|
|
12
|
+
return false;
|
|
13
|
+
return press.params?.isSnap !== true;
|
|
14
|
+
}
|
|
15
|
+
export function SnapActionButton({ element, emit, }) {
|
|
7
16
|
const { accentHex } = useSnapPalette();
|
|
8
17
|
const { colors } = useSnapTheme();
|
|
18
|
+
const { props } = element;
|
|
9
19
|
const label = String(props.label ?? "Action");
|
|
10
20
|
const variant = String(props.variant ?? "secondary");
|
|
11
21
|
const isPrimary = variant === "primary";
|
|
12
22
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
13
23
|
const textColor = isPrimary ? "#fff" : colors.text;
|
|
14
24
|
const iconColor = isPrimary ? "#fff" : colors.text;
|
|
25
|
+
const on = element.on;
|
|
26
|
+
const showExternalIcon = isExternalLinkAction(on);
|
|
15
27
|
return (_jsx(View, { style: styles.outer, children: _jsxs(Pressable, { style: ({ pressed }) => [
|
|
16
28
|
styles.btn,
|
|
17
29
|
isPrimary ? styles.btnDefault : styles.btnOther,
|
|
@@ -30,7 +42,12 @@ export function SnapActionButton({ element: { props }, emit, }) {
|
|
|
30
42
|
}
|
|
31
43
|
}
|
|
32
44
|
})();
|
|
33
|
-
}, children: [iconName && ICON_MAP[iconName]
|
|
45
|
+
}, children: [iconName && ICON_MAP[iconName]
|
|
46
|
+
? (() => {
|
|
47
|
+
const I = ICON_MAP[iconName];
|
|
48
|
+
return _jsx(I, { size: 16, color: iconColor });
|
|
49
|
+
})()
|
|
50
|
+
: null, _jsx(Text, { style: { color: textColor, fontSize: 14, fontWeight: "600" }, children: label }), showExternalIcon ? (_jsx(ExternalLink, { size: 14, color: iconColor, style: { opacity: 0.6 } })) : null] }) }));
|
|
34
51
|
}
|
|
35
52
|
const styles = StyleSheet.create({
|
|
36
53
|
outer: { flex: 1, minWidth: 0 },
|
|
@@ -14,7 +14,9 @@ export type SnapPage = {
|
|
|
14
14
|
};
|
|
15
15
|
export type SnapActionHandlers = {
|
|
16
16
|
submit: (target: string, inputs: Record<string, JsonValue>) => void;
|
|
17
|
-
open_url: (target: string
|
|
17
|
+
open_url: (target: string, options?: {
|
|
18
|
+
isSnap?: boolean;
|
|
19
|
+
}) => void;
|
|
18
20
|
open_mini_app: (target: string) => void;
|
|
19
21
|
view_cast: (params: {
|
|
20
22
|
hash: string;
|
|
@@ -103,7 +103,9 @@ function SnapViewInner({ snap, handlers, loading = false, }) {
|
|
|
103
103
|
h.submit(String(p.target ?? ""), inputs);
|
|
104
104
|
break;
|
|
105
105
|
case "open_url":
|
|
106
|
-
h.open_url(String(p.target ?? "")
|
|
106
|
+
h.open_url(String(p.target ?? ""), {
|
|
107
|
+
isSnap: p.isSnap === true,
|
|
108
|
+
});
|
|
107
109
|
break;
|
|
108
110
|
case "open_mini_app":
|
|
109
111
|
h.open_mini_app(String(p.target ?? ""));
|
|
@@ -149,7 +151,7 @@ function SnapViewInner({ snap, handlers, loading = false, }) {
|
|
|
149
151
|
{
|
|
150
152
|
backgroundColor: mode === "dark" ? "rgba(0,0,0,0.1)" : "rgba(255,255,255,0.2)",
|
|
151
153
|
},
|
|
152
|
-
], children: _jsx(ActivityIndicator, { size: "large", color: accentHex }) })) : null, showConfetti ? _jsx(ConfettiOverlay, {}, `confetti-${confettiEpochRef.current}`) : null, _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
|
|
154
|
+
], children: _jsx(ActivityIndicator, { size: "large", color: accentHex }) })) : null, showConfetti ? (_jsx(ConfettiOverlay, {}, `confetti-${confettiEpochRef.current}`)) : null, _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
|
|
153
155
|
applyStatePaths(stateRef.current, changes);
|
|
154
156
|
}, onAction: handleAction }, pageKey)] }));
|
|
155
157
|
}
|
package/dist/ui/catalog.d.ts
CHANGED
package/dist/ui/catalog.js
CHANGED
|
@@ -99,8 +99,11 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
99
99
|
params: z.object({ target: z.string() }),
|
|
100
100
|
},
|
|
101
101
|
open_url: {
|
|
102
|
-
description: "Open target
|
|
103
|
-
params: z.object({
|
|
102
|
+
description: "Open target snap or external URL.",
|
|
103
|
+
params: z.object({
|
|
104
|
+
target: z.string(),
|
|
105
|
+
isSnap: z.boolean().optional(),
|
|
106
|
+
}),
|
|
104
107
|
},
|
|
105
108
|
open_mini_app: {
|
|
106
109
|
description: "Open target URL as a Farcaster mini app.",
|
package/package.json
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useState } from "react";
|
|
4
|
+
import { ExternalLink } from "lucide-react";
|
|
4
5
|
import { Button } from "@neynar/ui/button";
|
|
5
6
|
import { cn } from "@neynar/ui/utils";
|
|
6
7
|
import { useSnapColors } from "../hooks/use-snap-colors";
|
|
7
8
|
import { ICON_MAP } from "./icon";
|
|
8
9
|
|
|
10
|
+
function isExternalLinkAction(
|
|
11
|
+
on: Record<string, unknown> | undefined,
|
|
12
|
+
): boolean {
|
|
13
|
+
if (!on) return false;
|
|
14
|
+
const press = on.press as
|
|
15
|
+
| { action?: string; params?: Record<string, unknown> }
|
|
16
|
+
| undefined;
|
|
17
|
+
if (!press || press.action !== "open_url") return false;
|
|
18
|
+
return press.params?.isSnap !== true;
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
export function SnapActionButton({
|
|
10
|
-
element
|
|
22
|
+
element,
|
|
11
23
|
emit,
|
|
12
24
|
}: {
|
|
13
|
-
element: {
|
|
25
|
+
element: {
|
|
26
|
+
props: Record<string, unknown>;
|
|
27
|
+
on?: Record<string, unknown>;
|
|
28
|
+
};
|
|
14
29
|
emit: (name: string) => void;
|
|
15
30
|
}) {
|
|
31
|
+
const { props } = element;
|
|
16
32
|
const label = String(props.label ?? "Action");
|
|
17
33
|
const variant = String(props.variant ?? "secondary");
|
|
18
34
|
const isPrimary = variant === "primary";
|
|
@@ -21,6 +37,7 @@ export function SnapActionButton({
|
|
|
21
37
|
const [hovered, setHovered] = useState(false);
|
|
22
38
|
|
|
23
39
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
40
|
+
const showExternalIcon = isExternalLinkAction(element.on);
|
|
24
41
|
|
|
25
42
|
const style = isPrimary
|
|
26
43
|
? {
|
|
@@ -29,7 +46,9 @@ export function SnapActionButton({
|
|
|
29
46
|
borderColor: "transparent",
|
|
30
47
|
}
|
|
31
48
|
: {
|
|
32
|
-
backgroundColor: hovered
|
|
49
|
+
backgroundColor: hovered
|
|
50
|
+
? `color-mix(in srgb, ${colors.accent} 15%, transparent)`
|
|
51
|
+
: colors.muted,
|
|
33
52
|
color: colors.text,
|
|
34
53
|
borderColor: "transparent",
|
|
35
54
|
};
|
|
@@ -47,6 +66,9 @@ export function SnapActionButton({
|
|
|
47
66
|
>
|
|
48
67
|
{Icon && <Icon size={16} />}
|
|
49
68
|
{label}
|
|
69
|
+
{showExternalIcon && (
|
|
70
|
+
<ExternalLink size={14} style={{ opacity: 0.6 }} />
|
|
71
|
+
)}
|
|
50
72
|
</Button>
|
|
51
73
|
</div>
|
|
52
74
|
);
|
package/src/react/index.tsx
CHANGED
|
@@ -34,7 +34,7 @@ export type SnapPage = {
|
|
|
34
34
|
|
|
35
35
|
export type SnapActionHandlers = {
|
|
36
36
|
submit: (target: string, inputs: Record<string, JsonValue>) => void;
|
|
37
|
-
open_url: (target: string) => void;
|
|
37
|
+
open_url: (target: string, options?: { isSnap?: boolean }) => void;
|
|
38
38
|
open_mini_app: (target: string) => void;
|
|
39
39
|
view_cast: (params: { hash: string }) => void;
|
|
40
40
|
view_profile: (params: { fid: number }) => void;
|
|
@@ -174,9 +174,7 @@ function SnapLoadingOverlay({
|
|
|
174
174
|
zIndex: 10,
|
|
175
175
|
background: tint,
|
|
176
176
|
backdropFilter: active ? "blur(10px) saturate(1.05)" : "none",
|
|
177
|
-
WebkitBackdropFilter: active
|
|
178
|
-
? "blur(10px) saturate(1.05)"
|
|
179
|
-
: "none",
|
|
177
|
+
WebkitBackdropFilter: active ? "blur(10px) saturate(1.05)" : "none",
|
|
180
178
|
opacity: active ? 1 : 0,
|
|
181
179
|
pointerEvents: active ? "auto" : "none",
|
|
182
180
|
transition: "opacity 0.28s ease, backdrop-filter 0.28s ease",
|
|
@@ -308,7 +306,9 @@ export function SnapView({
|
|
|
308
306
|
handlers.submit(String(p.target ?? ""), inputs);
|
|
309
307
|
break;
|
|
310
308
|
case "open_url":
|
|
311
|
-
handlers.open_url(String(p.target ?? "")
|
|
309
|
+
handlers.open_url(String(p.target ?? ""), {
|
|
310
|
+
isSnap: p.isSnap === true,
|
|
311
|
+
});
|
|
312
312
|
break;
|
|
313
313
|
case "open_mini_app":
|
|
314
314
|
handlers.open_mini_app(String(p.target ?? ""));
|
|
@@ -356,7 +356,9 @@ export function SnapView({
|
|
|
356
356
|
|
|
357
357
|
return (
|
|
358
358
|
<div style={{ position: "relative", width: "100%" }}>
|
|
359
|
-
{showConfetti &&
|
|
359
|
+
{showConfetti && (
|
|
360
|
+
<ConfettiOverlay key={`confetti-${confettiEpochRef.current}`} />
|
|
361
|
+
)}
|
|
360
362
|
<SnapLoadingOverlay
|
|
361
363
|
appearance={appearance}
|
|
362
364
|
accentHex={accentHex}
|
|
@@ -2,16 +2,29 @@ declare const __DEV__: boolean;
|
|
|
2
2
|
|
|
3
3
|
import type { ComponentRenderProps } from "@json-render/react-native";
|
|
4
4
|
import { Pressable, StyleSheet, Text, View } from "react-native";
|
|
5
|
+
import { ExternalLink } from "lucide-react-native";
|
|
5
6
|
import { useSnapPalette } from "../use-snap-palette";
|
|
6
7
|
import { useSnapTheme } from "../theme";
|
|
7
8
|
import { ICON_MAP } from "./snap-icon";
|
|
8
9
|
|
|
10
|
+
function isExternalLinkAction(
|
|
11
|
+
on: Record<string, unknown> | undefined,
|
|
12
|
+
): boolean {
|
|
13
|
+
if (!on) return false;
|
|
14
|
+
const press = on.press as
|
|
15
|
+
| { action?: string; params?: Record<string, unknown> }
|
|
16
|
+
| undefined;
|
|
17
|
+
if (!press || press.action !== "open_url") return false;
|
|
18
|
+
return press.params?.isSnap !== true;
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
export function SnapActionButton({
|
|
10
|
-
element
|
|
22
|
+
element,
|
|
11
23
|
emit,
|
|
12
24
|
}: ComponentRenderProps<Record<string, unknown>>) {
|
|
13
25
|
const { accentHex } = useSnapPalette();
|
|
14
26
|
const { colors } = useSnapTheme();
|
|
27
|
+
const { props } = element;
|
|
15
28
|
const label = String(props.label ?? "Action");
|
|
16
29
|
const variant = String(props.variant ?? "secondary");
|
|
17
30
|
const isPrimary = variant === "primary";
|
|
@@ -20,6 +33,9 @@ export function SnapActionButton({
|
|
|
20
33
|
const textColor = isPrimary ? "#fff" : colors.text;
|
|
21
34
|
const iconColor = isPrimary ? "#fff" : colors.text;
|
|
22
35
|
|
|
36
|
+
const on = (element as unknown as { on?: Record<string, unknown> }).on;
|
|
37
|
+
const showExternalIcon = isExternalLinkAction(on);
|
|
38
|
+
|
|
23
39
|
return (
|
|
24
40
|
<View style={styles.outer}>
|
|
25
41
|
<Pressable
|
|
@@ -43,12 +59,18 @@ export function SnapActionButton({
|
|
|
43
59
|
})();
|
|
44
60
|
}}
|
|
45
61
|
>
|
|
46
|
-
{iconName && ICON_MAP[iconName]
|
|
47
|
-
(() => {
|
|
48
|
-
|
|
62
|
+
{iconName && ICON_MAP[iconName]
|
|
63
|
+
? (() => {
|
|
64
|
+
const I = ICON_MAP[iconName]!;
|
|
65
|
+
return <I size={16} color={iconColor} />;
|
|
66
|
+
})()
|
|
67
|
+
: null}
|
|
49
68
|
<Text style={{ color: textColor, fontSize: 14, fontWeight: "600" }}>
|
|
50
69
|
{label}
|
|
51
70
|
</Text>
|
|
71
|
+
{showExternalIcon ? (
|
|
72
|
+
<ExternalLink size={14} color={iconColor} style={{ opacity: 0.6 }} />
|
|
73
|
+
) : null}
|
|
52
74
|
</Pressable>
|
|
53
75
|
</View>
|
|
54
76
|
);
|
|
@@ -36,7 +36,7 @@ export type SnapPage = {
|
|
|
36
36
|
|
|
37
37
|
export type SnapActionHandlers = {
|
|
38
38
|
submit: (target: string, inputs: Record<string, JsonValue>) => void;
|
|
39
|
-
open_url: (target: string) => void;
|
|
39
|
+
open_url: (target: string, options?: { isSnap?: boolean }) => void;
|
|
40
40
|
open_mini_app: (target: string) => void;
|
|
41
41
|
view_cast: (params: { hash: string }) => void;
|
|
42
42
|
view_profile: (params: { fid: number }) => void;
|
|
@@ -184,7 +184,9 @@ function SnapViewInner({
|
|
|
184
184
|
h.submit(String(p.target ?? ""), inputs);
|
|
185
185
|
break;
|
|
186
186
|
case "open_url":
|
|
187
|
-
h.open_url(String(p.target ?? "")
|
|
187
|
+
h.open_url(String(p.target ?? ""), {
|
|
188
|
+
isSnap: p.isSnap === true,
|
|
189
|
+
});
|
|
188
190
|
break;
|
|
189
191
|
case "open_mini_app":
|
|
190
192
|
h.open_mini_app(String(p.target ?? ""));
|
|
@@ -241,7 +243,9 @@ function SnapViewInner({
|
|
|
241
243
|
<ActivityIndicator size="large" color={accentHex} />
|
|
242
244
|
</View>
|
|
243
245
|
) : null}
|
|
244
|
-
{showConfetti ?
|
|
246
|
+
{showConfetti ? (
|
|
247
|
+
<ConfettiOverlay key={`confetti-${confettiEpochRef.current}`} />
|
|
248
|
+
) : null}
|
|
245
249
|
<SnapCatalogView
|
|
246
250
|
key={pageKey}
|
|
247
251
|
spec={spec}
|
package/src/ui/catalog.ts
CHANGED
|
@@ -117,8 +117,11 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
117
117
|
params: z.object({ target: z.string() }),
|
|
118
118
|
},
|
|
119
119
|
open_url: {
|
|
120
|
-
description: "Open target
|
|
121
|
-
params: z.object({
|
|
120
|
+
description: "Open target snap or external URL.",
|
|
121
|
+
params: z.object({
|
|
122
|
+
target: z.string(),
|
|
123
|
+
isSnap: z.boolean().optional(),
|
|
124
|
+
}),
|
|
122
125
|
},
|
|
123
126
|
open_mini_app: {
|
|
124
127
|
description: "Open target URL as a Farcaster mini app.",
|