PluginProbe
Code Snippets / trunk
Code Snippets vtrunk
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
← All changes | js/components/EditMenu/SnippetForm/SnippetForm.tsx +59 -5 4.0.0-beta.1 → trunk View file →
@@ -1,5 +1,5 @@
1 -import React, { useState } from 'react'
1 +import React, { useEffect, useRef, useState } from 'react'
2 2 import classnames from 'classnames'
3 3 import { __ } from '@wordpress/i18n'
4 4 import { WithRestAPIContext } from '../../../hooks/useRestAPI'
5 5 import { WithSnippetsAPIContext } from '../../../hooks/useSnippetsAPI'
@@ -8,8 +8,9 @@
8 8 import { handleUnknownError } from '../../../utils/errors'
9 9 import { createSnippetObject, getSnippetType, isCondition, validateSnippet } from '../../../utils/snippets/snippets'
10 10 import { buildUrl } from '../../../utils/urls'
11 11 import { ConfirmDialog } from '../../common/ConfirmDialog'
12 +import { ScreenMetaSlot } from '../../common/ScreenMetaSlot'
12 13 import { Toolbar } from '../../common/Toolbar'
13 14 import { UpsellBanner } from '../../common/UpsellBanner'
14 15 import { UpsellDialog } from '../../common/UpsellDialog'
15 16 import { EditorSidebar } from '../EditorSidebar'
@@ -95,9 +96,9 @@
95 96 .replace(__('Create New Snippet', 'code-snippets'), __('Edit Snippet', 'code-snippets'))
96 97 .replace(__('Create New Condition', 'code-snippets'), __('Edit Condition', 'code-snippets'))
97 98
98 99 const newUrl = buildUrl(window.CODE_SNIPPETS.urls.edit, { id: response.id })
99 - window.history.pushState({}, document.title, newUrl)
100 + window.history.replaceState({}, document.title, newUrl)
100 101 }
101 102 }
102 103 })
103 104 .then(refreshSnippetsList)
@@ -125,9 +126,11 @@
125 126 <form id="snippet-form" method="post" onSubmit={handleSubmit} className={className}>
126 127 {children}
127 128 </form>
128 129
129 - <ConfirmSubmitDialog {...{ doSubmit, submitAction, setSubmitAction, validationWarning, setValidationWarning }} />
130 + <ConfirmSubmitDialog
131 + {...{ doSubmit, submitAction, setSubmitAction, validationWarning, setValidationWarning }}
132 + />
130 133 </>
131 134 )
132 135 }
133 136
@@ -140,13 +143,61 @@
140 143 </div>
141 144 : null
142 145 }
143 146
147 +const useReloadOnPopState = (isDirty: boolean) => {
148 + const currentUrl = useRef(window.location.href)
149 + const skipNextUnloadPrompt = useRef(false)
150 +
151 + useEffect(() => {
152 + currentUrl.current = window.location.href
153 + })
154 +
155 + useEffect(() => {
156 + const handleBeforeUnload = (event: BeforeUnloadEvent) => {
157 + if (skipNextUnloadPrompt.current) {
158 + skipNextUnloadPrompt.current = false
159 + return
160 + }
161 +
162 + event.preventDefault()
163 + // Required by Chrome and Edge versions before 119.
164 + // eslint-disable-next-line @typescript-eslint/no-deprecated
165 + event.returnValue = true
166 + }
167 +
168 + if (isDirty) {
169 + window.addEventListener('beforeunload', handleBeforeUnload)
170 + }
171 +
172 + return () => window.removeEventListener('beforeunload', handleBeforeUnload)
173 + }, [isDirty])
174 +
175 + useEffect(() => {
176 + const handlePopState = () => {
177 + if (isDirty && !window.confirm(
178 + __('You have unsaved changes. Leave this page and discard them?', 'code-snippets')
179 + )) {
180 + window.history.pushState({}, document.title, currentUrl.current)
181 + return
182 + }
183 +
184 + skipNextUnloadPrompt.current = isDirty
185 + window.location.reload()
186 + }
187 +
188 + window.addEventListener('popstate', handlePopState)
189 + return () => window.removeEventListener('popstate', handlePopState)
190 + }, [isDirty])
191 +}
192 +
144 193 const EditFormWrap: React.FC = () => {
145 - const { snippet, isReadOnly } = useSnippetForm()
194 + const { snippet, isReadOnly, isDirty } = useSnippetForm()
146 195 const [isExpanded, setIsExpanded] = useState(false)
147 196 const [isUpgradeDialogOpen, setIsUpgradeDialogOpen] = useState(false)
148 197
198 + useReloadOnPopState(isDirty)
199 +
149 200 return (
150 201 <>
151 202 <PageHeading />
152 203 <Notices placement="above-form" />
@@ -179,10 +230,13 @@
179 230 export const SnippetForm: React.FC = () =>
180 231 <WithRestAPIContext>
181 232 <WithSnippetsAPIContext>
182 233 <WithSnippetsListContext>
183 - <WithSnippetFormContext initialSnippet={() => createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}>
234 + <WithSnippetFormContext
235 + initialSnippet={() => createSnippetObject(window.CODE_SNIPPETS_EDIT?.snippet)}
236 + >
184 237 <Toolbar />
238 + <ScreenMetaSlot />
185 239 <EditFormWrap />
186 240 </WithSnippetFormContext>
187 241 </WithSnippetsListContext>
188 242 </WithSnippetsAPIContext>