@@ -3,6 +3,7 @@ import { useState, type ChangeEvent, type ReactNode } from 'react';
33import type { ExtendedActionState } from '../api' ;
44import { Badge } from './Badge.tsx' ;
55import { Button } from './Button' ;
6+ import { Snackbar } from './Snackbar.tsx' ;
67import {
78 CheckIcon ,
89 ExclamationShieldIcon ,
@@ -14,6 +15,23 @@ import {
1415type ActionType = ExtendedActionState ;
1516
1617export type StylePreset = 'default' | 'x-dark' | 'x-light' | 'custom' ;
18+ export enum DisclaimerType {
19+ BLOCKED = 'blocked' ,
20+ UNKNOWN = 'unknown' ,
21+ }
22+
23+ export type Disclaimer =
24+ | {
25+ type : DisclaimerType . BLOCKED ;
26+ ignorable : boolean ;
27+ hidden : boolean ;
28+ onSkip : ( ) => void ;
29+ }
30+ | {
31+ type : DisclaimerType . UNKNOWN ;
32+ ignorable : boolean ;
33+ } ;
34+
1735const stylePresetClassMap : Record < StylePreset , string > = {
1836 default : 'dial-light' ,
1937 'x-dark' : 'x-dark' ,
@@ -28,7 +46,7 @@ interface LayoutProps {
2846 success ?: string | null ;
2947 websiteUrl ?: string | null ;
3048 websiteText ?: string | null ;
31- disclaimer ?: ReactNode ;
49+ disclaimer ?: Disclaimer | null ;
3250 type : ActionType ;
3351 title : string ;
3452 description : string ;
@@ -79,6 +97,72 @@ const Linkable = ({
7997 < div className = { className } > { children } </ div >
8098 ) ;
8199
100+ const DisclaimerBlock = ( {
101+ type,
102+ hidden,
103+ ignorable,
104+ onSkip,
105+ } : {
106+ type : DisclaimerType ;
107+ ignorable : boolean ;
108+ onSkip ?: ( ) => void ;
109+ hidden : boolean ;
110+ } ) => {
111+ if ( type === DisclaimerType . BLOCKED && ! hidden ) {
112+ return (
113+ < Snackbar variant = "error" >
114+ < p >
115+ This Action or it's origin has been flagged as an unsafe action,
116+ & has been blocked . If you believe this action has been blocked in
117+ error , please { ' ' }
118+ < a
119+ href = "https://discord.gg/saydialect"
120+ className = "cursor-pointer underline"
121+ target = "_blank"
122+ rel = "noopener noreferrer"
123+ >
124+ submit an issue
125+ </ a >
126+ .
127+ { ! ignorable &&
128+ ' Your action provider blocks execution of this action.' }
129+ </ p >
130+ { ignorable && onSkip && (
131+ < button
132+ className = "mt-3 font-semibold transition-colors hover:text-text-error-hover motion-reduce:transition-none"
133+ onClick = { onSkip }
134+ >
135+ Ignore warning & proceed
136+ </ button >
137+ ) }
138+ </ Snackbar >
139+ ) ;
140+ }
141+
142+ if ( type === DisclaimerType . UNKNOWN ) {
143+ return (
144+ < Snackbar variant = "warning" >
145+ < p >
146+ This Action has not yet been registered. Only use it if you trust the
147+ source. This Action will not unfurl on X until it is registered.
148+ { ! ignorable &&
149+ ' Your action provider blocks execution of this action.' }
150+ </ p >
151+ < a
152+ className = "mt-3 inline-block font-semibold transition-colors hover:text-text-warning-hover motion-reduce:transition-none"
153+ href = "https://discord.gg/saydialect"
154+ target = "_blank"
155+ rel = "noopener noreferrer"
156+ >
157+ Report
158+ </ a >
159+ </ Snackbar >
160+ ) ;
161+ }
162+
163+ return null ;
164+ } ;
165+
82166export const ActionLayout = ( {
83167 stylePreset = 'default' ,
84168 title,
@@ -119,13 +203,13 @@ export const ActionLayout = ({
119203 rel = "noopener noreferrer"
120204 >
121205 < LinkIcon className = "mr-2 text-icon-primary transition-colors group-hover:text-icon-primary-hover motion-reduce:transition-none" />
122- < span className = "text-text-link transition-colors group-hover:text-text-link-hover group-hover:underline motion-reduce:transition-none" >
206+ < span className = "text-text-link group-hover:text-text-link-hover transition-colors group-hover:underline motion-reduce:transition-none" >
123207 { websiteText ?? websiteUrl }
124208 </ span >
125209 </ a >
126210 ) }
127211 { websiteText && ! websiteUrl && (
128- < span className = "inline-flex items-center truncate text-subtext text-text-link " >
212+ < span className = "text-text-link inline-flex items-center truncate text-subtext" >
129213 { websiteText }
130214 </ span >
131215 ) }
@@ -163,7 +247,24 @@ export const ActionLayout = ({
163247 < span className = "mb-4 whitespace-pre-wrap text-subtext text-text-secondary" >
164248 { description }
165249 </ span >
166- { disclaimer && < div className = "mb-4" > { disclaimer } </ div > }
250+ { disclaimer && (
251+ < div className = "mb-4" >
252+ < DisclaimerBlock
253+ type = { disclaimer . type }
254+ ignorable = { disclaimer . ignorable }
255+ hidden = {
256+ disclaimer . type === DisclaimerType . BLOCKED
257+ ? disclaimer . hidden
258+ : false
259+ }
260+ onSkip = {
261+ disclaimer . type === DisclaimerType . BLOCKED
262+ ? disclaimer . onSkip
263+ : undefined
264+ }
265+ />
266+ </ div >
267+ ) }
167268 < ActionContent form = { form } inputs = { inputs } buttons = { buttons } />
168269 { success && (
169270 < span className = "mt-4 flex justify-center text-subtext text-text-success" >
@@ -259,7 +360,7 @@ const ActionInput = ({
259360 return (
260361 < div
261362 className = { clsx (
262- 'flex items-center gap-2 rounded-input border border-input-stroke transition-colors focus-within:border-input-stroke-selected motion-reduce:transition-none' ,
363+ 'border-input-stroke focus-within:border-input-stroke-selected flex items-center gap-2 rounded-input border transition-colors motion-reduce:transition-none' ,
263364 {
264365 'hover:border-input-stroke-hover hover:focus-within:border-input-stroke-selected' :
265366 ! disabled ,
@@ -271,7 +372,7 @@ const ActionInput = ({
271372 value = { value }
272373 disabled = { disabled }
273374 onChange = { extendedChange }
274- className = "my-3 ml-4 flex-1 truncate bg-input-bg text-text-input outline-none placeholder:text-text-input-placeholder disabled:text-text-input-disabled"
375+ className = "bg-input-bg text-text-input placeholder:text-text-input-placeholder disabled:text-text-input-disabled my-3 ml-4 flex-1 truncate outline-none "
275376 />
276377 { button && (
277378 < div className = "my-2 mr-2" >
0 commit comments