diff --git a/package.json b/package.json index 3946837..644ce54 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@radix-ui/react-select": "^2.1.4", "@radix-ui/react-separator": "^1.1.1", "@radix-ui/react-slot": "^1.1.1", + "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", "@remixicon/react": "^4.6.0", "@solana/spl-token": "^0.4.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e95ac2..59ef399 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: '@radix-ui/react-slot': specifier: ^1.1.1 version: 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-switch': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-tooltip': specifier: ^1.1.6 version: 1.1.6(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -1747,6 +1750,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-switch@1.1.2': + resolution: {integrity: sha512-zGukiWHjEdBCRyXvKR6iXAQG6qXm2esuAD6kDOi9Cn+1X6ev3ASo4+CsYaD6Fov9r/AQFekqnD/7+V0Cs6/98g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tooltip@1.1.6': resolution: {integrity: sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==} peerDependencies: @@ -9337,6 +9353,21 @@ snapshots: optionalDependencies: '@types/react': 19.0.7 + '@radix-ui/react-switch@1.1.2(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.7)(react@19.0.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.7)(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.7 + '@types/react-dom': 19.0.3(@types/react@19.0.7) + '@radix-ui/react-tooltip@1.1.6(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/primitive': 1.1.1 diff --git a/prisma/migrations/20250120222231_user_degen_mode/migration.sql b/prisma/migrations/20250120222231_user_degen_mode/migration.sql new file mode 100644 index 0000000..a61e676 --- /dev/null +++ b/prisma/migrations/20250120222231_user_degen_mode/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "users" ADD COLUMN "degenMode" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7f86ac4..4089cf1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -15,6 +15,7 @@ model User { updatedAt DateTime @updatedAt earlyAccess Boolean @default(false) + degenMode Boolean @default(false) wallets Wallet[] conversations Conversation[] diff --git a/src/ai/providers.tsx b/src/ai/providers.tsx index ae3a0c2..eae4ab5 100644 --- a/src/ai/providers.tsx +++ b/src/ai/providers.tsx @@ -48,6 +48,7 @@ Confirmation Handling: 1. Always call the \`askForConfirmation\` tool to request explicit user confirmation. 2. STOP your response immediately after calling \`askForConfirmation\` without providing any additional information or context. 3. Wait for the user to explicitly confirm or reject the action in a separate response. + 4. Never ask for confirmation if the user has enabled \`degenMode\`. - Post-Confirmation Execution: - If the user confirms: 1. Only proceed with executing the tool in a new response after the confirmation. @@ -188,7 +189,7 @@ Your Task: Analyze the user's message and return the appropriate tools as a **JSON array of strings**. Rules: -- Only include the askForConfirmation tool if the user's message requires a transaction signature +- Only include the askForConfirmation tool if the user's message requires a transaction signature or if they are creating an action. - Only return the toolsets in the format: ["toolset1", "toolset2", ...]. - Do not add any text, explanations, or comments outside the array. - Be minimal — include only the toolsets necessary to handle the request. diff --git a/src/app/(user)/account/account-content.tsx b/src/app/(user)/account/account-content.tsx index 646f22b..08201db 100644 --- a/src/app/(user)/account/account-content.tsx +++ b/src/app/(user)/account/account-content.tsx @@ -1,5 +1,7 @@ 'use client'; +import { startTransition, useOptimistic } from 'react'; + import { useRouter } from 'next/navigation'; import { @@ -12,6 +14,8 @@ import { usePrivy, } from '@privy-io/react-auth'; import { useSolanaWallets } from '@privy-io/react-auth/solana'; +import { HelpCircle } from 'lucide-react'; +import { mutate } from 'swr'; import { WalletCard } from '@/components/dashboard/wallet-card'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; @@ -20,6 +24,13 @@ import { Card, CardContent } from '@/components/ui/card'; import { CopyableText } from '@/components/ui/copyable-text'; import { Label } from '@/components/ui/label'; import { Separator } from '@/components/ui/separator'; +import { Switch } from '@/components/ui/switch'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@/components/ui/tooltip'; import { useUser } from '@/hooks/use-user'; import { useEmbeddedWallets } from '@/hooks/use-wallets'; import { cn } from '@/lib/utils'; @@ -27,8 +38,10 @@ import { formatPrivyId, formatUserCreationDate, formatWalletAddress, + truncate, } from '@/lib/utils/format'; import { getUserID, grantDiscordRole } from '@/lib/utils/grant-discord-role'; +import { type UserUpdateData, updateUser } from '@/server/actions/user'; import { EmbeddedWallet } from '@/types/db'; import { LoadingStateSkeleton } from './loading-skeleton'; @@ -49,6 +62,16 @@ export function AccountContent() { unlinkWallet, } = useUser(); + const [optimisticUser, updateOptimisticUser] = useOptimistic( + { + degenMode: user?.degenMode || false, + }, + (state, update: UserUpdateData) => ({ + ...state, + ...update, + }), + ); + const { data: embeddedWallets = [], error: walletsError, @@ -121,6 +144,17 @@ export function AccountContent() { ...legacyWallets.map((w) => w.publicKey), ]; + const handleUpdateUser = async (data: UserUpdateData) => { + startTransition(() => { + updateOptimisticUser(data); + }); + + const result = await updateUser(data); + if (result.success) { + await mutate(`user-${userData.privyId}`); + } + }; + return (
+ Enable Degen Mode to skip confirmation prompts + for on-chain actions +
+Wallet
- {linkedSolanaWallet?.address - ? `${linkedSolanaWallet?.address}` - : 'Not connected'} + + {linkedSolanaWallet?.address + ? linkedSolanaWallet?.address + : 'Not connected'} + + + {linkedSolanaWallet?.address + ? truncate(linkedSolanaWallet?.address) + : 'Not connected'} +