diff --git a/package.json b/package.json
index 855840f8..b3a96ab9 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,6 @@
},
"dependencies": {
"@hocuspocus/provider": "2.0.6",
- "fast-deep-equal": "3.1.3",
"form-data": "4.0.0",
"i18next": "22.4.15",
"mailgun.js": "8.2.1",
@@ -101,6 +100,7 @@
"cookie-signature": "1.2.1",
"cosmiconfig-toml-loader": "1.0.0",
"cross-env": "7.0.3",
+ "debounce": "1.2.1",
"eslint": "8.40.0",
"eslint-config-stylelint": "18.0.0",
"eslint-import-resolver-typescript": "3.5.5",
@@ -111,6 +111,7 @@
"eslint-plugin-solid": "0.12.1",
"eslint-plugin-sonarjs": "0.19.0",
"eslint-plugin-unicorn": "47.0.0",
+ "fast-deep-equal": "3.1.3",
"graphql": "16.6.0",
"graphql-tag": "2.12.6",
"graphql-ws": "5.12.1",
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index a5a3ed40..c3e06da6 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -302,6 +302,8 @@
"Topic is supported by": "Topic is supported by",
"Topics": "Topics",
"Topics which supported by author": "Topics which supported by author",
+ "There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?": "There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?",
+ "There are unsaved changes in your profile settings. Are you sure you want to leave the page without saving?": "There are unsaved changes in your profile settings. Are you sure you want to leave the page without saving?",
"Try to find another way": "Try to find another way",
"Unfollow": "Unfollow",
"Unfollow the topic": "Unfollow the topic",
diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json
index 5612625c..b3e49787 100644
--- a/public/locales/ru/translation.json
+++ b/public/locales/ru/translation.json
@@ -319,6 +319,8 @@
"Topic is supported by": "Тему поддерживают",
"Topics": "Темы",
"Topics which supported by author": "Автор поддерживает темы",
+ "There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?": "В настройках публикации есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
+ "There are unsaved changes in your profile settings. Are you sure you want to leave the page without saving?": "В настройках вашего профиля есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
"Try to find another way": "Попробуйте найти по-другому",
"Unfollow": "Отписаться",
"Unfollow the topic": "Отписаться от темы",
diff --git a/src/components/Article/Article.module.scss b/src/components/Article/Article.module.scss
index 342db76f..42d0b498 100644
--- a/src/components/Article/Article.module.scss
+++ b/src/components/Article/Article.module.scss
@@ -1,11 +1,13 @@
h1 {
@include font-size(4rem);
+
line-height: 1.1;
margin-top: 0.5em;
}
h2 {
@include font-size(4rem);
+
line-height: 1.1;
}
@@ -44,7 +46,7 @@ img {
margin: 3.2rem 0;
position: relative;
- &:before {
+ &::before {
background: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNiAyMSIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0wIDEwLjUwMDFWMjFIMTAuNzA2MVYxMC41MDAxSDQuNTg4MzNDNC41ODgzMyA3LjE4NjU4IDcuMzI3NTggNC41MDAwOCAxMC43MDYxIDQuNTAwMDhWMEM0Ljc5Mjk3IDAgMCA0LjcwMDczIDAgMTAuNDk5OVYxMC41MDAxWk0yNiA0LjUwMDA4VjBDMjAuMDg2OSAwIDE1LjI5MzkgNC43MDA3MyAxNS4yOTM5IDEwLjQ5OTlWMjAuOTk5OUgyNlYxMC41MDA1SDE5Ljg4MjJDMTkuODgyNCA3LjE4NyAyMi42MjE3IDQuNTAwNSAyNiA0LjUwMDVWNC41MDAwOFoiIGZpbGw9ImJsYWNrIi8+PC9zdmc+')
no-repeat;
content: '';
@@ -60,17 +62,19 @@ img {
blockquote[data-type='quote'],
ta-quotation {
@include font-size(1.4rem);
+
border: solid #000;
border-width: 0 0 0 2px;
display: block;
font-weight: 500;
line-height: 1.6;
- margin: 1.6rem 0 0 calc(-8.33333% - 2px);
- padding: 0 0 0 8.33333%;
+ margin: 1.6rem 0 0 calc(-8.3333% - 2px);
+ padding: 0 0 0 8.3333%;
&[data-float='left'],
&[data-float='right'] {
@include font-size(2.2rem);
+
line-height: 1.4;
}
@@ -84,7 +88,7 @@ img {
}
}
- &:before {
+ &::before {
display: none;
}
}
@@ -95,13 +99,15 @@ img {
ta-border-sub {
background: #f1f2f3;
display: block;
+
@include font-size(1.4rem);
+
margin: 3.2rem 0;
padding: 3.2rem;
@include media-breakpoint-up(md) {
- margin: 3.2rem -8.33333%;
- padding: 3.2rem 8.33333%;
+ margin: 3.2rem -8.3333%;
+ padding: 3.2rem 8.3333%;
}
p:last-child {
@@ -144,7 +150,7 @@ img {
}
@include media-breakpoint-up(md) {
- margin: 0 8.33333% 3.2rem -16.66666%;
+ margin: 0 8.3333% 3.2rem -16.6666%;
}
}
@@ -154,7 +160,7 @@ img {
}
@include media-breakpoint-up(md) {
- margin: 0 -16.66666% 3.2rem 8.33333%;
+ margin: 0 -16.6666% 3.2rem 8.3333%;
}
}
@@ -168,13 +174,13 @@ img {
h2 {
@include media-breakpoint-up(xl) {
- margin-left: -16.6666666666%;
+ margin-left: -16.6666%;
}
}
:global(.img-align-left) {
float: left;
- margin: 1em 8.333333333% 0.5em 0;
+ margin: 1em 8.3333% 0.5em 0;
}
:global(.width-30) {
@@ -187,18 +193,18 @@ img {
:global(.img-align-left.width-50) {
@include media-breakpoint-up(xl) {
- margin-left: -16.6666666666%;
+ margin-left: -16.6666%;
}
}
:global(.img-align-right) {
float: right;
- margin: 1em 0 0.5em 8.333333333%;
+ margin: 1em 0 0.5em 8.3333%;
}
:global(.img-align-right.width-50) {
@include media-breakpoint-up(xl) {
- margin-right: -16.6666666666%;
+ margin-right: -16.6666%;
}
}
@@ -498,6 +504,7 @@ img {
button {
@include font-size(1.5rem);
+
border-radius: 0.8rem;
margin-right: 1.2rem;
padding: 0.9rem 1.2rem;
diff --git a/src/components/Article/Comment.tsx b/src/components/Article/Comment.tsx
index c8cc6028..6cfaf59b 100644
--- a/src/components/Article/Comment.tsx
+++ b/src/components/Article/Comment.tsx
@@ -17,7 +17,6 @@ import { useSnackbar } from '../../context/snackbar'
import { useConfirm } from '../../context/confirm'
import { Author, Reaction, ReactionKind } from '../../graphql/types.gen'
-
import { router } from '../../stores/router'
import styles from './Comment.module.scss'
@@ -48,6 +47,7 @@ export const Comment = (props: Props) => {
const {
actions: { showConfirm }
} = useConfirm()
+
const {
actions: { showSnackbar }
} = useSnackbar()
diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx
index 61940471..886119e0 100644
--- a/src/components/Article/FullArticle.tsx
+++ b/src/components/Article/FullArticle.tsx
@@ -132,7 +132,7 @@ export const FullArticle = (props: Props) => {
<>
{props.article.title}
-
+
{/*TODO: Check styles.shoutTopic*/}
@@ -212,7 +212,7 @@ export const FullArticle = (props: Props) => {
-
+
diff --git a/src/components/Author/Userpic/Userpic.tsx b/src/components/Author/Userpic/Userpic.tsx
index 57c205bd..077109a5 100644
--- a/src/components/Author/Userpic/Userpic.tsx
+++ b/src/components/Author/Userpic/Userpic.tsx
@@ -1,5 +1,4 @@
import { Show } from 'solid-js'
-import type { Author, User } from '../../../graphql/types.gen'
import styles from './Userpic.module.scss'
import { clsx } from 'clsx'
import { imageProxy } from '../../../utils/imageProxy'
diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx
index 56bb2514..7d8daf88 100644
--- a/src/components/Editor/Editor.tsx
+++ b/src/components/Editor/Editor.tsx
@@ -62,6 +62,7 @@ const providers: Record
= {}
export const Editor = (props: Props) => {
const { t } = useLocalize()
const { user } = useSession()
+
const [isCommonMarkup, setIsCommonMarkup] = createSignal(false)
const docName = `shout-${props.shoutId}`
@@ -247,7 +248,7 @@ export const Editor = (props: Props) => {
<>
(editorElRef.current = el)} id="editorBody" />
-
+
= 768px) {
padding-left: calc(21.9% + 3px);
max-width: 72.7%;
}
- @media (min-width: 1200px) {
+
+ @media (width >= 1200px) {
padding-left: calc(21.5% + 3px);
max-width: 64.9%;
}
@@ -38,32 +39,35 @@
.articleEditor figure,
.articleEditor .uploadedImage,
.articleEditor article[data-type='incut'] {
- @media (min-width: 768px) {
+ @media (width >= 768px) {
margin-left: calc(21.9% + 3px) !important;
max-width: 73.6%;
}
- @media (min-width: 1200px) {
+
+ @media (width >= 1200px) {
margin-left: calc(21.4% + 3px) !important;
max-width: 65.3%;
}
}
.articleEditor h2 {
- @media (min-width: 768px) {
+ @media (width >= 768px) {
padding-left: calc(21.9% + 2px);
max-width: 72.7%;
}
- @media (min-width: 1200px) {
+
+ @media (width >= 1200px) {
padding-left: 21.5%;
max-width: 87.1%;
}
}
.articleEditor h3 {
- @media (min-width: 768px) {
+ @media (width >= 768px) {
padding-left: calc(21.9% + 2px);
}
- @media (min-width: 1200px) {
+
+ @media (width >= 1200px) {
padding-left: 21.5%;
max-width: 87.1%;
}
@@ -73,7 +77,7 @@
.articleEditor * h2,
.articleEditor * h3,
.articleEditor * h4 {
- @media (min-width: 768px) {
+ @media (width >= 768px) {
padding-left: unset;
max-width: unset;
}
@@ -183,6 +187,7 @@ mark.highlight {
&[data-type='quote'] {
@include font-size(1.4rem);
+
border: solid #000;
border-width: 0 0 0 2px;
margin: 1.6rem 0;
@@ -204,7 +209,9 @@ mark.highlight {
&[data-type='punchline'] {
border: solid #000;
border-width: 2px 0;
+
@include font-size(3.2rem);
+
font-weight: 700;
line-height: 1.2;
margin: 1em 0;
@@ -213,6 +220,7 @@ mark.highlight {
&[data-float='left'],
&[data-float='right'] {
@include font-size(2.2rem);
+
line-height: 1.4;
}
@@ -230,7 +238,9 @@ mark.highlight {
.ProseMirror article[data-type='incut'] {
background: #f1f2f3;
+
@include font-size(1.4rem);
+
margin: 1em -1rem;
padding: 2em 2rem;
transition: background 0.3s ease-in-out;
diff --git a/src/components/Nav/ConfirmModal/ConfirmModal.module.scss b/src/components/Nav/ConfirmModal/ConfirmModal.module.scss
index 266b4adb..deb8f946 100644
--- a/src/components/Nav/ConfirmModal/ConfirmModal.module.scss
+++ b/src/components/Nav/ConfirmModal/ConfirmModal.module.scss
@@ -19,7 +19,6 @@
.confirmModalActions {
display: flex;
justify-content: space-between;
-
margin-top: 16px;
}
@@ -27,26 +26,23 @@
display: block;
width: 100%;
margin-right: 12px;
-
font-weight: 700;
-
margin-top: 32px;
padding: 1.6rem !important;
border: 1px solid black;
&:hover {
- background-color: rgba(0, 0, 0, 0.08);
+ background-color: rgb(0 0 0 / 8%);
}
}
.confirmModalButtonPrimary {
margin-right: 0;
-
background-color: black;
color: white;
border: none;
&:hover {
- background-color: rgba(0, 0, 0, 0.6);
+ background-color: rgb(0 0 0 / 60%);
}
}
diff --git a/src/components/Nav/Header.tsx b/src/components/Nav/Header.tsx
index 58135664..614597da 100644
--- a/src/components/Nav/Header.tsx
+++ b/src/components/Nav/Header.tsx
@@ -1,7 +1,6 @@
import { Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js'
-import { getPagePath } from '@nanostores/router'
+import { getPagePath, redirectPage } from '@nanostores/router'
import { clsx } from 'clsx'
-import { redirectPage } from '@nanostores/router'
import { Modal } from './Modal'
import { AuthModal } from './AuthModal'
diff --git a/src/components/TableOfContents/TableOfContents.module.scss b/src/components/TableOfContents/TableOfContents.module.scss
index 56ab8ace..c6f310a9 100644
--- a/src/components/TableOfContents/TableOfContents.module.scss
+++ b/src/components/TableOfContents/TableOfContents.module.scss
@@ -1,28 +1,26 @@
.TableOfContentsFixedWrapper {
- position: fixed;
- top: 150px;
- right: 20px;
-
+ position: absolute;
+ top: 0;
+ right: 0;
width: 281px;
+ min-height: 100%;
}
.TableOfContentsFixedWrapperLefted {
right: auto;
- left: 20px;
+ left: 70px;
}
.TableOfContentsContainer {
- position: absolute;
- right: 0;
- top: 0;
-
+ position: sticky;
+ top: 150px;
+ right: 20px;
display: flex;
width: 100%;
height: auto;
padding: 20px;
flex-direction: column;
align-items: flex-start;
-
background-color: transparent;
}
@@ -34,7 +32,6 @@
.TableOfContentsHeading {
margin: 0;
-
color: #000;
font-size: 22px;
font-style: normal;
@@ -46,20 +43,17 @@
position: absolute;
right: 20px;
top: 10px;
-
display: flex;
align-items: center;
justify-content: center;
-
width: 40px;
height: 40px;
-
background: transparent;
border: none;
cursor: pointer;
&:hover {
- box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 0 1px 1px rgb(0 0 0 / 30%);
}
}
@@ -70,18 +64,16 @@
.TableOfContentsHeadingsList {
position: relative;
-
display: flex;
flex-direction: column;
list-style-type: none;
-
margin: 0;
padding: 0 38px 0 0;
+ width: 100%;
}
.TableOfContentsHeadingsItem {
margin-top: 20px;
-
color: #000;
font-size: 14px;
font-style: normal;
@@ -91,7 +83,7 @@
letter-spacing: -0.14px;
&:hover {
- transform: scale(1.05);
+ color: rgb(0 0 0 / 50%);
}
}
diff --git a/src/components/TableOfContents/TableOfContents.tsx b/src/components/TableOfContents/TableOfContents.tsx
index a0b36dd2..7a3793cd 100644
--- a/src/components/TableOfContents/TableOfContents.tsx
+++ b/src/components/TableOfContents/TableOfContents.tsx
@@ -1,10 +1,12 @@
-import { onMount, For, Show, createSignal } from 'solid-js'
+import { For, Show, createSignal, createEffect, on } from 'solid-js'
import { clsx } from 'clsx'
import { DEFAULT_HEADER_OFFSET } from '../../stores/router'
import { useLocalize } from '../../context/localize'
+import { debounce } from 'debounce'
+
import { Icon } from '../_shared/Icon'
import styles from './TableOfContents.module.scss'
@@ -12,6 +14,7 @@ import styles from './TableOfContents.module.scss'
interface Props {
variant: 'article' | 'editor'
parentSelector: string
+ body: string
}
const scrollToHeader = (element) => {
@@ -30,21 +33,33 @@ export const TableOfContents = (props: Props) => {
const [headings, setHeadings] = createSignal([])
const [areHeadingsLoaded, setAreHeadingsLoaded] = createSignal(false)
- const [isVisible, setIsVisible] = createSignal(true)
+ const [isVisible, setIsVisible] = createSignal(props.variant === 'article')
const toggleIsVisible = () => {
setIsVisible((visible) => !visible)
}
- onMount(() => {
+ const updateHeadings = () => {
const { parentSelector } = props
+
// eslint-disable-next-line unicorn/prefer-spread
setHeadings(Array.from(document.querySelector(parentSelector).querySelectorAll('h2, h3, h4')))
-
setAreHeadingsLoaded(true)
- })
+ }
+
+ const debouncedUpdateHeadings = debounce(updateHeadings, 500)
+ createEffect(
+ on(
+ () => props.body,
+ () => debouncedUpdateHeadings()
+ )
+ )
return (
-
+ 2 : headings().length > 1)
+ }
+ >
{
})
}
- const [prevForm, setPrevForm] = createSignal
(clone(form))
+ const [prevForm, setPrevForm] = createStore(clone(form))
const [saving, setSaving] = createSignal(false)
const mediaItems: Accessor = createMemo(() => {
@@ -94,6 +90,20 @@ export const EditView = (props: Props) => {
})
})
+ onMount(() => {
+ // eslint-disable-next-line unicorn/consistent-function-scoping
+ const handleBeforeUnload = (event) => {
+ if (!deepEqual(prevForm, form)) {
+ event.returnValue = t(
+ `There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?`
+ )
+ }
+ }
+
+ window.addEventListener('beforeunload', handleBeforeUnload)
+ onCleanup(() => window.removeEventListener('beforeunload', handleBeforeUnload))
+ })
+
const handleTitleInputChange = (value) => {
setForm('title', value)
setForm('slug', slugify(value))
@@ -174,7 +184,7 @@ export const EditView = (props: Props) => {
const autoSaveRecursive = () => {
autoSaveTimeOutId = setTimeout(async () => {
- const hasChanges = !deepEqual(form, prevForm())
+ const hasChanges = !deepEqual(form, prevForm)
if (hasChanges) {
setSaving(true)
if (props.shout.visibility === 'owner') {
diff --git a/src/components/Views/PublishSettings/PublishSettings.tsx b/src/components/Views/PublishSettings/PublishSettings.tsx
index f2f6904d..61ab1d73 100644
--- a/src/components/Views/PublishSettings/PublishSettings.tsx
+++ b/src/components/Views/PublishSettings/PublishSettings.tsx
@@ -1,6 +1,6 @@
import { clsx } from 'clsx'
import styles from './PublishSettings.module.scss'
-import { createEffect, createSignal, onMount, Show } from 'solid-js'
+import { createSignal, onMount, Show } from 'solid-js'
import { TopicSelect, UploadModalContent } from '../../Editor'
import { Button } from '../../_shared/Button'
import { hideModal, showModal } from '../../../stores/ui'
diff --git a/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx b/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx
index d5e70faf..452cb032 100644
--- a/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx
+++ b/src/components/_shared/GrowingTextarea/GrowingTextarea.tsx
@@ -1,7 +1,6 @@
import { clsx } from 'clsx'
import styles from './GrowingTextarea.module.scss'
-import { createSignal, Show, Switch } from 'solid-js'
-import { style } from 'solid-js/web'
+import { createSignal, Show } from 'solid-js'
type Props = {
class?: string
diff --git a/src/context/editor.tsx b/src/context/editor.tsx
index 3cd0efca..28316a7d 100644
--- a/src/context/editor.tsx
+++ b/src/context/editor.tsx
@@ -65,6 +65,17 @@ const topic2topicInput = (topic: Topic): TopicInput => {
}
}
+const saveDraftToLocalStorage = (formToSave: ShoutForm) => {
+ localStorage.setItem(`shout-${formToSave.shoutId}`, JSON.stringify(formToSave))
+}
+const getDraftFromLocalStorage = (shoutId: number) => {
+ return JSON.parse(localStorage.getItem(`shout-${shoutId}`))
+}
+
+const removeDraftFromLocalStorage = (shoutId: number) => {
+ localStorage.removeItem(`shout-${shoutId}`)
+}
+
export const EditorProvider = (props: { children: JSX.Element }) => {
const { t } = useLocalize()
@@ -164,17 +175,6 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
await updateShout(draftForm, { publish: false })
}
- const saveDraftToLocalStorage = (formToSave: ShoutForm) => {
- localStorage.setItem(`shout-${formToSave.shoutId}`, JSON.stringify(formToSave))
- }
- const getDraftFromLocalStorage = (shoutId: number) => {
- return JSON.parse(localStorage.getItem(`shout-${shoutId}`))
- }
-
- const removeDraftFromLocalStorage = (shoutId: number) => {
- localStorage.removeItem(`shout-${shoutId}`)
- }
-
const publishShout = async (formToPublish: ShoutForm) => {
if (isEditorPanelVisible()) {
toggleEditorPanel()
diff --git a/src/context/profile.tsx b/src/context/profile.tsx
index 0e4a8cbf..6588c354 100644
--- a/src/context/profile.tsx
+++ b/src/context/profile.tsx
@@ -34,14 +34,16 @@ const useProfileForm = () => {
if (!currentSlug()) return
try {
await loadAuthor({ slug: currentSlug() })
- setForm({
+ const updatedFormValues = {
name: currentAuthor()?.name,
slug: currentAuthor()?.slug,
bio: currentAuthor()?.bio,
about: currentAuthor()?.about,
userpic: currentAuthor()?.userpic,
links: currentAuthor()?.links
- })
+ }
+
+ setForm(updatedFormValues)
} catch (error) {
console.error(error)
}
diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx
index 2a8ebf68..b2ff4231 100644
--- a/src/pages/profile/profileSettings.page.tsx
+++ b/src/pages/profile/profileSettings.page.tsx
@@ -1,8 +1,10 @@
import { PageLayout } from '../../components/_shared/PageLayout'
import { Icon } from '../../components/_shared/Icon'
import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
-import { For, createSignal, Show, onMount } from 'solid-js'
+import { For, createSignal, Show, onMount, onCleanup } from 'solid-js'
+import deepEqual from 'fast-deep-equal'
import { clsx } from 'clsx'
+
import styles from './Settings.module.scss'
import { useProfileForm } from '../../context/profile'
import { validateUrl } from '../../utils/validateUrl'
@@ -13,6 +15,8 @@ import { useSnackbar } from '../../context/snackbar'
import { useLocalize } from '../../context/localize'
import { handleFileUpload } from '../../utils/handleFileUpload'
import { Userpic } from '../../components/Author/Userpic'
+import { createStore } from 'solid-js/store'
+import { clone } from '../../utils/clone'
export const ProfileSettingsPage = () => {
const { t } = useLocalize()
@@ -24,11 +28,12 @@ export const ProfileSettingsPage = () => {
const {
actions: { showSnackbar }
} = useSnackbar()
-
const {
actions: { loadSession }
} = useSession()
+
const { form, updateFormField, submit, slugError } = useProfileForm()
+ const [prevForm, setPrevForm] = createStore(clone(form))
const handleChangeSocial = (value: string) => {
if (validateUrl(value)) {
@@ -45,6 +50,7 @@ export const ProfileSettingsPage = () => {
try {
await submit(form)
+ setPrevForm(clone(form))
showSnackbar({ body: t('Profile successfully saved') })
} catch {
showSnackbar({ type: 'error', body: t('Error') })
@@ -70,9 +76,23 @@ export const ProfileSettingsPage = () => {
}
const [hostname, setHostname] = createSignal(null)
- onMount(() => setHostname(window?.location.host))
- console.log('!!! form:', form)
+ onMount(() => {
+ setHostname(window?.location.host)
+
+ // eslint-disable-next-line unicorn/consistent-function-scoping
+ const handleBeforeUnload = (event) => {
+ if (!deepEqual(form, prevForm)) {
+ event.returnValue = t(
+ 'There are unsaved changes in your profile settings. Are you sure you want to leave the page without saving?'
+ )
+ }
+ }
+
+ window.addEventListener('beforeunload', handleBeforeUnload)
+ onCleanup(() => window.removeEventListener('beforeunload', handleBeforeUnload))
+ })
+
return (