🚸 (editor) Add graph gesture notification
This commit is contained in:
33
apps/builder/src/components/Toaster.tsx
Normal file
33
apps/builder/src/components/Toaster.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { colors } from '@/lib/theme'
|
||||
import { useColorMode, useColorModeValue } from '@chakra-ui/react'
|
||||
import { Toaster as SonnerToaster } from 'sonner'
|
||||
|
||||
export const Toaster = () => {
|
||||
const { colorMode } = useColorMode()
|
||||
const theme = useColorModeValue(
|
||||
{
|
||||
bg: undefined,
|
||||
actionBg: colors.blue[500],
|
||||
actionColor: undefined,
|
||||
},
|
||||
{
|
||||
bg: colors.gray[900],
|
||||
actionBg: colors.blue[400],
|
||||
actionColor: 'white',
|
||||
}
|
||||
)
|
||||
return (
|
||||
<SonnerToaster
|
||||
theme={colorMode}
|
||||
toastOptions={{
|
||||
actionButtonStyle: {
|
||||
backgroundColor: theme.actionBg,
|
||||
color: theme.actionColor,
|
||||
},
|
||||
style: {
|
||||
backgroundColor: theme.bg,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
@ -643,3 +643,11 @@ export const XCircleIcon = (props: IconProps) => (
|
||||
<path d="m9 9 6 6" />
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export const LightBulbIcon = (props: IconProps) => (
|
||||
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
|
||||
<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5" />
|
||||
<path d="M9 18h6" />
|
||||
<path d="M10 22h4" />
|
||||
</Icon>
|
||||
)
|
||||
|
@ -2,7 +2,7 @@ import { signOut, useSession } from 'next-auth/react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { createContext, ReactNode, useEffect, useState } from 'react'
|
||||
import { isDefined, isNotDefined } from '@typebot.io/lib'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { User } from '@typebot.io/schemas'
|
||||
import { setUser as setSentryUser } from '@sentry/nextjs'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { updateUserQuery } from './queries/updateUserQuery'
|
||||
|
@ -17,25 +17,17 @@ import {
|
||||
SettingsIcon,
|
||||
} from '@/components/icons'
|
||||
import { useTypebot } from '../providers/TypebotProvider'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { EditorSettingsModal } from './EditorSettingsModal'
|
||||
import { parseDefaultPublicId } from '@/features/publish/helpers/parseDefaultPublicId'
|
||||
import { useTranslate } from '@tolgee/react'
|
||||
|
||||
export const BoardMenuButton = (props: FlexProps) => {
|
||||
const { query } = useRouter()
|
||||
const { typebot } = useTypebot()
|
||||
const { user } = useUser()
|
||||
const [isDownloading, setIsDownloading] = useState(false)
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
const { t } = useTranslate()
|
||||
|
||||
useEffect(() => {
|
||||
if (user && !user.graphNavigation && !query.isFirstBot) onOpen()
|
||||
}, [onOpen, query.isFirstBot, user, user?.graphNavigation])
|
||||
|
||||
const downloadFlow = () => {
|
||||
assert(typebot)
|
||||
setIsDownloading(true)
|
||||
@ -54,7 +46,7 @@ export const BoardMenuButton = (props: FlexProps) => {
|
||||
}
|
||||
|
||||
const redirectToDocumentation = () =>
|
||||
window.open('https://docs.typebot.io/get-started/overview', '_blank')
|
||||
window.open('https://docs.typebot.io/editor/graph', '_blank')
|
||||
|
||||
return (
|
||||
<Flex
|
||||
|
@ -29,6 +29,10 @@ import { useGroupsStore } from '../hooks/useGroupsStore'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { projectMouse } from '../helpers/projectMouse'
|
||||
import { useKeyboardShortcuts } from '@/hooks/useKeyboardShortcuts'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import { graphGestureNotficationKey } from '@typebot.io/schemas/features/user/constants'
|
||||
import { toast } from 'sonner'
|
||||
import { LightBulbIcon } from '@/components/icons'
|
||||
|
||||
const maxScale = 2
|
||||
const minScale = 0.3
|
||||
@ -55,6 +59,7 @@ export const Graph = ({
|
||||
setDraggedItem,
|
||||
} = useBlockDnd()
|
||||
const { pasteGroups, createGroup } = useTypebot()
|
||||
const { user, updateUser } = useUser()
|
||||
const {
|
||||
isReadOnly,
|
||||
setGraphPosition: setGlobalGraphPosition,
|
||||
@ -184,6 +189,26 @@ export const Graph = ({
|
||||
setLastMouseClickPosition(
|
||||
projectMouse({ x: e.clientX, y: e.clientY }, graphPosition)
|
||||
)
|
||||
} else if (
|
||||
user &&
|
||||
!user?.displayedInAppNotifications?.[graphGestureNotficationKey]
|
||||
) {
|
||||
toast.info('To move the graph using your mouse, hold the Space bar', {
|
||||
action: {
|
||||
label: 'More info',
|
||||
onClick: () => {
|
||||
window.open('https://docs.typebot.io/editor/graph', '_blank')
|
||||
},
|
||||
},
|
||||
duration: 30000,
|
||||
icon: <LightBulbIcon w="20px" h="20px" />,
|
||||
})
|
||||
updateUser({
|
||||
displayedInAppNotifications: {
|
||||
...user.displayedInAppNotifications,
|
||||
[graphGestureNotficationKey]: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
setSelectBoxCoordinates(undefined)
|
||||
setOpenedBlockId(undefined)
|
||||
|
@ -22,7 +22,7 @@ import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||
import { initPostHogIfEnabled } from '@/features/telemetry/posthog'
|
||||
import { TolgeeProvider, useTolgeeSSR } from '@tolgee/react'
|
||||
import { tolgee } from '@/lib/tolgee'
|
||||
import { Toaster } from 'sonner'
|
||||
import { Toaster } from '@/components/Toaster'
|
||||
|
||||
initPostHogIfEnabled()
|
||||
|
||||
@ -63,8 +63,8 @@ const App = ({ Component, pageProps }: AppProps) => {
|
||||
return (
|
||||
<TolgeeProvider tolgee={ssrTolgee}>
|
||||
<ToastContainer />
|
||||
<Toaster />
|
||||
<ChakraProvider theme={customTheme}>
|
||||
<Toaster />
|
||||
<SessionProvider session={pageProps.session}>
|
||||
<UserProvider>
|
||||
<TypebotProvider typebotId={typebotId}>
|
||||
|
@ -2,7 +2,7 @@ import prisma from '@typebot.io/lib/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import { methodNotAllowed, notAuthenticated } from '@typebot.io/lib/api'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { Prisma, User } from '@typebot.io/prisma'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
@ -18,6 +18,8 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
data: {
|
||||
...data,
|
||||
onboardingCategories: data.onboardingCategories ?? [],
|
||||
displayedInAppNotifications:
|
||||
data.displayedInAppNotifications ?? Prisma.DbNull,
|
||||
},
|
||||
})
|
||||
return res.send({ typebots })
|
||||
|
@ -64,15 +64,6 @@
|
||||
"group": "Get Started",
|
||||
"pages": ["get-started/introduction", "get-started/overview"]
|
||||
},
|
||||
{
|
||||
"group": "Workspace",
|
||||
"pages": [
|
||||
"workspace",
|
||||
"workspace/add-members-and-guests",
|
||||
"workspace/subscription",
|
||||
"workspace/preferences"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Flow",
|
||||
"pages": [
|
||||
@ -191,6 +182,15 @@
|
||||
"group": "Results",
|
||||
"pages": ["results/overview", "results/analytics"]
|
||||
},
|
||||
{
|
||||
"group": "Workspace",
|
||||
"pages": [
|
||||
"workspace",
|
||||
"workspace/add-members-and-guests",
|
||||
"workspace/subscription",
|
||||
"workspace/preferences"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Guides",
|
||||
"pages": [
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { graphGestureNotficationKey } from '@typebot.io/schemas/features/user/constants'
|
||||
|
||||
export const mockedUser: User = {
|
||||
id: 'userId',
|
||||
@ -13,4 +14,7 @@ export const mockedUser: User = {
|
||||
lastActivityAt: new Date('2022-01-01'),
|
||||
onboardingCategories: [],
|
||||
updatedAt: new Date('2022-01-01'),
|
||||
displayedInAppNotifications: {
|
||||
[graphGestureNotficationKey]: true,
|
||||
},
|
||||
}
|
||||
|
@ -144,6 +144,8 @@ export const updateUser = (data: Partial<User>) =>
|
||||
data: {
|
||||
...data,
|
||||
onboardingCategories: data.onboardingCategories ?? [],
|
||||
displayedInAppNotifications:
|
||||
data.displayedInAppNotifications ?? Prisma.DbNull,
|
||||
},
|
||||
where: {
|
||||
id: userId,
|
||||
|
@ -59,6 +59,7 @@ model User {
|
||||
workspaces MemberInWorkspace[]
|
||||
sessions Session[]
|
||||
bannedIps BannedIp[]
|
||||
displayedInAppNotifications Json?
|
||||
}
|
||||
|
||||
model ApiToken {
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "User" ADD COLUMN "displayedInAppNotifications" JSONB;
|
@ -55,6 +55,7 @@ model User {
|
||||
workspaces MemberInWorkspace[]
|
||||
sessions Session[]
|
||||
bannedIps BannedIp[]
|
||||
displayedInAppNotifications Json?
|
||||
}
|
||||
|
||||
model ApiToken {
|
||||
|
1
packages/schemas/features/user/constants.ts
Normal file
1
packages/schemas/features/user/constants.ts
Normal file
@ -0,0 +1 @@
|
||||
export const graphGestureNotficationKey = 'graphGestureNotification'
|
22
packages/schemas/features/user/schema.ts
Normal file
22
packages/schemas/features/user/schema.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { GraphNavigation, User as PrismaUser } from '@typebot.io/prisma'
|
||||
import { z } from '../../zod'
|
||||
|
||||
const displayedInAppNotificationsSchema = z.record(z.boolean())
|
||||
|
||||
export const userSchema = z.object({
|
||||
id: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
lastActivityAt: z.date(),
|
||||
name: z.string().nullable(),
|
||||
email: z.string().nullable(),
|
||||
emailVerified: z.date().nullable(),
|
||||
image: z.string().nullable(),
|
||||
company: z.string().nullable(),
|
||||
onboardingCategories: z.array(z.string()),
|
||||
graphNavigation: z.nativeEnum(GraphNavigation),
|
||||
preferredAppAppearance: z.string().nullable(),
|
||||
displayedInAppNotifications: displayedInAppNotificationsSchema.nullable(),
|
||||
}) satisfies z.ZodType<PrismaUser>
|
||||
|
||||
export type User = z.infer<typeof userSchema>
|
@ -13,3 +13,4 @@ export * from './features/workspace'
|
||||
export * from './features/items'
|
||||
export * from './features/analytics'
|
||||
export * from './features/events'
|
||||
export * from './features/user/schema'
|
||||
|
@ -18,6 +18,7 @@ const inspectUser = async () => {
|
||||
},
|
||||
select: {
|
||||
name: true,
|
||||
createdAt: true,
|
||||
lastActivityAt: true,
|
||||
company: true,
|
||||
onboardingCategories: true,
|
||||
|
Reference in New Issue
Block a user