Skip to content

Commit

Permalink
⚡ (editor) Improve edges responsiveness
Browse files Browse the repository at this point in the history
Also fixed a ton of useEffects should make everything a bit more reactive.

Closes #307
  • Loading branch information
baptisteArno committed Feb 28, 2023
1 parent caf4086 commit f8f98ad
Show file tree
Hide file tree
Showing 24 changed files with 437 additions and 428 deletions.
4 changes: 3 additions & 1 deletion apps/builder/src/features/auth/api/getAuthenticatedUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ const authenticateByToken = async (
apiToken: string
): Promise<User | undefined> => {
if (typeof window !== 'undefined') return
return (await prisma.user.findFirst({
const user = (await prisma.user.findFirst({
where: { apiTokens: { some: { token: apiToken } } },
})) as User
setUser({ id: user.id, email: user.email ?? undefined })
return user
}

const extractBearerToken = (req: NextApiRequest) =>
Expand Down
1 change: 0 additions & 1 deletion apps/builder/src/features/billing/billing.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ test('plan changes should work', async ({ page }) => {
await expect(page.locator('text=$73.00 >> nth=0')).toBeVisible()
await expect(page.locator('text=$30.00 >> nth=0')).toBeVisible()
await expect(page.locator('text=$4.00 >> nth=0')).toBeVisible()
await expect(page.locator('text=user@email.com')).toBeVisible()
await addSubscriptionToWorkspace(
planChangeWorkspaceId,
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => {
</Stack>
<SwitchWithLabel
label={'Custom content?'}
moreInfoContent="By default, the email body will be a recap of what has been collected so far. You can override it with this option."
initialValue={options.isCustomBody ?? false}
onCheckChange={handleIsCustomBodyChange}
/>
Expand Down
2 changes: 1 addition & 1 deletion apps/builder/src/features/editor/components/EditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const EditorPage = () => {
{!isReadOnly && <BlocksSideBar />}
<GraphProvider isReadOnly={isReadOnly}>
<GroupsCoordinatesProvider groups={typebot.groups}>
<Graph flex="1" typebot={typebot} />
<Graph flex="1" typebot={typebot} key={typebot.id} />
<BoardMenuButton pos="absolute" right="40px" top="20px" />
<RightPanel />
</GroupsCoordinatesProvider>
Expand Down
186 changes: 71 additions & 115 deletions apps/builder/src/features/editor/hooks/useUndo.ts
Original file line number Diff line number Diff line change
@@ -1,152 +1,108 @@
import { isDefined } from '@udecode/plate-core'
import { dequal } from 'dequal'
// import { diff } from 'deep-object-diff'
import { useReducer, useCallback, useRef } from 'react'
import { isNotDefined } from 'utils'
import { useCallback, useRef, useState } from 'react'

enum ActionType {
Undo = 'UNDO',
Redo = 'REDO',
Set = 'SET',
Flush = 'FLUSH',
}

export interface Actions<T extends { updatedAt: Date } | undefined> {
set: (
newPresent: T | ((current: T) => T),
options?: { updateDate: boolean }
) => void
export interface Actions<T extends { updatedAt: Date }> {
set: (newPresent: T | ((current: T) => T) | undefined) => void
undo: () => void
redo: () => void
flush: () => void
canUndo: boolean
canRedo: boolean
presentRef: React.MutableRefObject<T>
}

interface Action<T extends { updatedAt: Date } | undefined> {
type: ActionType
newPresent?: T
updateDate?: boolean
}

export interface State<T extends { updatedAt: Date } | undefined> {
export interface History<T extends { updatedAt: Date }> {
past: T[]
present: T
present: T | undefined
future: T[]
}

const initialState = {
past: [],
present: null,
present: undefined,
future: [],
}

const reducer = <T extends { updatedAt: Date } | undefined>(
state: State<T>,
action: Action<T>
) => {
const { past, present, future } = state
export const useUndo = <T extends { updatedAt: Date }>(
initialPresent?: T
): [T | undefined, Actions<T>] => {
const [history, setHistory] = useState<History<T>>(initialState)
const presentRef = useRef<T | null>(initialPresent ?? null)

switch (action.type) {
case ActionType.Undo: {
if (past.length === 0 || !present) {
return state
}
const canUndo = history.past.length !== 0
const canRedo = history.future.length !== 0

const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)
const undo = useCallback(() => {
const { past, present, future } = history
if (past.length === 0 || !present) return

return {
past: newPast,
present: { ...previous, updatedAt: present.updatedAt },
future: [present, ...future],
}
}
const previous = past[past.length - 1]
const newPast = past.slice(0, past.length - 1)

case ActionType.Redo: {
if (future.length === 0) {
return state
}
const next = future[0]
const newFuture = future.slice(1)
const newPresent = { ...previous, updatedAt: present.updatedAt }

return {
past: [...past, present],
present: next,
future: newFuture,
}
}
setHistory({
past: newPast,
present: newPresent,
future: [present, ...future],
})
presentRef.current = newPresent
}, [history])

case ActionType.Set: {
const { newPresent } = action
const redo = useCallback(() => {
const { past, present, future } = history
if (future.length === 0) return
const next = future[0]
const newFuture = future.slice(1)

setHistory({
past: present ? [...past, present] : past,
present: next,
future: newFuture,
})
presentRef.current = next
}, [history])

const set = useCallback(
(newPresentArg: T | ((current: T) => T) | undefined) => {
const { past, present } = history
const newPresent =
typeof newPresentArg === 'function'
? newPresentArg(presentRef.current as T)
: newPresentArg
if (
isNotDefined(newPresent) ||
(present &&
dequal(
JSON.parse(JSON.stringify(newPresent)),
JSON.parse(JSON.stringify(present))
))
newPresent &&
present &&
dequal(
JSON.parse(JSON.stringify(newPresent)),
JSON.parse(JSON.stringify(present))
)
) {
return state
return
}

return {
if (newPresent === undefined) {
presentRef.current = null
setHistory(initialState)
return
}
setHistory({
past: [...past, present].filter(isDefined),
present: newPresent,
future: [],
}
}

case ActionType.Flush:
return { ...initialState, present }
}
}

const useUndo = <T extends { updatedAt: Date } | undefined>(
initialPresent: T
): [State<T>, Actions<T>] => {
const [state, dispatch] = useReducer(reducer, {
...initialState,
present: initialPresent,
})
const presentRef = useRef<T>(initialPresent)

const canUndo = state.past.length !== 0
const canRedo = state.future.length !== 0

const undo = useCallback(() => {
if (canUndo) {
dispatch({ type: ActionType.Undo })
}
}, [canUndo])

const redo = useCallback(() => {
if (canRedo) {
dispatch({ type: ActionType.Redo })
}
}, [canRedo])

const set = useCallback((newPresent: T | ((current: T) => T)) => {
const updatedTypebot =
newPresent && typeof newPresent === 'function'
? newPresent(presentRef.current)
: newPresent
presentRef.current = updatedTypebot
dispatch({
type: ActionType.Set,
newPresent: updatedTypebot,
})
}, [])
})
presentRef.current = newPresent
},
[history]
)

const flush = useCallback(() => {
dispatch({ type: ActionType.Flush })
setHistory({
present: presentRef.current ?? undefined,
past: [],
future: [],
})
}, [])

return [
state as State<T>,
{ set, undo, redo, flush, canUndo, canRedo, presentRef },
]
return [history.present, { set, undo, redo, flush, canUndo, canRedo }]
}

export default useUndo
Loading

4 comments on commit f8f98ad

@vercel
Copy link

@vercel vercel bot commented on f8f98ad Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs.typebot.io
docs-typebot-io.vercel.app
docs-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on f8f98ad Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on f8f98ad Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-typebot-io.vercel.app
builder-v2-git-main-typebot-io.vercel.app
app.typebot.io

@vercel
Copy link

@vercel vercel bot commented on f8f98ad Feb 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

vitamyway.com
am.nigerias.io
an.nigerias.io
app.yvon.earth
ar.nigerias.io
bot.enreso.org
bot.rslabs.pro
bots.bridge.ai
chat.hayuri.id
chicken.cr8.ai
gollum.riku.ai
gsbulletin.com
panther.cr7.ai
panther.cr8.ai
penguin.cr8.ai
talk.gocare.io
test.bot.gives
ticketfute.com
unicorn.cr8.ai
apo.nigerias.io
apr.nigerias.io
aso.nigerias.io
bot.ageenda.com
bot.artiweb.app
bot.devitus.com
bot.jesopizz.it
bot.reeplai.com
bot.scayver.com
bot.tc-mail.com
chat.lalmon.com
chat.sureb4.com
eventhub.com.au
fitness.riku.ai
games.klujo.com
sakuranembro.it
typebot.aloe.do
bot.contakit.com
bot.piccinato.co
bot.sv-energy.it
botc.ceox.com.br
clo.closeer.work
cockroach.cr8.ai
faqs.nigerias.io
feedback.ofx.one
form.syncwin.com
haymanevents.com
kw.wpwakanda.com
bbutton.wpwakanda.com
bot.coachayongzul.com
bot.digitalpointer.id
bot.eikju.photography
bot.incusservices.com
bot.meuesocial.com.br
help.atlasoutfittersk9.com
herbalife.barrettamario.it
homepageonly.wpwakanda.com
liveconvert.kandalearn.com
mainmenu1one.wpwakanda.com
tarian.theiofoundation.org
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
bot.pinpointinteractive.com
bot.polychromes-project.com
bot.seidinembroseanchetu.it
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
jcapp.virtuesocialmedia.com
liveconvert2.kandalearn.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
jc-app.virtuesocialmedia.com
piazzatorre.barrettamario.it
type.cookieacademyonline.com
bot.brigadeirosemdrama.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
viewer-v2-git-main-typebot-io.vercel.app

Please sign in to comment.