From 5d5f4ccfdf935b3bbf5734e1fbaad28af4f51af0 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Thu, 9 Nov 2023 17:29:48 +0300 Subject: [PATCH] Feature/social input (#299) Social input in profile --- public/icons/social-facebook.svg | 3 ++ public/icons/social-instagram.svg | 5 ++ public/icons/social-linkedin.svg | 7 +++ public/icons/social-telegram.svg | 5 ++ public/icons/social-vk.svg | 3 ++ .../SocialNetworkInput.module.scss | 50 +++++++++++++++++++ .../SocialNetworkInput/SocialNetworkInput.tsx | 44 ++++++++++++++++ .../_shared/SocialNetworkInput/index.ts | 1 + .../_shared/SolidSwiper/ArticleCardSwiper.tsx | 2 +- src/context/profile.tsx | 4 ++ src/pages/profile/Settings.module.scss | 4 ++ src/pages/profile/profileSettings.page.tsx | 47 ++++++++++------- src/utils/profileSocialLinks.ts | 50 +++++++++++++++++++ 13 files changed, 205 insertions(+), 20 deletions(-) create mode 100644 public/icons/social-facebook.svg create mode 100644 public/icons/social-instagram.svg create mode 100644 public/icons/social-linkedin.svg create mode 100644 public/icons/social-telegram.svg create mode 100644 public/icons/social-vk.svg create mode 100644 src/components/_shared/SocialNetworkInput/SocialNetworkInput.module.scss create mode 100644 src/components/_shared/SocialNetworkInput/SocialNetworkInput.tsx create mode 100644 src/components/_shared/SocialNetworkInput/index.ts create mode 100644 src/utils/profileSocialLinks.ts diff --git a/public/icons/social-facebook.svg b/public/icons/social-facebook.svg new file mode 100644 index 00000000..10e45710 --- /dev/null +++ b/public/icons/social-facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/social-instagram.svg b/public/icons/social-instagram.svg new file mode 100644 index 00000000..05db9472 --- /dev/null +++ b/public/icons/social-instagram.svg @@ -0,0 +1,5 @@ + + + diff --git a/public/icons/social-linkedin.svg b/public/icons/social-linkedin.svg new file mode 100644 index 00000000..d0bb452e --- /dev/null +++ b/public/icons/social-linkedin.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icons/social-telegram.svg b/public/icons/social-telegram.svg new file mode 100644 index 00000000..420cb810 --- /dev/null +++ b/public/icons/social-telegram.svg @@ -0,0 +1,5 @@ + + + diff --git a/public/icons/social-vk.svg b/public/icons/social-vk.svg new file mode 100644 index 00000000..37b3ea78 --- /dev/null +++ b/public/icons/social-vk.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/_shared/SocialNetworkInput/SocialNetworkInput.module.scss b/src/components/_shared/SocialNetworkInput/SocialNetworkInput.module.scss new file mode 100644 index 00000000..65401cdf --- /dev/null +++ b/src/components/_shared/SocialNetworkInput/SocialNetworkInput.module.scss @@ -0,0 +1,50 @@ +.SocialNetworkInput { + display: flex; + align-items: center; + justify-content: flex-start; + height: 54px; + border: 2px solid var(--black-100); + border-radius: 2px; + + .icon { + width: 54px; + height: 54px; + padding: 16px; + border-right: 2px solid var(--black-100); + display: flex; + align-items: center; + justify-content: center; + } + + .input { + @include font-size(1.7rem); + + margin: 0 16px; + width: 100%; + border: none; + height: 1em; + padding: 0; + display: block; + flex: 1; + + &:focus { + outline: none; + } + + &::placeholder { + color: var(--black-300); + } + } + + .remove { + width: 54px; + height: 54px; + padding: 16px; + &:hover { + background: var(--background-color-invert); + img { + filter: invert(1); + } + } + } +} diff --git a/src/components/_shared/SocialNetworkInput/SocialNetworkInput.tsx b/src/components/_shared/SocialNetworkInput/SocialNetworkInput.tsx new file mode 100644 index 00000000..c3008a2a --- /dev/null +++ b/src/components/_shared/SocialNetworkInput/SocialNetworkInput.tsx @@ -0,0 +1,44 @@ +import { clsx } from 'clsx' +import styles from './SocialNetworkInput.module.scss' +import { Icon } from '../Icon' +import { onMount, Show } from 'solid-js' + +type Props = { + class?: string + network?: string + link?: string + isExist: boolean + handleChange: (value: string) => void + handleDelete?: () => void + slug?: string + autofocus?: boolean +} + +export const SocialNetworkInput = (props: Props) => { + const inputRef: { current: HTMLInputElement } = { current: null } + onMount(() => { + if (props.autofocus) { + inputRef.current.focus() + } + }) + return ( +
+
+ +
+ (inputRef.current = el)} + class={styles.input} + type="text" + value={props.isExist ? props.link : null} + onChange={(event) => props.handleChange(event.currentTarget.value)} + placeholder={props.autofocus ? null : `${props.link}${props.slug}`} + /> + + + +
+ ) +} diff --git a/src/components/_shared/SocialNetworkInput/index.ts b/src/components/_shared/SocialNetworkInput/index.ts new file mode 100644 index 00000000..bb028529 --- /dev/null +++ b/src/components/_shared/SocialNetworkInput/index.ts @@ -0,0 +1 @@ +export { SocialNetworkInput } from './SocialNetworkInput' diff --git a/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx b/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx index 4c4fbfab..c0b839a8 100644 --- a/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx +++ b/src/components/_shared/SolidSwiper/ArticleCardSwiper.tsx @@ -49,7 +49,7 @@ export const ArticleCardSwiper = (props: Props) => { speed={800} autoplay={{ disableOnInteraction: false, - delay: 3000, + delay: 6000, pauseOnMouseEnter: true }} > diff --git a/src/context/profile.tsx b/src/context/profile.tsx index 0038b518..5c55b500 100644 --- a/src/context/profile.tsx +++ b/src/context/profile.tsx @@ -69,6 +69,10 @@ const useProfileForm = () => { }) } } + createEffect(() => { + console.log('!!! FL:', form.links) + }) + return { form, submit, updateFormField, slugError } } diff --git a/src/pages/profile/Settings.module.scss b/src/pages/profile/Settings.module.scss index 54d620e3..1bdb7520 100644 --- a/src/pages/profile/Settings.module.scss +++ b/src/pages/profile/Settings.module.scss @@ -215,3 +215,7 @@ h5 { } } } + +.socialInput { + margin-top: 1rem; +} diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx index c5251456..dccc7cc6 100644 --- a/src/pages/profile/profileSettings.page.tsx +++ b/src/pages/profile/profileSettings.page.tsx @@ -1,5 +1,4 @@ import { PageLayout } from '../../components/_shared/PageLayout' -import { Icon } from '../../components/_shared/Icon' import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation' import { For, createSignal, Show, onMount, onCleanup, createEffect } from 'solid-js' import deepEqual from 'fast-deep-equal' @@ -19,11 +18,14 @@ import SimplifiedEditor from '../../components/Editor/SimplifiedEditor' import { GrowingTextarea } from '../../components/_shared/GrowingTextarea' import { AuthGuard } from '../../components/AuthGuard' import { handleImageUpload } from '../../utils/handleImageUpload' +import { SocialNetworkInput } from '../../components/_shared/SocialNetworkInput' +import { profileSocialLinks } from '../../utils/profileSocialLinks' export const ProfileSettingsPage = () => { const { t } = useLocalize() const [addLinkForm, setAddLinkForm] = createSignal(false) const [incorrectUrl, setIncorrectUrl] = createSignal(false) + const [isUserpicUpdating, setIsUserpicUpdating] = createSignal(false) const [uploadError, setUploadError] = createSignal(false) const [isFloatingPanelVisible, setIsFloatingPanelVisible] = createSignal(false) @@ -38,7 +40,7 @@ export const ProfileSettingsPage = () => { const { form, updateFormField, submit, slugError } = useProfileForm() const [prevForm, setPrevForm] = createStore(clone(form)) - + const [social, setSocial] = createSignal(form.links) const handleChangeSocial = (value: string) => { if (validateUrl(value)) { updateFormField('links', value) @@ -108,6 +110,14 @@ export const ProfileSettingsPage = () => { } }) + const handleDeleteSocialLink = (link) => { + updateFormField('links', link, true) + } + + createEffect(() => { + setSocial(form.links) + }) + return ( @@ -230,27 +240,26 @@ export const ProfileSettingsPage = () => { -
- handleChangeSocial(event.currentTarget.value)} - /> -
+ handleChangeSocial(value)} + />

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

- - {(link) => ( -
- - -
+ + {(network) => ( + handleChangeSocial(value)} + isExist={!network.isPlaceholder} + slug={form.slug} + handleDelete={() => handleDeleteSocialLink(network.link)} + /> )} diff --git a/src/utils/profileSocialLinks.ts b/src/utils/profileSocialLinks.ts new file mode 100644 index 00000000..637c1743 --- /dev/null +++ b/src/utils/profileSocialLinks.ts @@ -0,0 +1,50 @@ +type Link = { + link: string + isPlaceholder: boolean + name?: string +} + +const links: Link[] = [ + { link: 'https://facebook.com/', name: 'facebook', isPlaceholder: true }, + { link: 'https://linkedin.com/', name: 'linkedin', isPlaceholder: true }, + { link: 'https://vk.com/', name: 'vk', isPlaceholder: true }, + { link: 'https://instagram.com/', name: 'instagram', isPlaceholder: true }, + { link: 'https://t.me/', name: 'telegram', isPlaceholder: true } +] + +const checkLink = (link: string, keyword: string): boolean => link.includes(keyword) + +export const profileSocialLinks = (socialLinks: string[]): Link[] => { + const processedLinks: Link[] = [] + let unmatchedLinks: string[] = [...socialLinks] + + links.forEach((linkObj) => { + let linkMatched = false + + socialLinks.forEach((serverLink) => { + if (checkLink(serverLink, new URL(linkObj.link).hostname.replace('www.', ''))) { + processedLinks.push({ ...linkObj, link: serverLink, isPlaceholder: false }) + linkMatched = true + unmatchedLinks = unmatchedLinks.filter((unmatchedLink) => unmatchedLink !== serverLink) + } + }) + + if (!linkMatched) { + processedLinks.push({ ...linkObj, isPlaceholder: true }) + } + }) + + unmatchedLinks.forEach((unmatchedLink) => { + processedLinks.push({ link: unmatchedLink, isPlaceholder: false }) + }) + + return processedLinks.sort((a, b) => { + if (a.isPlaceholder && !b.isPlaceholder) { + return 1 + } else if (!a.isPlaceholder && b.isPlaceholder) { + return -1 + } else { + return 0 + } + }) +}