mirror of
https://fast.feibisi.com/https://github.com/parcelvoy/platform.git
synced 2025-08-28 11:46:02 +08:00
Adds check for unsaved changes (#599)
This commit is contained in:
parent
dd4a8528ca
commit
33119ef1d3
2 changed files with 50 additions and 12 deletions
|
@ -4,7 +4,7 @@ import './EmailEditor.css'
|
|||
import Button, { LinkButton } from '../../../ui/Button'
|
||||
import api from '../../../api'
|
||||
import { Campaign, Resource, Template } from '../../../types'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useBlocker, useNavigate } from 'react-router-dom'
|
||||
import { localeState } from '../CampaignDetail'
|
||||
import Modal from '../../../ui/Modal'
|
||||
import HtmlEditor from './HtmlEditor'
|
||||
|
@ -29,6 +29,7 @@ export default function EmailEditor() {
|
|||
const [template, setTemplate] = useState<Template | undefined>(templates[0])
|
||||
const [isSaving, setIsSaving] = useState(false)
|
||||
const [showConfig, setShowConfig] = useState(false)
|
||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
api.resources.all(project.id)
|
||||
|
@ -36,6 +37,19 @@ export default function EmailEditor() {
|
|||
.catch(() => setResources([]))
|
||||
}, [])
|
||||
|
||||
const blocker = useBlocker(
|
||||
({ currentLocation, nextLocation }) => hasUnsavedChanges && currentLocation.pathname !== nextLocation.pathname,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state !== 'blocked') return
|
||||
if (confirm(t('confirm_unsaved_changes'))) {
|
||||
blocker.proceed()
|
||||
} else {
|
||||
blocker.reset()
|
||||
}
|
||||
}, [blocker.state])
|
||||
|
||||
async function handleTemplateSave({ id, type, data }: Template) {
|
||||
setIsSaving(true)
|
||||
try {
|
||||
|
@ -46,10 +60,16 @@ export default function EmailEditor() {
|
|||
setCampaign(newCampaign)
|
||||
toast.success(t('template_saved'))
|
||||
} finally {
|
||||
setHasUnsavedChanges(false)
|
||||
setIsSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleTemplateChange = (change: SetStateAction<Template | undefined>) => {
|
||||
setHasUnsavedChanges(true)
|
||||
setTemplate(change)
|
||||
}
|
||||
|
||||
const campaignChange = (change: SetStateAction<Campaign>) => {
|
||||
setCampaign(change)
|
||||
}
|
||||
|
@ -94,7 +114,7 @@ export default function EmailEditor() {
|
|||
<Suspense key={template.id} fallback={null}>
|
||||
<VisualEditor
|
||||
template={template}
|
||||
setTemplate={setTemplate}
|
||||
setTemplate={handleTemplateChange}
|
||||
resources={resources}
|
||||
/>
|
||||
</Suspense>
|
||||
|
@ -102,7 +122,7 @@ export default function EmailEditor() {
|
|||
: <HtmlEditor
|
||||
template={template}
|
||||
key={template.id}
|
||||
setTemplate={setTemplate} />
|
||||
setTemplate={handleTemplateChange} />
|
||||
))
|
||||
}
|
||||
</section>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createElement, DragEventHandler, Fragment, memo, ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { createElement, DragEventHandler, Fragment, memo, ReactNode, SetStateAction, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useBlocker, useNavigate } from 'react-router-dom'
|
||||
import ReactFlow, {
|
||||
addEdge,
|
||||
Background,
|
||||
|
@ -493,14 +493,12 @@ export default function JourneyEditor() {
|
|||
const journeyId = journey.id
|
||||
|
||||
const loadSteps = useCallback(async () => {
|
||||
|
||||
const steps = await api.journeys.steps.get(project.id, journeyId)
|
||||
|
||||
const { edges, nodes } = stepsToNodes(steps)
|
||||
|
||||
setNodes(nodes)
|
||||
setEdges(edges)
|
||||
|
||||
}, [project, journeyId])
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -508,10 +506,29 @@ export default function JourneyEditor() {
|
|||
}, [loadSteps])
|
||||
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
|
||||
|
||||
const blocker = useBlocker(
|
||||
({ currentLocation, nextLocation }) => hasUnsavedChanges && currentLocation.pathname !== nextLocation.pathname,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (blocker.state !== 'blocked') return
|
||||
if (confirm(t('confirm_unsaved_changes'))) {
|
||||
blocker.proceed()
|
||||
} else {
|
||||
blocker.reset()
|
||||
}
|
||||
}, [blocker.state])
|
||||
|
||||
const handleSetNodes = (nodes: SetStateAction<Array<Node<any, string | undefined>>>) => {
|
||||
setHasUnsavedChanges(true)
|
||||
setNodes(nodes)
|
||||
}
|
||||
|
||||
const saveSteps = useCallback(async () => {
|
||||
|
||||
setSaving(true)
|
||||
|
||||
try {
|
||||
const stepMap = await api.journeys.steps.set(project.id, journey.id, nodesToSteps(nodes, edges))
|
||||
|
||||
|
@ -524,6 +541,7 @@ export default function JourneyEditor() {
|
|||
} catch (error: any) {
|
||||
toast.error(`Unable to save: ${error}`)
|
||||
} finally {
|
||||
setHasUnsavedChanges(false)
|
||||
setSaving(false)
|
||||
}
|
||||
}, [project, journey, nodes, edges])
|
||||
|
@ -580,7 +598,7 @@ export default function JourneyEditor() {
|
|||
},
|
||||
}
|
||||
|
||||
setNodes(nds => nds.concat(newStep))
|
||||
handleSetNodes(nds => nds.concat(newStep))
|
||||
|
||||
}, [setNodes, flowInstance, project, journey])
|
||||
|
||||
|
@ -658,7 +676,7 @@ export default function JourneyEditor() {
|
|||
label={t('name')}
|
||||
name="name"
|
||||
value={editNode.data.name ?? ''}
|
||||
onChange={name => setNodes(nds => nds.map(n => n.id === editNode.id ? { ...n, data: { ...n.data, name } } : n))}
|
||||
onChange={name => handleSetNodes(nds => nds.map(n => n.id === editNode.id ? { ...n, data: { ...n.data, name } } : n))}
|
||||
/>
|
||||
{
|
||||
type.hasDataKey && (
|
||||
|
@ -667,14 +685,14 @@ export default function JourneyEditor() {
|
|||
subtitle={t('data_key_description')}
|
||||
name="data_key"
|
||||
value={editNode.data.data_key}
|
||||
onChange={data_key => setNodes(nds => nds.map(n => n.id === editNode.id ? { ...n, data: { ...n.data, data_key } } : n))}
|
||||
onChange={data_key => handleSetNodes(nds => nds.map(n => n.id === editNode.id ? { ...n, data: { ...n.data, data_key } } : n))}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
type.Edit && createElement(type.Edit, {
|
||||
value: editNode.data.data ?? {},
|
||||
onChange: data => setNodes(nds => nds.map(n => n.id === editNode.id
|
||||
onChange: data => handleSetNodes(nds => nds.map(n => n.id === editNode.id
|
||||
? {
|
||||
...editNode,
|
||||
data: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue