mrged-testing
This commit is contained in:
commit
c5906a2762
|
@ -56,6 +56,7 @@
|
||||||
"@solid-devtools/logger": "^0.5.0",
|
"@solid-devtools/logger": "^0.5.0",
|
||||||
"@solid-primitives/memo": "^1.1.2",
|
"@solid-primitives/memo": "^1.1.2",
|
||||||
"@solid-primitives/storage": "^1.3.3",
|
"@solid-primitives/storage": "^1.3.3",
|
||||||
|
"@solid-primitives/upload": "^0.0.105",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
"@types/node": "^18.11.9",
|
"@types/node": "^18.11.9",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
|
|
3
public/icons/apple.svg
Normal file
3
public/icons/apple.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.9159 0C13.0948 1.21486 12.6003 2.40503 11.948 3.24708C11.2501 4.15029 10.0472 4.84886 8.88168 4.8124C8.66892 3.64929 9.21368 2.45089 9.87651 1.6453C10.6036 0.756201 11.8498 0.0740913 12.9159 0ZM16.4177 17.0985C17.0185 16.1776 17.243 15.7131 17.7094 14.6735C14.3169 13.3833 13.7733 8.56035 17.1308 6.70925C16.1067 5.425 14.6676 4.68056 13.3093 4.68056C12.3305 4.68056 11.6599 4.93598 11.0502 5.16818C10.5421 5.36169 10.0764 5.53907 9.50996 5.53907C8.89784 5.53907 8.35578 5.34472 7.78819 5.14121C7.1645 4.91758 6.50998 4.68291 5.69781 4.68291C4.17342 4.68291 2.55083 5.61434 1.5221 7.20672C0.0760363 9.44945 0.322698 13.6656 2.66774 17.2573C3.50592 18.5427 4.62583 19.9869 6.0906 19.9998C6.69839 20.0058 7.10283 19.8244 7.5405 19.6281C8.04146 19.4035 8.58593 19.1592 9.52867 19.1542C10.477 19.1485 11.0128 19.3957 11.5071 19.6237C11.9336 19.8204 12.3291 20.0028 12.9317 19.9963C14.3976 19.9845 15.5795 18.3839 16.4177 17.0985Z" fill="#0B0B0A"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
3
public/icons/password-hide.svg
Normal file
3
public/icons/password-hide.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.65441 0.231661L9.68495 5.65813C9.7181 5.6779 9.75068 5.69975 9.78214 5.72348C9.82187 5.75305 9.85898 5.78488 9.89344 5.81859L22.6933 15.6986C23.1773 16.0711 23.266 16.7642 22.8933 17.2482C22.6752 17.5322 22.3479 17.6776 22.0184 17.6776C21.7821 17.6776 21.5434 17.6027 21.3436 17.4482L18.8218 15.502C16.7955 16.7934 14.453 17.4731 12 17.4731C6.69216 17.4731 2.01349 14.2511 0.0752888 9.26566C-0.0247164 9.01122 -0.0247164 8.72721 0.0730288 8.47259C0.783219 6.61144 1.93866 4.91947 3.39091 3.59094L1.30455 1.9807C0.820536 1.60798 0.732016 0.914929 1.10473 0.431054C1.47726 -0.0552304 2.17484 -0.141678 2.65437 0.231044L2.65441 0.231661ZM2.29996 8.86387C3.9927 12.7677 7.75343 15.2647 12.0001 15.2647C13.7543 15.2647 15.4355 14.8474 16.9372 14.0475L14.9695 12.529C14.1267 13.1978 13.0877 13.5675 12 13.5675C9.3778 13.5675 7.24641 11.4589 7.24641 8.86623C7.24641 8.18391 7.392 7.51721 7.67224 6.8966L5.1736 4.96766C3.94473 6.0071 2.94617 7.35878 2.29982 8.86377L2.29996 8.86387ZM12.0025 0.209C17.3195 0.209 22.0001 3.45157 23.9272 8.47092C24.025 8.72781 24.025 9.01184 23.9227 9.26853C23.5252 10.291 22.9571 11.3067 22.2868 12.1998C22.0708 12.4906 21.7369 12.6427 21.4028 12.6427C21.1711 12.6427 20.9393 12.5701 20.7415 12.4224C20.253 12.0567 20.1532 11.3636 20.5212 10.8773C20.9869 10.2569 21.3892 9.56631 21.7005 8.86171C20.0145 4.93305 16.2538 2.41745 12.0028 2.41745C10.9849 2.41745 10.0032 2.55832 9.08288 2.83329C8.49452 3.01278 7.88317 2.6766 7.7082 2.09258C7.53324 1.50857 7.86509 0.892876 8.44891 0.717908C9.57365 0.381539 10.7713 0.208817 12.0028 0.208817L12.0025 0.209ZM9.45528 8.86628C9.45528 10.241 10.596 11.3589 12.0003 11.3589C12.3922 11.3589 12.7726 11.2687 13.1182 11.0997L9.51502 8.31838C9.47754 8.48806 9.45532 8.67094 9.45532 8.86623L9.45528 8.86628ZM12.4319 4.11948C14.1339 4.31497 15.5948 5.35327 16.3357 6.89612C16.5992 7.44604 16.3674 8.1073 15.8176 8.37078C15.663 8.44593 15.5018 8.48002 15.3404 8.48002C14.9291 8.48002 14.536 8.25044 14.3429 7.85285C13.9339 6.99836 13.125 6.42355 12.182 6.31454C11.5754 6.24636 11.1413 5.69644 11.2094 5.09208C11.2799 4.48526 11.8479 4.05587 12.4319 4.11951L12.4319 4.11948Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
3
public/icons/password-open.svg
Normal file
3
public/icons/password-open.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="24" height="18" viewBox="0 0 24 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.9998 0C17.3178 0 21.9994 3.24315 23.927 8.26336C24.0247 8.52025 24.0247 8.80427 23.9247 9.06115C21.9884 14.0474 17.3093 17.27 12 17.27C6.6907 17.27 2.01155 14.0474 0.0752887 9.06115C-0.0247163 8.80426 -0.0247163 8.52024 0.0730287 8.26336C2.00028 3.24334 6.68169 0 12.0002 0H11.9998ZM11.9998 2.21123C7.74537 2.21123 3.9819 4.72722 2.29779 8.65886C3.99092 12.561 7.75463 15.0588 11.9998 15.0588C16.2451 15.0588 20.0086 12.561 21.7019 8.65886C20.018 4.72717 16.2547 2.21123 11.9998 2.21123V2.21123ZM11.9998 3.90662C14.6224 3.90662 16.7542 6.03839 16.7542 8.66112C16.7542 11.2564 14.6201 13.3631 11.9998 13.3631C9.37958 13.3631 7.24554 11.2541 7.24554 8.66112C7.24554 6.03839 9.37731 3.90662 11.9998 3.90662V3.90662ZM11.9998 6.11804C10.5977 6.11804 9.4545 7.25878 9.4545 8.66339C9.4545 10.0383 10.5954 11.1564 11.9998 11.1564C13.4043 11.1564 14.5452 10.0383 14.5452 8.66339C14.5452 7.25878 13.402 6.11804 11.9998 6.11804V6.11804Z" fill="black"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
|
@ -0,0 +1,10 @@
|
||||||
|
.navigationHeader {
|
||||||
|
@include font-size(1.8rem);
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 1.1em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
@include font-size(1.4rem);
|
||||||
|
}
|
21
src/components/Discours/ProfileSettingsNavigation.tsx
Normal file
21
src/components/Discours/ProfileSettingsNavigation.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import styles from './ProfileSettingsNavigation.module.scss'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h4 class={styles.navigationHeader}>Настройки</h4>
|
||||||
|
<ul class={clsx(styles.navigation, 'nodash')}>
|
||||||
|
<li>
|
||||||
|
<a href="/profile/settings">Профиль</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/profile/subscriptions">Подписки</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/profile/security">Вход и безопасность</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
135
src/components/Pages/profile/ProfileSecurityPage.tsx
Normal file
135
src/components/Pages/profile/ProfileSecurityPage.tsx
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
|
import type { PageProps } from '../../types'
|
||||||
|
import styles from './Settings.module.scss'
|
||||||
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import ProfileSettingsNavigation from '../../Discours/ProfileSettingsNavigation'
|
||||||
|
|
||||||
|
export const ProfileSecurityPage = (props: PageProps) => {
|
||||||
|
return (
|
||||||
|
<PageWrap>
|
||||||
|
<div class="wide-container">
|
||||||
|
<div class="shift-content">
|
||||||
|
<div class="left-col">
|
||||||
|
<div class={clsx('left-navigation', styles.leftNavigation)}>
|
||||||
|
<ProfileSettingsNavigation />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 col-lg-9 col-xl-8">
|
||||||
|
<h1>Вход и безопасность</h1>
|
||||||
|
<p class="description">Настройки аккаунта, почты, пароля и способов входа.</p>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<h4>Почта</h4>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<input type="text" name="email" id="email" placeholder="Почта" />
|
||||||
|
<label for="email">Почта</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>Изменить пароль</h4>
|
||||||
|
<h5>Текущий пароль</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="password-current"
|
||||||
|
id="password-current"
|
||||||
|
class={clsx(styles.passwordInput, 'nolabel')}
|
||||||
|
/>
|
||||||
|
<button type="button" class={styles.passwordToggleControl}>
|
||||||
|
<Icon name="password-hide" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5>Новый пароль</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password-new"
|
||||||
|
id="password-new"
|
||||||
|
class={clsx(styles.passwordInput, 'nolabel')}
|
||||||
|
/>
|
||||||
|
<button type="button" class={styles.passwordToggleControl}>
|
||||||
|
<Icon name="password-open" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5>Подтвердите новый пароль</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
name="password-new-confirm"
|
||||||
|
id="password-new-confirm"
|
||||||
|
class={clsx(styles.passwordInput, 'nolabel')}
|
||||||
|
/>
|
||||||
|
<button type="button" class={styles.passwordToggleControl}>
|
||||||
|
<Icon name="password-open" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h4>Социальные сети</h4>
|
||||||
|
<h5>Google</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<p>
|
||||||
|
<button class={clsx('button button--light', styles.socialButton)} type="button">
|
||||||
|
<Icon name="google" class={styles.icon} />
|
||||||
|
Привязать
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5>VK</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<p>
|
||||||
|
<button class={clsx(styles.socialButton, 'button button--light')} type="button">
|
||||||
|
<Icon name="vk" class={styles.icon} />
|
||||||
|
Привязать
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5>Facebook</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<p>
|
||||||
|
<button class={clsx(styles.socialButton, 'button button--light')} type="button">
|
||||||
|
<Icon name="facebook" class={styles.icon} />
|
||||||
|
Привязать
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5>Apple</h5>
|
||||||
|
<div class="pretty-form__item">
|
||||||
|
<p>
|
||||||
|
<button
|
||||||
|
class={clsx(
|
||||||
|
styles.socialButton,
|
||||||
|
styles.socialButtonApple,
|
||||||
|
'button' + ' button--light'
|
||||||
|
)}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Icon name="apple" class={styles.icon} />
|
||||||
|
Привязать
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<button class="button button--submit" type="submit">
|
||||||
|
Сохранить настройки
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageWrap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for lazy loading
|
||||||
|
export default ProfileSecurityPage
|
|
@ -1,122 +1,182 @@
|
||||||
import { PageWrap } from '../../_shared/PageWrap'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
|
import { t } from '../../../utils/intl'
|
||||||
import type { PageProps } from '../../types'
|
import type { PageProps } from '../../types'
|
||||||
import styles from './Settings.module.scss'
|
|
||||||
import { Icon } from '../../_shared/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
import ProfileSettingsNavigation from '../../Discours/ProfileSettingsNavigation'
|
||||||
|
import { For, createSignal, Show } from 'solid-js'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './Settings.module.scss'
|
||||||
|
import { useProfileForm } from '../../../context/profile'
|
||||||
|
import { createFileUploader } from '@solid-primitives/upload'
|
||||||
|
|
||||||
export const ProfileSettingsPage = (props: PageProps) => {
|
export const ProfileSettingsPage = (props: PageProps) => {
|
||||||
|
const [addLinkForm, setAddLinkForm] = createSignal<boolean>(false)
|
||||||
|
const { form, updateFormField, submit } = useProfileForm()
|
||||||
|
const handleChangeSocial = (value) => {
|
||||||
|
updateFormField('links', value)
|
||||||
|
setAddLinkForm(false)
|
||||||
|
}
|
||||||
|
const handleSubmit = (event: Event): void => {
|
||||||
|
event.preventDefault()
|
||||||
|
submit(form)
|
||||||
|
}
|
||||||
|
const { selectFiles: selectFilesAsync } = createFileUploader({ accept: 'image/*' })
|
||||||
|
|
||||||
|
const handleUpload = () => {
|
||||||
|
selectFilesAsync(async ([{ source, name, size, file }]) => {
|
||||||
|
try {
|
||||||
|
console.log({ source, name, size, file })
|
||||||
|
// DO UPLOAD STUFF HERE AND RETURN URL
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrap>
|
<PageWrap>
|
||||||
|
<Show when={form}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
<div class="shift-content">
|
<div class="shift-content">
|
||||||
<div class="left-col">
|
<div class="left-col">
|
||||||
<div class="left-navigation">WIP</div>
|
<div class={clsx('left-navigation', styles.leftNavigation)}>
|
||||||
|
<ProfileSettingsNavigation />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10 col-lg-9 col-xl-8">
|
<div class="col-md-10 col-lg-9 col-xl-8">
|
||||||
<h1>Настройки профиля</h1>
|
<h1>{t('Profile settings')}</h1>
|
||||||
<p class="description">Здесь можно настроить свой профиль так, как вы хотите.</p>
|
<p class="description">{t('Here you can customize your profile the way you want.')}</p>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
<form>
|
<h4>{t('Userpic')}</h4>
|
||||||
<h4>Аватар</h4>
|
|
||||||
<div class="pretty-form__item">
|
<div class="pretty-form__item">
|
||||||
<div class={styles.avatarContainer}>
|
<div class={styles.avatarContainer}>
|
||||||
<img class={styles.avatar} />
|
<img class={styles.avatar} src={form.userpic} alt={form.name} />
|
||||||
<input
|
<button type="button" class={styles.avatarInput} onClick={handleUpload} />
|
||||||
type="file"
|
|
||||||
name="avatar"
|
|
||||||
class={styles.avatarInput}
|
|
||||||
accept="image/jpeg,image/png,image/gif,image/webp"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h4>{t('Name')}</h4>
|
||||||
<h4>Имя</h4>
|
|
||||||
<p class="description">
|
<p class="description">
|
||||||
Ваше имя появится на странице вашего профиля и как ваша подпись
|
{t(
|
||||||
в публикациях, комментариях и откликах
|
'Your name will appear on your profile page and as your signature in publications, comments and responses.'
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<div class="pretty-form__item">
|
<div class="pretty-form__item">
|
||||||
<input type="text" name="username" id="username" placeholder="Имя" />
|
<input
|
||||||
|
type="text"
|
||||||
|
name="username"
|
||||||
|
id="username"
|
||||||
|
placeholder={t('Name')}
|
||||||
|
onChange={(event) => updateFormField('name', event.currentTarget.value)}
|
||||||
|
value={form.name}
|
||||||
|
/>
|
||||||
<label for="username">Имя</label>
|
<label for="username">Имя</label>
|
||||||
</div>
|
</div>
|
||||||
|
{/*Не готов бекенд*/}
|
||||||
|
{/*<h4>{t('Address on Discourse')}</h4>*/}
|
||||||
|
{/*<div class="pretty-form__item">*/}
|
||||||
|
{/* <div class={styles.discoursName}>*/}
|
||||||
|
{/* <label for="user-address">https://discours.io/user/</label>*/}
|
||||||
|
{/* <div class={styles.discoursNameField}>*/}
|
||||||
|
{/* <input*/}
|
||||||
|
{/* type="text"*/}
|
||||||
|
{/* name="user-address"*/}
|
||||||
|
{/* id="user-address"*/}
|
||||||
|
{/* onChange={(event) => updateFormField('slug', event.currentTarget.value)}*/}
|
||||||
|
{/* value={form.slug}*/}
|
||||||
|
{/* class="nolabel"*/}
|
||||||
|
{/* />*/}
|
||||||
|
{/* <p class="form-message form-message--error">*/}
|
||||||
|
{/* {t('Sorry, this address is already taken, please choose another one.')}*/}
|
||||||
|
{/* </p>*/}
|
||||||
|
{/* </div>*/}
|
||||||
|
{/* </div>*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
|
||||||
<h4>Адрес на Дискурсе</h4>
|
{/*Нет реализации полей на бэке*/}
|
||||||
<div class="pretty-form__item">
|
{/*<h4>{t('Introduce')}</h4>*/}
|
||||||
<div class={styles.discoursName}>
|
{/*<div class="pretty-form__item">*/}
|
||||||
<label for="user-address">https://discours.io/user/</label>
|
{/* <textarea name="presentation" id="presentation" placeholder={t('Introduce')} />*/}
|
||||||
<div class={styles.discoursNameField}>
|
{/* <label for="presentation">{t('Introduce')}</label>*/}
|
||||||
<input type="text" name="user-address" id="user-address" class="nolabel" />
|
{/*</div>*/}
|
||||||
<p class="form-message form-message--error">
|
|
||||||
Увы, этот адрес уже занят, выберите другой
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4>Представление</h4>
|
<h4>{t('About myself')}</h4>
|
||||||
<div class="pretty-form__item">
|
<div class="pretty-form__item">
|
||||||
<textarea name="presentation" id="presentation" placeholder="Представление" />
|
<textarea
|
||||||
<label for="presentation">Представление</label>
|
name="about"
|
||||||
</div>
|
id="about"
|
||||||
|
placeholder={t('About myself')}
|
||||||
<h4>О себе</h4>
|
value={form.bio}
|
||||||
<div class="pretty-form__item">
|
onChange={(event) => updateFormField('bio', event.currentTarget.value)}
|
||||||
<textarea name="about" id="about" placeholder="О себе" />
|
|
||||||
<label for="about">О себе</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4>Чем могу помочь/навыки</h4>
|
|
||||||
<div class="pretty-form__item">
|
|
||||||
<input type="text" name="skills" id="skills" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4>Откуда</h4>
|
|
||||||
<div class="pretty-form__item">
|
|
||||||
<input type="text" name="location" id="location" placeholder="Откуда" />
|
|
||||||
<label for="location">Откуда</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h4>Дата рождения</h4>
|
|
||||||
<div class="pretty-form__item">
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
name="birthdate"
|
|
||||||
id="birthdate"
|
|
||||||
placeholder="Дата рождения"
|
|
||||||
class="nolabel"
|
|
||||||
/>
|
/>
|
||||||
|
<label for="about">{t('About myself')}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/*Нет реализации полей на бэке*/}
|
||||||
|
{/*<h4>{t('How can I help/skills')}</h4>*/}
|
||||||
|
{/*<div class="pretty-form__item">*/}
|
||||||
|
{/* <input type="text" name="skills" id="skills" />*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
{/*<h4>{t('Where')}</h4>*/}
|
||||||
|
{/*<div class="pretty-form__item">*/}
|
||||||
|
{/* <input type="text" name="location" id="location" placeholder="Откуда" />*/}
|
||||||
|
{/* <label for="location">{t('Where')}</label>*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
|
||||||
|
{/*<h4>{t('Date of Birth')}</h4>*/}
|
||||||
|
{/*<div class="pretty-form__item">*/}
|
||||||
|
{/* <input*/}
|
||||||
|
{/* type="date"*/}
|
||||||
|
{/* name="birthdate"*/}
|
||||||
|
{/* id="birthdate"*/}
|
||||||
|
{/* placeholder="Дата рождения"*/}
|
||||||
|
{/* class="nolabel"*/}
|
||||||
|
{/* />*/}
|
||||||
|
{/*</div>*/}
|
||||||
|
|
||||||
<div class={clsx(styles.multipleControls, 'pretty-form__item')}>
|
<div class={clsx(styles.multipleControls, 'pretty-form__item')}>
|
||||||
<div class={styles.multipleControlsHeader}>
|
<div class={styles.multipleControlsHeader}>
|
||||||
<h4>Социальные сети</h4>
|
<h4>{t('Social networks')}</h4>
|
||||||
<button class="button">+</button>
|
<button type="button" class="button" onClick={() => setAddLinkForm(!addLinkForm())}>
|
||||||
|
+
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={addLinkForm()}>
|
||||||
<div class={styles.multipleControlsItem}>
|
<div class={styles.multipleControlsItem}>
|
||||||
<input type="text" name="social1" class="nolabel" />
|
<input
|
||||||
<button>
|
autofocus={true}
|
||||||
<Icon name="remove" class={styles.icon} />
|
type="text"
|
||||||
</button>
|
name="link"
|
||||||
</div>
|
class="nolabel"
|
||||||
<div class={styles.multipleControlsItem}>
|
onChange={(event) => handleChangeSocial(event.currentTarget.value)}
|
||||||
<input type="text" name="social1" class="nolabel" />
|
/>
|
||||||
<button>
|
</div>
|
||||||
|
</Show>
|
||||||
|
<For each={form.links}>
|
||||||
|
{(link) => (
|
||||||
|
<div class={styles.multipleControlsItem}>
|
||||||
|
<input type="text" value={link} readonly={true} name="link" class="nolabel" />
|
||||||
|
<button type="button" onClick={() => updateFormField('links', link, true)}>
|
||||||
<Icon name="remove" class={styles.icon} />
|
<Icon name="remove" class={styles.icon} />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<p>
|
<p>
|
||||||
<button class="button button--submit">Сохранить настройки</button>
|
<button type="submit" class="button button--submit">
|
||||||
|
{t('Save settings')}
|
||||||
|
</button>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<pre>{JSON.stringify(form, null, 2)}</pre>
|
||||||
</div>
|
</div>
|
||||||
|
</Show>
|
||||||
</PageWrap>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
137
src/components/Pages/profile/ProfileSubscriptionsPage.tsx
Normal file
137
src/components/Pages/profile/ProfileSubscriptionsPage.tsx
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
|
import type { PageProps } from '../../types'
|
||||||
|
import styles from './Settings.module.scss'
|
||||||
|
import stylesSettings from '../../../styles/FeedSettings.module.scss'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import ProfileSettingsNavigation from '../../Discours/ProfileSettingsNavigation'
|
||||||
|
import { SearchField } from '../../_shared/SearchField'
|
||||||
|
|
||||||
|
export const ProfileSubscriptionsPage = (props: PageProps) => {
|
||||||
|
return (
|
||||||
|
<PageWrap>
|
||||||
|
<div class="wide-container">
|
||||||
|
<div class="shift-content">
|
||||||
|
<div class="left-col">
|
||||||
|
<div class={clsx('left-navigation', styles.leftNavigation)}>
|
||||||
|
<ProfileSettingsNavigation />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 col-lg-9 col-xl-8">
|
||||||
|
<h1>Подписки</h1>
|
||||||
|
<p class="description">Здесь можно управлять всеми своими подписками на сайте.</p>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<ul class="view-switcher">
|
||||||
|
<li class="selected">
|
||||||
|
<a href="#">Все</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">Авторы</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">Темы</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">Сообщества</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">Коллекции</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class={clsx('pretty-form__item', styles.searchContainer)}>
|
||||||
|
<SearchField onChange={() => console.log('nothing')} class={styles.searchField} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={clsx(stylesSettings.settingsList, styles.topicsList)}>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox1" id="checkbox1" />
|
||||||
|
<label for="checkbox1" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox1" class={styles.settingsListCell}>
|
||||||
|
Культура
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox2" id="checkbox2" />
|
||||||
|
<label for="checkbox2" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox2" class={styles.settingsListCell}>
|
||||||
|
Eto_ya sam
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox3" id="checkbox3" />
|
||||||
|
<label for="checkbox3" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox3" class={styles.settingsListCell}>
|
||||||
|
Технопарк
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox4" id="checkbox4" />
|
||||||
|
<label for="checkbox4" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox4" class={styles.settingsListCell}>
|
||||||
|
Лучшее
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox5" id="checkbox5" />
|
||||||
|
<label for="checkbox5" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox5" class={styles.settingsListCell}>
|
||||||
|
Реклама
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox6" id="checkbox6" />
|
||||||
|
<label for="checkbox6" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox6" class={styles.settingsListCell}>
|
||||||
|
Искусство
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox7" id="checkbox7" />
|
||||||
|
<label for="checkbox7" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox7" class={styles.settingsListCell}>
|
||||||
|
Общество
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class={stylesSettings.settingsListRow}>
|
||||||
|
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
||||||
|
<input type="checkbox" name="checkbox8" id="checkbox8" />
|
||||||
|
<label for="checkbox8" />
|
||||||
|
</div>
|
||||||
|
<label for="checkbox8" class={styles.settingsListCell}>
|
||||||
|
Личный опыт
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<button class="button button--submit">Сохранить настройки</button>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageWrap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for lazy loading
|
||||||
|
export default ProfileSubscriptionsPage
|
|
@ -1,7 +1,17 @@
|
||||||
h4 {
|
h4,
|
||||||
|
h5 {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
@include font-size(2.4rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
@include font-size(1.7rem);
|
||||||
|
margin: 0 0 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.avatarContainer {
|
.avatarContainer {
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -103,3 +113,77 @@ h4 {
|
||||||
.discoursNameField {
|
.discoursNameField {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.leftNavigation {
|
||||||
|
top: 9rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passwordToggleControl {
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.passwordInput {
|
||||||
|
padding-right: 3em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchContainer {
|
||||||
|
margin-top: 2.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchField {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
label:first-child {
|
||||||
|
opacity: 0.5;
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.topicsList {
|
||||||
|
label {
|
||||||
|
@include font-size(1.7rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.topicsListItem {
|
||||||
|
padding-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.socialButton {
|
||||||
|
color: #000;
|
||||||
|
display: flex;
|
||||||
|
padding: 0.8em 1em;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.socialButtonApple {
|
||||||
|
&:hover {
|
||||||
|
.icon {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
filter: invert(0);
|
||||||
|
transition: filter 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,8 @@ import { InboxPage } from './Pages/InboxPage'
|
||||||
import { LayoutShoutsPage } from './Pages/LayoutShoutsPage'
|
import { LayoutShoutsPage } from './Pages/LayoutShoutsPage'
|
||||||
import { SessionProvider } from '../context/session'
|
import { SessionProvider } from '../context/session'
|
||||||
import { ProfileSettingsPage } from './Pages/profile/ProfileSettingsPage'
|
import { ProfileSettingsPage } from './Pages/profile/ProfileSettingsPage'
|
||||||
|
import { ProfileSecurityPage } from './Pages/profile/ProfileSecurityPage'
|
||||||
|
import { ProfileSubscriptionsPage } from './Pages/profile/ProfileSubscriptionsPage'
|
||||||
|
|
||||||
// TODO: lazy load
|
// TODO: lazy load
|
||||||
// const SomePage = lazy(() => import('./Pages/SomePage'))
|
// const SomePage = lazy(() => import('./Pages/SomePage'))
|
||||||
|
@ -60,7 +62,9 @@ const pagesMap: Record<keyof Routes, Component<PageProps>> = {
|
||||||
principles: PrinciplesPage,
|
principles: PrinciplesPage,
|
||||||
termsOfUse: TermsOfUsePage,
|
termsOfUse: TermsOfUsePage,
|
||||||
thanks: ThanksPage,
|
thanks: ThanksPage,
|
||||||
profileSettings: ProfileSettingsPage
|
profileSettings: ProfileSettingsPage,
|
||||||
|
profileSecurity: ProfileSecurityPage,
|
||||||
|
profileSubscriptions: ProfileSubscriptionsPage
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Root = (props: PageProps) => {
|
export const Root = (props: PageProps) => {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import '../../styles/FeedSettings.scss'
|
import styles from '../../styles/FeedSettings.module.scss'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
// type FeedSettingsSearchParams = {
|
// type FeedSettingsSearchParams = {
|
||||||
|
@ -27,20 +27,20 @@ export const FeedSettingsView = (_props) => {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="settings-list">
|
<div class={styles.settingsList}>
|
||||||
<div class="settings-list__row">
|
<div class={styles.settingsListRow}>
|
||||||
<h2>Общее</h2>
|
<h2>Общее</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-list__row">
|
<div class={styles.settingsListRow}>
|
||||||
<label for="checkbox1" class="settings-list__cell">
|
<label for="checkbox1" class={styles.settingsListCell}>
|
||||||
Комментарии к моим постам
|
Комментарии к моим постам
|
||||||
</label>
|
</label>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input type="checkbox" name="checkbox1" id="checkbox1" />
|
<input type="checkbox" name="checkbox1" id="checkbox1" />
|
||||||
<label for="checkbox1" />
|
<label for="checkbox1" />
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="checkbox1-notification"
|
name="checkbox1-notification"
|
||||||
|
@ -51,15 +51,15 @@ export const FeedSettingsView = (_props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-list__row">
|
<div class={styles.settingsListRow}>
|
||||||
<label for="checkbox2" class="settings-list__cell">
|
<label for="checkbox2" class={styles.settingsListCell}>
|
||||||
новые подписчики
|
новые подписчики
|
||||||
</label>
|
</label>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input type="checkbox" name="checkbox2" id="checkbox2" />
|
<input type="checkbox" name="checkbox2" id="checkbox2" />
|
||||||
<label for="checkbox2" />
|
<label for="checkbox2" />
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="checkbox2-notification"
|
name="checkbox2-notification"
|
||||||
|
@ -70,15 +70,15 @@ export const FeedSettingsView = (_props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-list__row">
|
<div class={styles.settingsListRow}>
|
||||||
<label for="checkbox3" class="settings-list__cell">
|
<label for="checkbox3" class={styles.settingsListCell}>
|
||||||
добавление моих текстов в коллекции
|
добавление моих текстов в коллекции
|
||||||
</label>
|
</label>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input type="checkbox" name="checkbox3" id="checkbox3" />
|
<input type="checkbox" name="checkbox3" id="checkbox3" />
|
||||||
<label for="checkbox3" />
|
<label for="checkbox3" />
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="checkbox3-notification"
|
name="checkbox3-notification"
|
||||||
|
@ -89,19 +89,19 @@ export const FeedSettingsView = (_props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-list__row">
|
<div class={styles.settingsListRow}>
|
||||||
<h2>Мои подписки</h2>
|
<h2>Мои подписки</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="settings-list__row">
|
<div class={styles.settingsListRow}>
|
||||||
<label for="checkbox4" class="settings-list__cell">
|
<label for="checkbox4" class={styles.settingsListCell}>
|
||||||
добавление моих текстов в коллекции
|
добавление моих текстов в коллекции
|
||||||
</label>
|
</label>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input type="checkbox" name="checkbox4" id="checkbox4" />
|
<input type="checkbox" name="checkbox4" id="checkbox4" />
|
||||||
<label for="checkbox4" />
|
<label for="checkbox4" />
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-list__cell">
|
<div class={styles.settingsListCell}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="checkbox4-notification"
|
name="checkbox4-notification"
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
transition: box-shadow 0.3s;
|
transition: box-shadow 0.3s;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&:focus {
|
+ label {
|
||||||
box-shadow: 0 3px 0 #ccc;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
import styles from './SearchField.module.scss'
|
import styles from './SearchField.module.scss'
|
||||||
import { Icon } from './Icon'
|
import { Icon } from './Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
type SearchFieldProps = {
|
type SearchFieldProps = {
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
|
class?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchField = (props: SearchFieldProps) => {
|
export const SearchField = (props: SearchFieldProps) => {
|
||||||
const handleInputChange = (event) => props.onChange(event.target.value.trim())
|
const handleInputChange = (event) => props.onChange(event.target.value.trim())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={styles.searchField}>
|
<div class={clsx(styles.searchField, props.class)}>
|
||||||
<label for="search-field">
|
<label for="search-field">
|
||||||
<Icon name="search" class={styles.icon} />
|
<Icon name="search" class={styles.icon} />
|
||||||
</label>
|
</label>
|
||||||
<input id="search-field" type="text" onInput={handleInputChange} placeholder={t('Search')} />
|
<input
|
||||||
|
id="search-field"
|
||||||
|
type="text"
|
||||||
|
class="search-input"
|
||||||
|
onInput={handleInputChange}
|
||||||
|
placeholder={t('Search')}
|
||||||
|
/>
|
||||||
|
<label for="search-field">Поиск</label>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,3 +22,10 @@ export type RootSearchParams = {
|
||||||
modal: string
|
modal: string
|
||||||
lang: string
|
lang: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UploadFile = {
|
||||||
|
source: string
|
||||||
|
name: string
|
||||||
|
size: number
|
||||||
|
file: File
|
||||||
|
}
|
||||||
|
|
64
src/context/profile.tsx
Normal file
64
src/context/profile.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import { createEffect, createMemo } from 'solid-js'
|
||||||
|
import { createStore } from 'solid-js/store'
|
||||||
|
import { useSession } from './session'
|
||||||
|
import { loadAuthor, useAuthorsStore } from '../stores/zine/authors'
|
||||||
|
import { apiClient } from '../utils/apiClient'
|
||||||
|
import type { ProfileInput } from '../graphql/types.gen'
|
||||||
|
|
||||||
|
const submit = async (profile: ProfileInput) => {
|
||||||
|
try {
|
||||||
|
await apiClient.updateProfile(profile)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useProfileForm = () => {
|
||||||
|
const { session } = useSession()
|
||||||
|
const currentSlug = createMemo(() => session()?.user?.slug)
|
||||||
|
const { authorEntities } = useAuthorsStore({ authors: [] })
|
||||||
|
const currentAuthor = createMemo(() => authorEntities()[currentSlug()])
|
||||||
|
|
||||||
|
const [form, setForm] = createStore<ProfileInput>({
|
||||||
|
name: '',
|
||||||
|
bio: '',
|
||||||
|
userpic: '',
|
||||||
|
links: []
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(async () => {
|
||||||
|
if (!currentSlug()) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
await loadAuthor({ slug: currentSlug() })
|
||||||
|
setForm({
|
||||||
|
name: currentAuthor()?.name,
|
||||||
|
bio: currentAuthor()?.bio,
|
||||||
|
userpic: currentAuthor()?.userpic,
|
||||||
|
links: currentAuthor()?.links
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateFormField = (fieldName: string, value: string, remove?: boolean) => {
|
||||||
|
if (fieldName === 'links') {
|
||||||
|
if (remove) {
|
||||||
|
setForm(
|
||||||
|
'links',
|
||||||
|
form.links.filter((item) => item !== value)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
setForm((prev) => ({ ...prev, links: [...prev.links, value] }))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setForm({
|
||||||
|
[fieldName]: value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { form, submit, updateFormField }
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useProfileForm }
|
|
@ -1,19 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
// WARNING: need Auth header
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
mutation ProfileUpdateMutation($user: User!) {
|
|
||||||
profileUpdate(user: $user) {
|
|
||||||
error
|
|
||||||
token
|
|
||||||
user {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
bio
|
|
||||||
# links
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
13
src/graphql/mutation/update-profile.ts
Normal file
13
src/graphql/mutation/update-profile.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
// WARNING: need Auth header
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
mutation ProfileUpdateMutation($profile: ProfileInput!) {
|
||||||
|
updateProfile(profile: $profile) {
|
||||||
|
error
|
||||||
|
author {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -189,5 +189,19 @@
|
||||||
"create_group": "Создать группу",
|
"create_group": "Создать группу",
|
||||||
"discourse_theme": "Тема дискурса",
|
"discourse_theme": "Тема дискурса",
|
||||||
"cancel": "Отмена",
|
"cancel": "Отмена",
|
||||||
"group_chat": "Общий чат"
|
"group_chat": "Общий чат",
|
||||||
|
"Profile settings": "Настройки профиля",
|
||||||
|
"Here you can customize your profile the way you want.": "Здесь можно настроить свой профиль так, как вы хотите.",
|
||||||
|
"Userpic": "Аватар",
|
||||||
|
"Name": "Имя",
|
||||||
|
"Your name will appear on your profile page and as your signature in publications, comments and responses.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах",
|
||||||
|
"Address on Discourse": "Адрес на Дискурсе",
|
||||||
|
"Sorry, this address is already taken, please choose another one.": "Увы, этот адрес уже занят, выберите другой",
|
||||||
|
"Introduce": "Представление",
|
||||||
|
"About myself": "О себе",
|
||||||
|
"How can I help/skills": "Чем могу помочь/навыки",
|
||||||
|
"Where": "Откуда",
|
||||||
|
"Date of Birth": "Дата рождения",
|
||||||
|
"Social networks": "Социальные сети",
|
||||||
|
"Save settings": "Сохранить настройки"
|
||||||
}
|
}
|
||||||
|
|
12
src/pages/profile/security.astro
Normal file
12
src/pages/profile/security.astro
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
import Prerendered from '../../main.astro'
|
||||||
|
import { Root } from '../../components/Root'
|
||||||
|
import { initRouter } from '../../stores/router'
|
||||||
|
|
||||||
|
const { pathname, search } = Astro.url
|
||||||
|
initRouter(pathname, search)
|
||||||
|
---
|
||||||
|
|
||||||
|
<Prerendered>
|
||||||
|
<Root client:load />
|
||||||
|
</Prerendered>
|
12
src/pages/profile/subscriptions.astro
Normal file
12
src/pages/profile/subscriptions.astro
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
---
|
||||||
|
import Prerendered from '../../main.astro'
|
||||||
|
import { Root } from '../../components/Root'
|
||||||
|
import { initRouter } from '../../stores/router'
|
||||||
|
|
||||||
|
const { pathname, search } = Astro.url
|
||||||
|
initRouter(pathname, search)
|
||||||
|
---
|
||||||
|
|
||||||
|
<Prerendered>
|
||||||
|
<Root client:load />
|
||||||
|
</Prerendered>
|
|
@ -28,6 +28,8 @@ export interface Routes {
|
||||||
expo: 'layout'
|
expo: 'layout'
|
||||||
inbox: void // TODO: добавить ID текущего юзера
|
inbox: void // TODO: добавить ID текущего юзера
|
||||||
profileSettings: void
|
profileSettings: void
|
||||||
|
profileSecurity: void
|
||||||
|
profileSubscriptions: void
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchParamsStore = createSearchParams()
|
const searchParamsStore = createSearchParams()
|
||||||
|
@ -55,7 +57,9 @@ const routerStore = createRouter<Routes>(
|
||||||
termsOfUse: '/about/terms-of-use',
|
termsOfUse: '/about/terms-of-use',
|
||||||
thanks: '/about/thanks',
|
thanks: '/about/thanks',
|
||||||
expo: '/expo/:layout',
|
expo: '/expo/:layout',
|
||||||
profileSettings: '/profile/settings'
|
profileSettings: '/profile/settings',
|
||||||
|
profileSecurity: '/profile/security',
|
||||||
|
profileSubscriptions: '/profile/subscriptions'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
search: false,
|
search: false,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.settings-list {
|
.settingsList {
|
||||||
display: table;
|
display: table;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
height: 2.8rem;
|
height: 2.8rem;
|
||||||
position: static;
|
position: static;
|
||||||
width: 2.8rem;
|
width: 2.8rem;
|
||||||
vertical-align: middle;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,11 +46,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-list__row {
|
.settingsListRow {
|
||||||
display: table-row;
|
display: table-row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-list__cell {
|
.settingsListCell {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
padding: 0 0.5em 1em 0;
|
padding: 0 0.5em 1em 0;
|
||||||
|
|
|
@ -10,7 +10,9 @@ import type {
|
||||||
QueryLoadMessagesByArgs,
|
QueryLoadMessagesByArgs,
|
||||||
MutationCreateChatArgs,
|
MutationCreateChatArgs,
|
||||||
MutationCreateMessageArgs,
|
MutationCreateMessageArgs,
|
||||||
QueryLoadRecipientsArgs
|
QueryLoadRecipientsArgs,
|
||||||
|
User,
|
||||||
|
ProfileInput
|
||||||
} from '../graphql/types.gen'
|
} from '../graphql/types.gen'
|
||||||
import { publicGraphQLClient } from '../graphql/publicGraphQLClient'
|
import { publicGraphQLClient } from '../graphql/publicGraphQLClient'
|
||||||
import { getToken, privateGraphQLClient } from '../graphql/privateGraphQLClient'
|
import { getToken, privateGraphQLClient } from '../graphql/privateGraphQLClient'
|
||||||
|
@ -42,6 +44,7 @@ import shoutsLoadBy from '../graphql/query/articles-load-by'
|
||||||
import shoutLoad from '../graphql/query/article-load'
|
import shoutLoad from '../graphql/query/article-load'
|
||||||
import loadRecipients from '../graphql/query/chat-recipients'
|
import loadRecipients from '../graphql/query/chat-recipients'
|
||||||
import createMessage from '../graphql/mutation/create-chat-message'
|
import createMessage from '../graphql/mutation/create-chat-message'
|
||||||
|
import updateProfile from '../graphql/mutation/update-profile'
|
||||||
|
|
||||||
type ApiErrorCode =
|
type ApiErrorCode =
|
||||||
| 'unknown'
|
| 'unknown'
|
||||||
|
@ -213,6 +216,10 @@ export const apiClient = {
|
||||||
console.debug('getAuthor', response)
|
console.debug('getAuthor', response)
|
||||||
return response.data.getAuthor
|
return response.data.getAuthor
|
||||||
},
|
},
|
||||||
|
updateProfile: async (input: ProfileInput) => {
|
||||||
|
const response = await privateGraphQLClient.mutation(updateProfile, { profile: input }).toPromise()
|
||||||
|
console.debug('updateProfile', response)
|
||||||
|
},
|
||||||
getTopic: async ({ slug }: { slug: string }): Promise<Topic> => {
|
getTopic: async ({ slug }: { slug: string }): Promise<Topic> => {
|
||||||
const response = await publicGraphQLClient.query(topicBySlug, { slug }).toPromise()
|
const response = await publicGraphQLClient.query(topicBySlug, { slug }).toPromise()
|
||||||
return response.data.getTopic
|
return response.data.getTopic
|
||||||
|
|
|
@ -2818,6 +2818,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@solid-primitives/rootless" "^1.2.0"
|
"@solid-primitives/rootless" "^1.2.0"
|
||||||
|
|
||||||
|
"@solid-primitives/upload@^0.0.105":
|
||||||
|
version "0.0.105"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solid-primitives/upload/-/upload-0.0.105.tgz#cf415667c0fae842dbf2470554dc0c28e8ba4fac"
|
||||||
|
integrity sha512-991xLetzr25NIeuAtWpYmJSA7lJ0HSOJT9sl3sRtgpR4+QJEDIsM4lw2iYYpw7XUFGBqqX2CHI5TitvYzy/Maw==
|
||||||
|
dependencies:
|
||||||
|
"@solid-primitives/utils" "^4.0.0"
|
||||||
|
|
||||||
"@solid-primitives/utils@^3.1.0":
|
"@solid-primitives/utils@^3.1.0":
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@solid-primitives/utils/-/utils-3.1.0.tgz#52edf36dabe62eba94f8356c3b9b788234d088a8"
|
resolved "https://registry.yarnpkg.com/@solid-primitives/utils/-/utils-3.1.0.tgz#52edf36dabe62eba94f8356c3b9b788234d088a8"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user