import { createFileUploader } from '@solid-primitives/upload' import { clsx } from 'clsx' import deepEqual from 'fast-deep-equal' import { For, Match, Show, Switch, createEffect, createSignal, lazy, onCleanup, onMount } from 'solid-js' import { createStore } from 'solid-js/store' import { useConfirm } from '../../context/confirm' import { useLocalize } from '../../context/localize' import { useProfileForm } from '../../context/profile' import { useSession } from '../../context/session' import { useSnackbar } from '../../context/snackbar' import { hideModal, showModal } from '../../stores/ui' import { clone } from '../../utils/clone' import { getImageUrl } from '../../utils/getImageUrl' import { handleImageUpload } from '../../utils/handleImageUpload' import { profileSocialLinks } from '../../utils/profileSocialLinks' import { validateUrl } from '../../utils/validateUrl' import { Modal } from '../Nav/Modal' import { ProfileSettingsNavigation } from '../Nav/ProfileSettingsNavigation' import { Button } from '../_shared/Button' import { Icon } from '../_shared/Icon' import { ImageCropper } from '../_shared/ImageCropper' import { Loading } from '../_shared/Loading' import { Popover } from '../_shared/Popover' import { SocialNetworkInput } from '../_shared/SocialNetworkInput' import styles from '../../pages/profile/Settings.module.scss' const SimplifiedEditor = lazy(() => import('../../components/Editor/SimplifiedEditor')) const GrowingTextarea = lazy(() => import('../../components/_shared/GrowingTextarea/GrowingTextarea')) export const ProfileSettings = () => { const { t } = useLocalize() const [prevForm, setPrevForm] = createStore({}) const [isFormInitialized, setIsFormInitialized] = createSignal(false) const [isSaving, setIsSaving] = createSignal(false) const [social, setSocial] = createSignal([]) const [addLinkForm, setAddLinkForm] = createSignal(false) const [incorrectUrl, setIncorrectUrl] = createSignal(false) const [isUserpicUpdating, setIsUserpicUpdating] = createSignal(false) const [userpicFile, setUserpicFile] = createSignal(null) const [uploadError, setUploadError] = createSignal(false) const [isFloatingPanelVisible, setIsFloatingPanelVisible] = createSignal(false) const [hostname, setHostname] = createSignal(null) const [slugError, setSlugError] = createSignal() const [nameError, setNameError] = createSignal() const { form, submit, updateFormField, setForm } = useProfileForm() const { showSnackbar } = useSnackbar() const { loadAuthor } = useSession() const { showConfirm } = useConfirm() createEffect(() => { if (Object.keys(form).length > 0 && !isFormInitialized()) { setPrevForm(form) setSocial(form.links) setIsFormInitialized(true) } }) const slugInputRef: { current: HTMLInputElement } = { current: null } const nameInputRef: { current: HTMLInputElement } = { current: null } const handleChangeSocial = (value: string) => { if (validateUrl(value)) { updateFormField('links', value) setAddLinkForm(false) } else { setIncorrectUrl(true) } } const handleSubmit = async (event: Event) => { event.preventDefault() setIsSaving(true) if (nameInputRef.current.value.length === 0) { setNameError(t('Required')) nameInputRef.current.focus() setIsSaving(false) return } if (slugInputRef.current.value.length === 0) { setSlugError(t('Required')) slugInputRef.current.focus() setIsSaving(false) return } try { await submit(form) setPrevForm(clone(form)) showSnackbar({ body: t('Profile successfully saved') }) } catch (error) { if (error.code === 'duplicate_slug') { setSlugError(t('The address is already taken')) slugInputRef.current.focus() return } showSnackbar({ type: 'error', body: t('Error') }) } finally { setIsSaving(false) } await loadAuthor() // renews author's profile } const handleCancel = async () => { const isConfirmed = await showConfirm({ confirmBody: t('Do you really want to reset all changes?'), confirmButtonVariant: 'primary', declineButtonVariant: 'secondary', }) if (isConfirmed) { setForm(clone(prevForm)) } } const handleCropAvatar = () => { const { selectFiles } = createFileUploader({ multiple: false, accept: 'image/*' }) selectFiles(([uploadFile]) => { setUserpicFile(uploadFile) showModal('cropImage') }) } const handleUploadAvatar = async (uploadFile) => { try { setUploadError(false) setIsUserpicUpdating(true) const result = await handleImageUpload(uploadFile) updateFormField('pic', result.url) setUserpicFile(null) setIsUserpicUpdating(false) } catch (error) { setUploadError(true) console.error('[upload avatar] error', error) } } 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)) }) createEffect(() => { if (!deepEqual(form, prevForm)) { setIsFloatingPanelVisible(true) } }) const handleDeleteSocialLink = (link) => { updateFormField('links', link, true) } return ( 0 && isFormInitialized()} fallback={}> <>

{t('Profile settings')}

{t('Here you can customize your profile the way you want.')}

{t('Userpic')}

{(triggerRef: (el) => void) => ( )} {/* @@TODO inspect popover below. onClick causes page refreshing */} {/* {(triggerRef: (el) => void) => ( )} */}
{t('Here you can upload your photo')}
{t('Upload error')}

{t('Name')}

{t( 'Your name will appear on your profile page and as your signature in publications, comments and responses.', )}

updateFormField('name', event.currentTarget.value)} value={form.name} ref={(el) => (nameInputRef.current = el)} />
{t(`${nameError()}`)}

{t('Address on Discours')}

updateFormField('slug', event.currentTarget.value)} value={form.slug} ref={(el) => (slugInputRef.current = el)} class="nolabel" />

{t(`${slugError()}`)}

{t('Introduce')}

updateFormField('bio', value)} initialValue={form.bio || ''} allowEnterKey={false} maxLength={120} />

{t('About')}

updateFormField('about', value)} />

{t('Social networks')}

handleChangeSocial(value)} />

{t('It does not look like url')}

{(network) => ( handleChangeSocial(value)} isExist={!network.isPlaceholder} slug={form.slug} handleDelete={() => handleDeleteSocialLink(network.link)} /> )}
setUserpicFile(null)}>

{t('Crop image')}

{ handleUploadAvatar(data) hideModal() }} onDecline={() => hideModal()} />
) }