parent
141ab3d0ed
commit
3d6c6ccbc8
3
public/icons/check-subscribed-black.svg
Normal file
3
public/icons/check-subscribed-black.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="13" height="10" viewBox="0 0 13 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 4.5L4.66667 8L12 1" stroke="currentColor" stroke-width="1.5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 178 B |
3
public/icons/close-white.svg
Normal file
3
public/icons/close-white.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99993 6.06066L8.71224 9.77297L9.7729 8.71231L6.06059 5L9.7729 1.28769L8.71224 0.227029L4.99993 3.93934L1.28762 0.227029L0.226959 1.28769L3.93927 5L0.226959 8.71231L1.28762 9.77297L4.99993 6.06066Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 368 B |
|
@ -9,9 +9,11 @@
|
||||||
"Add audio": "Add audio",
|
"Add audio": "Add audio",
|
||||||
"Add blockquote": "Add blockquote",
|
"Add blockquote": "Add blockquote",
|
||||||
"Add comment": "Comment",
|
"Add comment": "Comment",
|
||||||
|
"Here you can manage all your Discourse subscriptions": "Here you can manage all your Discourse subscriptions",
|
||||||
"Add cover": "Add cover",
|
"Add cover": "Add cover",
|
||||||
"Add image": "Add image",
|
"Add image": "Add image",
|
||||||
"Add images": "Add images",
|
"Add images": "Add images",
|
||||||
|
"Collections": "Collections",
|
||||||
"Add intro": "Add intro",
|
"Add intro": "Add intro",
|
||||||
"Add link": "Add link",
|
"Add link": "Add link",
|
||||||
"Add rule": "Add rule",
|
"Add rule": "Add rule",
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
"Confirm": "Подтвердить",
|
"Confirm": "Подтвердить",
|
||||||
"Cooperate": "Соучаствовать",
|
"Cooperate": "Соучаствовать",
|
||||||
"Copy": "Скопировать",
|
"Copy": "Скопировать",
|
||||||
|
"Collections": "Коллекции",
|
||||||
"Copy link": "Скопировать ссылку",
|
"Copy link": "Скопировать ссылку",
|
||||||
"Corrections history": "История правок",
|
"Corrections history": "История правок",
|
||||||
"Create Chat": "Создать чат",
|
"Create Chat": "Создать чат",
|
||||||
|
@ -208,6 +209,7 @@
|
||||||
"Move up": "Переместить вверх",
|
"Move up": "Переместить вверх",
|
||||||
"My feed": "Моя лента",
|
"My feed": "Моя лента",
|
||||||
"My subscriptions": "Подписки",
|
"My subscriptions": "Подписки",
|
||||||
|
"Here you can manage all your Discourse subscriptions": "Здесь можно управлять всеми своими подписками на Дискурсе",
|
||||||
"Name": "Имя",
|
"Name": "Имя",
|
||||||
"Newsletter": "Рассылка",
|
"Newsletter": "Рассылка",
|
||||||
"New literary work": "Новое произведение",
|
"New literary work": "Новое произведение",
|
||||||
|
|
|
@ -17,8 +17,10 @@ import { Modal } from '../../Nav/Modal'
|
||||||
import { showModal } from '../../../stores/ui'
|
import { showModal } from '../../../stores/ui'
|
||||||
import { TopicCard } from '../../Topic/Card'
|
import { TopicCard } from '../../Topic/Card'
|
||||||
import { getNumeralsDeclension } from '../../../utils/getNumeralsDeclension'
|
import { getNumeralsDeclension } from '../../../utils/getNumeralsDeclension'
|
||||||
|
import { SubscriptionFilter } from '../../../pages/types'
|
||||||
|
import { isAuthor } from '../../../utils/isAuthor'
|
||||||
|
import { CheckButton } from '../../_shared/CheckButton'
|
||||||
|
|
||||||
type SubscriptionFilter = 'all' | 'users' | 'topics'
|
|
||||||
type Props = {
|
type Props = {
|
||||||
caption?: string
|
caption?: string
|
||||||
hideWriteButton?: boolean
|
hideWriteButton?: boolean
|
||||||
|
@ -40,10 +42,7 @@ type Props = {
|
||||||
followers?: Author[]
|
followers?: Author[]
|
||||||
following?: Array<Author | Topic>
|
following?: Array<Author | Topic>
|
||||||
showPublicationsCounter?: boolean
|
showPublicationsCounter?: boolean
|
||||||
}
|
minimizeSubscribeButton?: boolean
|
||||||
|
|
||||||
function isAuthor(value: Author | Topic): value is Author {
|
|
||||||
return 'name' in value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AuthorCard = (props: Props) => {
|
export const AuthorCard = (props: Props) => {
|
||||||
|
@ -276,12 +275,35 @@ export const AuthorCard = (props: Props) => {
|
||||||
<For each={props.author.links}>{(link) => <a href={link} />}</For>
|
<For each={props.author.links}>{(link) => <a href={link} />}</For>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
<Show when={!props.minimizeSubscribeButton}>
|
||||||
<Show
|
<Show
|
||||||
when={subscribed()}
|
when={subscribed()}
|
||||||
fallback={
|
fallback={
|
||||||
|
<button
|
||||||
|
onClick={handleSubscribe}
|
||||||
|
class={clsx('button', styles.button)}
|
||||||
|
classList={{
|
||||||
|
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
|
||||||
|
'button--subscribe': !props.isAuthorsList,
|
||||||
|
'button--subscribe-topic': props.isAuthorsList || props.isTextButton,
|
||||||
|
[styles.buttonWrite]: props.isAuthorsList || props.isTextButton,
|
||||||
|
[styles.isSubscribing]: isSubscribing()
|
||||||
|
}}
|
||||||
|
disabled={isSubscribing()}
|
||||||
|
>
|
||||||
|
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
|
||||||
|
<Icon name="author-subscribe" class={styles.icon} />
|
||||||
|
</Show>
|
||||||
|
<Show when={props.isTextButton || props.isAuthorPage}>
|
||||||
|
<span class={clsx(styles.buttonLabel, styles.buttonLabelVisible)}>
|
||||||
|
{t('Follow')}
|
||||||
|
</span>
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
onClick={handleSubscribe}
|
onClick={() => subscribe(false)}
|
||||||
class={clsx('button', styles.button)}
|
class={clsx('button', styles.button)}
|
||||||
classList={{
|
classList={{
|
||||||
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
|
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
|
||||||
|
@ -293,52 +315,37 @@ export const AuthorCard = (props: Props) => {
|
||||||
disabled={isSubscribing()}
|
disabled={isSubscribing()}
|
||||||
>
|
>
|
||||||
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
|
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
|
||||||
<Icon name="author-subscribe" class={styles.icon} />
|
<Icon name="author-unsubscribe" class={styles.icon} />
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={props.isTextButton || props.isAuthorPage}>
|
<Show when={props.isTextButton || props.isAuthorPage}>
|
||||||
<span class={clsx(styles.buttonLabel, styles.buttonLabelVisible)}>
|
<span
|
||||||
{t('Follow')}
|
class={clsx(
|
||||||
|
styles.buttonLabel,
|
||||||
|
styles.buttonLabelVisible,
|
||||||
|
styles.buttonUnfollowLabel
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('Unfollow')}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class={clsx(
|
||||||
|
styles.buttonLabel,
|
||||||
|
styles.buttonLabelVisible,
|
||||||
|
styles.buttonSubscribedLabel
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{t('You are subscribed')}
|
||||||
</span>
|
</span>
|
||||||
</Show>
|
</Show>
|
||||||
</button>
|
</button>
|
||||||
}
|
</Show>
|
||||||
>
|
</Show>
|
||||||
<button
|
<Show when={props.minimizeSubscribeButton}>
|
||||||
|
<CheckButton
|
||||||
|
text={t('Follow')}
|
||||||
|
checked={subscribed()}
|
||||||
onClick={() => subscribe(false)}
|
onClick={() => subscribe(false)}
|
||||||
class={clsx('button', styles.button)}
|
/>
|
||||||
classList={{
|
|
||||||
[styles.buttonSubscribe]: !props.isAuthorsList && !props.isTextButton,
|
|
||||||
'button--subscribe': !props.isAuthorsList,
|
|
||||||
'button--subscribe-topic': props.isAuthorsList || props.isTextButton,
|
|
||||||
[styles.buttonWrite]: props.isAuthorsList || props.isTextButton,
|
|
||||||
[styles.isSubscribing]: isSubscribing()
|
|
||||||
}}
|
|
||||||
disabled={isSubscribing()}
|
|
||||||
>
|
|
||||||
<Show when={!props.isAuthorsList && !props.isTextButton && !props.isAuthorPage}>
|
|
||||||
<Icon name="author-unsubscribe" class={styles.icon} />
|
|
||||||
</Show>
|
|
||||||
<Show when={props.isTextButton || props.isAuthorPage}>
|
|
||||||
<span
|
|
||||||
class={clsx(
|
|
||||||
styles.buttonLabel,
|
|
||||||
styles.buttonLabelVisible,
|
|
||||||
styles.buttonUnfollowLabel
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{t('Unfollow')}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class={clsx(
|
|
||||||
styles.buttonLabel,
|
|
||||||
styles.buttonLabelVisible,
|
|
||||||
styles.buttonSubscribedLabel
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{t('You are subscribed')}
|
|
||||||
</span>
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={!props.hideWriteButton}>
|
<Show when={!props.hideWriteButton}>
|
||||||
|
@ -425,7 +432,7 @@ export const AuthorCard = (props: Props) => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-24">
|
<div class="col-24">
|
||||||
<For each={following()}>
|
<For each={following()}>
|
||||||
{(subscription: Author | Topic) =>
|
{(subscription) =>
|
||||||
isAuthor(subscription) ? (
|
isAuthor(subscription) ? (
|
||||||
<AuthorCard
|
<AuthorCard
|
||||||
author={subscription}
|
author={subscription}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
.navigationHeader {
|
|
||||||
@include font-size(1.8rem);
|
|
||||||
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navigation {
|
|
||||||
@include font-size(1.4rem);
|
|
||||||
}
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
.navigationHeader {
|
||||||
|
@include font-size(1.8rem);
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation {
|
||||||
|
@include font-size(1.4rem);
|
||||||
|
|
||||||
|
.active {
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--default-color-invert);
|
||||||
|
background: var(--background-color-invert);
|
||||||
|
cursor: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,22 @@
|
||||||
import styles from './ProfileSettingsNavigation.module.scss'
|
import styles from './ProfileSettingsNavigation.module.scss'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
import { useRouter } from '../../../stores/router'
|
||||||
|
|
||||||
export default () => {
|
export const ProfileSettingsNavigation = () => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
const { page } = useRouter()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h4 class={styles.navigationHeader}>{t('Settings')}</h4>
|
<h4 class={styles.navigationHeader}>{t('Settings')}</h4>
|
||||||
<ul class={clsx(styles.navigation, 'nodash')}>
|
<ul class={clsx(styles.navigation, 'nodash')}>
|
||||||
<li>
|
<li class={clsx({ [styles.active]: page().route === 'profileSettings' })}>
|
||||||
<a href="/profile/settings">{t('Profile')}</a>
|
<a href="/profile/settings">{t('Profile')}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class={clsx({ [styles.active]: page().route === 'profileSubscriptions' })}>
|
||||||
<a href="/profile/subscriptions">{t('Subscriptions')}</a>
|
<a href="/profile/subscriptions">{t('Subscriptions')}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class={clsx({ [styles.active]: page().route === 'profileSecurity' })}>
|
||||||
<a href="/profile/security">{t('Security')}</a>
|
<a href="/profile/security">{t('Security')}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
1
src/components/Nav/ProfileSettingsNavigation/index.ts
Normal file
1
src/components/Nav/ProfileSettingsNavigation/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { ProfileSettingsNavigation } from './ProfileSettingsNavigation'
|
|
@ -11,6 +11,7 @@ import { ShowOnlyOnClient } from '../_shared/ShowOnlyOnClient'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
import { CardTopic } from '../Feed/CardTopic'
|
import { CardTopic } from '../Feed/CardTopic'
|
||||||
|
import { CheckButton } from '../_shared/CheckButton'
|
||||||
|
|
||||||
interface TopicProps {
|
interface TopicProps {
|
||||||
topic: Topic
|
topic: Topic
|
||||||
|
@ -24,6 +25,7 @@ interface TopicProps {
|
||||||
showPublications?: boolean
|
showPublications?: boolean
|
||||||
showDescription?: boolean
|
showDescription?: boolean
|
||||||
isCardMode?: boolean
|
isCardMode?: boolean
|
||||||
|
minimizeSubscribeButton?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TopicCard = (props: TopicProps) => {
|
export const TopicCard = (props: TopicProps) => {
|
||||||
|
@ -105,27 +107,34 @@ export const TopicCard = (props: TopicProps) => {
|
||||||
>
|
>
|
||||||
<ShowOnlyOnClient>
|
<ShowOnlyOnClient>
|
||||||
<Show when={isSessionLoaded()}>
|
<Show when={isSessionLoaded()}>
|
||||||
<button
|
<Show
|
||||||
onClick={handleSubscribe}
|
when={!props.minimizeSubscribeButton}
|
||||||
class="button--light button--subscribe-topic"
|
fallback={
|
||||||
classList={{
|
<CheckButton text={t('Follow')} checked={subscribed()} onClick={handleSubscribe} />
|
||||||
[styles.isSubscribing]: isSubscribing(),
|
}
|
||||||
[styles.isSubscribed]: subscribed()
|
|
||||||
}}
|
|
||||||
disabled={isSubscribing()}
|
|
||||||
>
|
>
|
||||||
<Show when={props.iconButton}>
|
<button
|
||||||
<Show when={subscribed()} fallback="+">
|
onClick={handleSubscribe}
|
||||||
<Icon name="check-subscribed" />
|
class="button--light button--subscribe-topic"
|
||||||
|
classList={{
|
||||||
|
[styles.isSubscribing]: isSubscribing(),
|
||||||
|
[styles.isSubscribed]: subscribed()
|
||||||
|
}}
|
||||||
|
disabled={isSubscribing()}
|
||||||
|
>
|
||||||
|
<Show when={props.iconButton}>
|
||||||
|
<Show when={subscribed()} fallback="+">
|
||||||
|
<Icon name="check-subscribed" />
|
||||||
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
<Show when={!props.iconButton}>
|
||||||
<Show when={!props.iconButton}>
|
<Show when={subscribed()} fallback={t('Follow')}>
|
||||||
<Show when={subscribed()} fallback={t('Follow')}>
|
<span class={styles.buttonUnfollowLabel}>{t('Unfollow')}</span>
|
||||||
<span class={styles.buttonUnfollowLabel}>{t('Unfollow')}</span>
|
<span class={styles.buttonSubscribedLabel}>{t('You are subscribed')}</span>
|
||||||
<span class={styles.buttonSubscribedLabel}>{t('You are subscribed')}</span>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</button>
|
||||||
</button>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</ShowOnlyOnClient>
|
</ShowOnlyOnClient>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,6 +11,7 @@ import styles from '../../styles/AllTopics.module.scss'
|
||||||
import { SearchField } from '../_shared/SearchField'
|
import { SearchField } from '../_shared/SearchField'
|
||||||
import { scrollHandler } from '../../utils/scroll'
|
import { scrollHandler } from '../../utils/scroll'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
|
import { dummyFilter } from '../../utils/dummyFilter'
|
||||||
|
|
||||||
type AllAuthorsPageSearchParams = {
|
type AllAuthorsPageSearchParams = {
|
||||||
by: '' | 'name' | 'shouts' | 'followers'
|
by: '' | 'name' | 'shouts' | 'followers'
|
||||||
|
@ -69,26 +70,7 @@ export const AllAuthorsView = (props: AllAuthorsViewProps) => {
|
||||||
const subscribed = (s) => Boolean(session()?.news?.authors && session()?.news?.authors?.includes(s || ''))
|
const subscribed = (s) => Boolean(session()?.news?.authors && session()?.news?.authors?.includes(s || ''))
|
||||||
|
|
||||||
const filteredAuthors = createMemo(() => {
|
const filteredAuthors = createMemo(() => {
|
||||||
let q = searchQuery().toLowerCase()
|
return dummyFilter(sortedAuthors(), searchQuery(), lang())
|
||||||
|
|
||||||
if (q.length === 0) {
|
|
||||||
return sortedAuthors()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lang() === 'ru') q = translit(q)
|
|
||||||
|
|
||||||
return sortedAuthors().filter((author) => {
|
|
||||||
if (author.slug.split('-').some((w) => w.startsWith(q))) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = author.name.toLowerCase()
|
|
||||||
if (lang() === 'ru') {
|
|
||||||
name = translit(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return name.split(' ').some((word) => word.startsWith(q))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
|
const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
|
||||||
|
@ -186,7 +168,7 @@ export const AllAuthorsView = (props: AllAuthorsViewProps) => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-20 col-xl-18">
|
<div class="col-lg-20 col-xl-18">
|
||||||
<AuthorCard
|
<AuthorCard
|
||||||
author={author}
|
author={author as Author}
|
||||||
hasLink={true}
|
hasLink={true}
|
||||||
subscribed={subscribed(author.slug)}
|
subscribed={subscribed(author.slug)}
|
||||||
noSocialButtons={true}
|
noSocialButtons={true}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { SearchField } from '../_shared/SearchField'
|
||||||
import { scrollHandler } from '../../utils/scroll'
|
import { scrollHandler } from '../../utils/scroll'
|
||||||
import { StatMetrics } from '../_shared/StatMetrics'
|
import { StatMetrics } from '../_shared/StatMetrics'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
|
import { dummyFilter } from '../../utils/dummyFilter'
|
||||||
|
|
||||||
type AllTopicsPageSearchParams = {
|
type AllTopicsPageSearchParams = {
|
||||||
by: 'shouts' | 'authors' | 'title' | ''
|
by: 'shouts' | 'authors' | 'title' | ''
|
||||||
|
@ -66,32 +67,9 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
|
||||||
const subscribed = (s) => Boolean(session()?.news?.topics && session()?.news?.topics?.includes(s || ''))
|
const subscribed = (s) => Boolean(session()?.news?.topics && session()?.news?.topics?.includes(s || ''))
|
||||||
|
|
||||||
const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
|
const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = createSignal('')
|
const [searchQuery, setSearchQuery] = createSignal('')
|
||||||
|
|
||||||
const filteredResults = createMemo(() => {
|
const filteredResults = createMemo(() => {
|
||||||
/* very stupid filter by string algorithm with no deps */
|
return dummyFilter(sortedTopics(), searchQuery(), lang())
|
||||||
let q = searchQuery().toLowerCase()
|
|
||||||
if (q.length === 0) {
|
|
||||||
return sortedTopics()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lang() === 'ru') {
|
|
||||||
q = translit(q)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sortedTopics().filter((topic) => {
|
|
||||||
if (topic.slug.split('-').some((w) => w.startsWith(q))) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
let title = topic.title.toLowerCase()
|
|
||||||
if (lang() === 'ru') {
|
|
||||||
title = translit(title)
|
|
||||||
}
|
|
||||||
|
|
||||||
return title.split(' ').some((word) => word.startsWith(q))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const AllTopicsHead = () => (
|
const AllTopicsHead = () => (
|
||||||
|
@ -180,7 +158,7 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
|
||||||
{(topic) => (
|
{(topic) => (
|
||||||
<>
|
<>
|
||||||
<TopicCard
|
<TopicCard
|
||||||
topic={topic}
|
topic={topic as Topic}
|
||||||
compact={false}
|
compact={false}
|
||||||
subscribed={subscribed(topic.slug)}
|
subscribed={subscribed(topic.slug)}
|
||||||
showPublications={true}
|
showPublications={true}
|
||||||
|
|
|
@ -23,7 +23,6 @@ type Props = {
|
||||||
shouts: Shout[]
|
shouts: Shout[]
|
||||||
author: Author
|
author: Author
|
||||||
authorSlug: string
|
authorSlug: string
|
||||||
// route?: 'author' | 'authorComments' | 'authorAbout' | 'authorFollowing' | 'authorFollowers' | string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PRERENDERED_ARTICLES_COUNT = 12
|
export const PRERENDERED_ARTICLES_COUNT = 12
|
||||||
|
@ -40,11 +39,11 @@ export const AuthorView = (props: Props) => {
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
|
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
|
||||||
const [followers, setFollowers] = createSignal<Author[]>([])
|
const [followers, setFollowers] = createSignal<Author[]>([])
|
||||||
const [subscriptions, setSubscriptions] = createSignal<Array<Author | Topic>>([])
|
const [following, setFollowing] = createSignal<Array<Author | Topic>>([])
|
||||||
const [bioWrapper, setBioWrapper] = createSignal<HTMLElement>()
|
|
||||||
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
|
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
|
||||||
|
|
||||||
const bioContainerRef: { current: HTMLDivElement } = { current: null }
|
const bioContainerRef: { current: HTMLDivElement } = { current: null }
|
||||||
|
const bioWrapperRef: { current: HTMLDivElement } = { current: null }
|
||||||
const fetchSubscriptions = async (): Promise<{ authors: Author[]; topics: Topic[] }> => {
|
const fetchSubscriptions = async (): Promise<{ authors: Author[]; topics: Topic[] }> => {
|
||||||
try {
|
try {
|
||||||
const [getAuthors, getTopics] = await Promise.all([
|
const [getAuthors, getTopics] = await Promise.all([
|
||||||
|
@ -62,7 +61,7 @@ export const AuthorView = (props: Props) => {
|
||||||
|
|
||||||
const checkBioHeight = () => {
|
const checkBioHeight = () => {
|
||||||
if (bioContainerRef.current) {
|
if (bioContainerRef.current) {
|
||||||
setShowExpandBioControl(bioContainerRef.current.offsetHeight > bioWrapper().offsetHeight)
|
setShowExpandBioControl(bioContainerRef.current.offsetHeight > bioWrapperRef.current.offsetHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +80,7 @@ export const AuthorView = (props: Props) => {
|
||||||
await loadMore()
|
await loadMore()
|
||||||
}
|
}
|
||||||
const { authors, topics } = await fetchSubscriptions()
|
const { authors, topics } = await fetchSubscriptions()
|
||||||
setSubscriptions([...authors, ...topics])
|
setFollowing([...authors, ...topics])
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
|
@ -131,7 +130,7 @@ export const AuthorView = (props: Props) => {
|
||||||
author={author()}
|
author={author()}
|
||||||
isAuthorPage={true}
|
isAuthorPage={true}
|
||||||
followers={followers()}
|
followers={followers()}
|
||||||
following={subscriptions()}
|
following={following()}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<div class={clsx(styles.groupControls, 'row')}>
|
<div class={clsx(styles.groupControls, 'row')}>
|
||||||
|
@ -170,7 +169,7 @@ export const AuthorView = (props: Props) => {
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-20 col-lg-18">
|
<div class="col-md-20 col-lg-18">
|
||||||
<div
|
<div
|
||||||
ref={setBioWrapper}
|
ref={(el) => (bioWrapperRef.current = el)}
|
||||||
class={styles.longBio}
|
class={styles.longBio}
|
||||||
classList={{ [styles.longBioExpanded]: isBioExpanded() }}
|
classList={{ [styles.longBioExpanded]: isBioExpanded() }}
|
||||||
>
|
>
|
||||||
|
|
30
src/components/_shared/CheckButton/CheckButton.module.scss
Normal file
30
src/components/_shared/CheckButton/CheckButton.module.scss
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
.CheckButton {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 32px;
|
||||||
|
min-width: 33px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: var(--background-color);
|
||||||
|
color: var(--default-color);
|
||||||
|
border: 2px solid var(--default-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
.close {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--background-color-invert);
|
||||||
|
color: var(--default-color-invert);
|
||||||
|
.check {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.close {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/components/_shared/CheckButton/CheckButton.tsx
Normal file
38
src/components/_shared/CheckButton/CheckButton.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './CheckButton.module.scss'
|
||||||
|
import { Icon } from '../Icon'
|
||||||
|
import { createSignal, Show } from 'solid-js'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
class?: string
|
||||||
|
checked: boolean
|
||||||
|
text: string
|
||||||
|
onClick: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signed - check mark icon
|
||||||
|
// On hover - cross icon
|
||||||
|
// If you clicked on the cross, you unsubscribed. Then the “Subscribe” button appears
|
||||||
|
|
||||||
|
export const CheckButton = (props: Props) => {
|
||||||
|
const [clicked, setClicked] = createSignal(!props.checked)
|
||||||
|
const handleClick = () => {
|
||||||
|
props.onClick()
|
||||||
|
setClicked((prev) => !prev)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<button type="button" class={clsx(styles.CheckButton, props.class)} onClick={handleClick}>
|
||||||
|
<Show
|
||||||
|
when={clicked()}
|
||||||
|
fallback={
|
||||||
|
<>
|
||||||
|
<Icon name="check-subscribed-black" class={styles.check} />
|
||||||
|
<Icon name="close-white" class={styles.close} />
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/_shared/CheckButton/index.ts
Normal file
1
src/components/_shared/CheckButton/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { CheckButton } from './CheckButton'
|
|
@ -1,6 +1,23 @@
|
||||||
.searchField {
|
.searchField {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.bordered {
|
||||||
|
border: 2px solid var(--black-100);
|
||||||
|
padding: 10px 0 12px 10px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-right: 40px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: none;
|
border: none;
|
|
@ -1,19 +1,20 @@
|
||||||
import styles from './SearchField.module.scss'
|
import styles from './SearchField.module.scss'
|
||||||
import { Icon } from './Icon'
|
import { Icon } from '../Icon'
|
||||||
|
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
|
||||||
type SearchFieldProps = {
|
type Props = {
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
class?: string
|
class?: string
|
||||||
|
variant?: 'bordered'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchField = (props: SearchFieldProps) => {
|
export const SearchField = (props: Props) => {
|
||||||
const handleInputChange = (event) => props.onChange(event.target.value.trim())
|
const handleInputChange = (event) => props.onChange(event.target.value.trim())
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
return (
|
return (
|
||||||
<div class={clsx(styles.searchField, props.class)}>
|
<div class={clsx(styles.searchField, props.class, { [styles.bordered]: props.variant === 'bordered' })}>
|
||||||
<label for="search-field">
|
<label for="search-field">
|
||||||
<Icon name="search" class={styles.icon} />
|
<Icon name="search" class={styles.icon} />
|
||||||
</label>
|
</label>
|
||||||
|
@ -24,7 +25,7 @@ export const SearchField = (props: SearchFieldProps) => {
|
||||||
onInput={handleInputChange}
|
onInput={handleInputChange}
|
||||||
placeholder={t('Search')}
|
placeholder={t('Search')}
|
||||||
/>
|
/>
|
||||||
<label for="search-field">Поиск</label>
|
<label for="search-field">{t('Search')}</label>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
1
src/components/_shared/SearchField/index.ts
Normal file
1
src/components/_shared/SearchField/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { SearchField } from './SearchField'
|
|
@ -5,6 +5,12 @@ import { loadAuthor, useAuthorsStore } from '../stores/zine/authors'
|
||||||
import { apiClient } from '../utils/apiClient'
|
import { apiClient } from '../utils/apiClient'
|
||||||
import type { ProfileInput } from '../graphql/types.gen'
|
import type { ProfileInput } from '../graphql/types.gen'
|
||||||
|
|
||||||
|
const userpicUrl = (userpic: string) => {
|
||||||
|
if (userpic.includes('assets.discours.io')) {
|
||||||
|
return userpic.replace('100x', '500x500')
|
||||||
|
}
|
||||||
|
return userpic
|
||||||
|
}
|
||||||
const useProfileForm = () => {
|
const useProfileForm = () => {
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
const currentSlug = createMemo(() => session()?.user?.slug)
|
const currentSlug = createMemo(() => session()?.user?.slug)
|
||||||
|
@ -34,13 +40,12 @@ const useProfileForm = () => {
|
||||||
if (!currentSlug()) return
|
if (!currentSlug()) return
|
||||||
try {
|
try {
|
||||||
await loadAuthor({ slug: currentSlug() })
|
await loadAuthor({ slug: currentSlug() })
|
||||||
|
|
||||||
setForm({
|
setForm({
|
||||||
name: currentAuthor()?.name,
|
name: currentAuthor()?.name,
|
||||||
slug: currentAuthor()?.slug,
|
slug: currentAuthor()?.slug,
|
||||||
bio: currentAuthor()?.bio,
|
bio: currentAuthor()?.bio,
|
||||||
about: currentAuthor()?.about,
|
about: currentAuthor()?.about,
|
||||||
userpic: currentAuthor()?.userpic.replace('100x', '500x500'),
|
userpic: userpicUrl(currentAuthor()?.userpic),
|
||||||
links: currentAuthor()?.links
|
links: currentAuthor()?.links
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -108,12 +108,12 @@ h5 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchField {
|
.searchField {
|
||||||
display: block;
|
margin-bottom: 2rem;
|
||||||
|
|
||||||
label:first-child {
|
label:first-child {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 1em;
|
right: 12px;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
top: 50%;
|
top: 50%;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { PageLayout } from '../../components/_shared/PageLayout'
|
||||||
import styles from './Settings.module.scss'
|
import styles from './Settings.module.scss'
|
||||||
import { Icon } from '../../components/_shared/Icon'
|
import { Icon } from '../../components/_shared/Icon'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
|
import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation'
|
||||||
|
|
||||||
export const ProfileSecurityPage = () => {
|
export const ProfileSecurityPage = () => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { PageLayout } from '../../components/_shared/PageLayout'
|
import { PageLayout } from '../../components/_shared/PageLayout'
|
||||||
import { Icon } from '../../components/_shared/Icon'
|
import { Icon } from '../../components/_shared/Icon'
|
||||||
import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
|
import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation'
|
||||||
import { For, createSignal, Show, onMount, onCleanup, createEffect } from 'solid-js'
|
import { For, createSignal, Show, onMount, onCleanup, createEffect } from 'solid-js'
|
||||||
import deepEqual from 'fast-deep-equal'
|
import deepEqual from 'fast-deep-equal'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
|
@ -2,10 +2,61 @@ import { PageLayout } from '../../components/_shared/PageLayout'
|
||||||
import styles from './Settings.module.scss'
|
import styles from './Settings.module.scss'
|
||||||
import stylesSettings from '../../styles/FeedSettings.module.scss'
|
import stylesSettings from '../../styles/FeedSettings.module.scss'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
|
import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation'
|
||||||
import { SearchField } from '../../components/_shared/SearchField'
|
import { SearchField } from '../../components/_shared/SearchField'
|
||||||
|
import { createEffect, createSignal, For, onMount, Show } from 'solid-js'
|
||||||
|
import { Author, Topic } from '../../graphql/types.gen'
|
||||||
|
import { apiClient } from '../../utils/apiClient'
|
||||||
|
import { useSession } from '../../context/session'
|
||||||
|
import { isAuthor } from '../../utils/isAuthor'
|
||||||
|
import { useLocalize } from '../../context/localize'
|
||||||
|
import { SubscriptionFilter } from '../types'
|
||||||
|
import { Loading } from '../../components/_shared/Loading'
|
||||||
|
import { TopicCard } from '../../components/Topic/Card'
|
||||||
|
import { AuthorCard } from '../../components/Author/AuthorCard'
|
||||||
|
import { dummyFilter } from '../../utils/dummyFilter'
|
||||||
|
|
||||||
export const ProfileSubscriptionsPage = () => {
|
export const ProfileSubscriptionsPage = () => {
|
||||||
|
const { t, lang } = useLocalize()
|
||||||
|
const { user, isAuthenticated } = useSession()
|
||||||
|
const [following, setFollowing] = createSignal<Array<Author | Topic>>([])
|
||||||
|
const [filtered, setFiltered] = createSignal<Array<Author | Topic>>([])
|
||||||
|
const [subscriptionFilter, setSubscriptionFilter] = createSignal<SubscriptionFilter>('all')
|
||||||
|
const [searchQuery, setSearchQuery] = createSignal('')
|
||||||
|
|
||||||
|
const fetchSubscriptions = async () => {
|
||||||
|
try {
|
||||||
|
const [getAuthors, getTopics] = await Promise.all([
|
||||||
|
apiClient.getAuthorFollowingUsers({ slug: user().slug }),
|
||||||
|
apiClient.getAuthorFollowingTopics({ slug: user().slug })
|
||||||
|
])
|
||||||
|
setFollowing([...getAuthors, ...getTopics])
|
||||||
|
setFiltered([...getAuthors, ...getTopics])
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[fetchSubscriptions] :', error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (isAuthenticated()) {
|
||||||
|
await fetchSubscriptions()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (following()) {
|
||||||
|
if (subscriptionFilter() === 'users') {
|
||||||
|
setFiltered(following().filter((s) => 'name' in s))
|
||||||
|
} else if (subscriptionFilter() === 'topics') {
|
||||||
|
setFiltered(following().filter((s) => 'title' in s))
|
||||||
|
} else {
|
||||||
|
setFiltered(following())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFiltered(dummyFilter(following(), searchQuery(), lang()))
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout>
|
<PageLayout>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
|
@ -19,112 +70,65 @@ export const ProfileSubscriptionsPage = () => {
|
||||||
<div class="col-md-19">
|
<div class="col-md-19">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-20 col-lg-18 col-xl-16">
|
<div class="col-md-20 col-lg-18 col-xl-16">
|
||||||
<h1>Подписки</h1>
|
<h1>{t('My subscriptions')}</h1>
|
||||||
<p class="description">Здесь можно управлять всеми своими подписками на сайте.</p>
|
<p class="description">{t('Here you can manage all your Discourse subscriptions')}</p>
|
||||||
|
<Show when={following()} fallback={<Loading />}>
|
||||||
<form>
|
|
||||||
<ul class="view-switcher">
|
<ul class="view-switcher">
|
||||||
<li class="selected">
|
<li class={clsx({ 'view-switcher__item--selected': subscriptionFilter() === 'all' })}>
|
||||||
<a href="src/components/Pages/profile#">Все</a>
|
<button type="button" onClick={() => setSubscriptionFilter('all')}>
|
||||||
|
{t('All')}
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class={clsx({ 'view-switcher__item--selected': subscriptionFilter() === 'users' })}>
|
||||||
<a href="src/components/Pages/profile#">Авторы</a>
|
<button type="button" onClick={() => setSubscriptionFilter('users')}>
|
||||||
|
{t('Authors')}
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li
|
||||||
<a href="src/components/Pages/profile#">Темы</a>
|
class={clsx({ 'view-switcher__item--selected': subscriptionFilter() === 'topics' })}
|
||||||
</li>
|
>
|
||||||
<li>
|
<button type="button" onClick={() => setSubscriptionFilter('topics')}>
|
||||||
<a href="src/components/Pages/profile#">Сообщества</a>
|
{t('Topics')}
|
||||||
</li>
|
</button>
|
||||||
<li>
|
|
||||||
<a href="src/components/Pages/profile#">Коллекции</a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class={clsx('pretty-form__item', styles.searchContainer)}>
|
<div class={clsx('pretty-form__item', styles.searchContainer)}>
|
||||||
<SearchField onChange={() => console.log('nothing')} class={styles.searchField} />
|
<SearchField
|
||||||
|
onChange={(value) => setSearchQuery(value)}
|
||||||
|
class={styles.searchField}
|
||||||
|
variant="bordered"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={clsx(stylesSettings.settingsList, styles.topicsList)}>
|
<div class={clsx(stylesSettings.settingsList, styles.topicsList)}>
|
||||||
<div class={stylesSettings.settingsListRow}>
|
<For each={filtered()}>
|
||||||
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
{(followingItem) => (
|
||||||
<input type="checkbox" name="checkbox1" id="checkbox1" />
|
<div>
|
||||||
<label for="checkbox1" />
|
{isAuthor(followingItem) ? (
|
||||||
</div>
|
<AuthorCard
|
||||||
<label for="checkbox1" class={stylesSettings.settingsListCell}>
|
author={followingItem}
|
||||||
Культура
|
hideWriteButton={true}
|
||||||
</label>
|
hasLink={true}
|
||||||
</div>
|
isTextButton={true}
|
||||||
<div class={stylesSettings.settingsListRow}>
|
truncateBio={true}
|
||||||
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
minimizeSubscribeButton={true}
|
||||||
<input type="checkbox" name="checkbox2" id="checkbox2" />
|
/>
|
||||||
<label for="checkbox2" />
|
) : (
|
||||||
</div>
|
<TopicCard
|
||||||
<label for="checkbox2" class={stylesSettings.settingsListCell}>
|
compact
|
||||||
Eto_ya sam
|
isTopicInRow
|
||||||
</label>
|
showDescription
|
||||||
</div>
|
isCardMode
|
||||||
<div class={stylesSettings.settingsListRow}>
|
topic={followingItem}
|
||||||
<div class={clsx(stylesSettings.settingsListCell, styles.topicsListItem)}>
|
minimizeSubscribeButton={true}
|
||||||
<input type="checkbox" name="checkbox3" id="checkbox3" />
|
/>
|
||||||
<label for="checkbox3" />
|
)}
|
||||||
</div>
|
</div>
|
||||||
<label for="checkbox3" class={stylesSettings.settingsListCell}>
|
)}
|
||||||
Технопарк
|
</For>
|
||||||
</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={stylesSettings.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={stylesSettings.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={stylesSettings.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={stylesSettings.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={stylesSettings.settingsListCell}>
|
|
||||||
Личный опыт
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</Show>
|
||||||
<br />
|
|
||||||
<p>
|
|
||||||
<button class="button button--submit">Сохранить настройки</button>
|
|
||||||
</p>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -46,3 +46,5 @@ export type UploadedFile = {
|
||||||
url: string
|
url: string
|
||||||
originalFilename?: string
|
originalFilename?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SubscriptionFilter = 'all' | 'users' | 'topics'
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
.settingsList {
|
.settingsList {
|
||||||
display: table;
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
@ -45,16 +46,3 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingsListRow {
|
|
||||||
display: table-row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settingsListCell {
|
|
||||||
display: table-cell;
|
|
||||||
padding: 0 0.5em 1em 0;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
padding-right: 2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
36
src/utils/dummyFilter.ts
Normal file
36
src/utils/dummyFilter.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { translit } from './ru2en'
|
||||||
|
import { Author, Topic } from '../graphql/types.gen'
|
||||||
|
|
||||||
|
type SearchData = Array<Author | Topic>
|
||||||
|
|
||||||
|
const prepareQuery = (searchQuery, lang) => {
|
||||||
|
const q = searchQuery.toLowerCase()
|
||||||
|
if (q.length === 0) return ''
|
||||||
|
return lang === 'ru' ? translit(q) : q
|
||||||
|
}
|
||||||
|
|
||||||
|
const stringMatches = (str, q, lang) => {
|
||||||
|
const preparedStr = lang === 'ru' ? translit(str.toLowerCase()) : str.toLowerCase()
|
||||||
|
return preparedStr.split(' ').some((word) => word.startsWith(q))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dummyFilter = (data: SearchData, searchQuery: string, lang: 'ru' | 'en'): SearchData => {
|
||||||
|
const q = prepareQuery(searchQuery, lang)
|
||||||
|
if (q.length === 0) return data
|
||||||
|
|
||||||
|
return data.filter((item) => {
|
||||||
|
const slugMatches = item.slug && item.slug.split('-').some((w) => w.startsWith(q))
|
||||||
|
if (slugMatches) return true
|
||||||
|
|
||||||
|
if ('title' in item) {
|
||||||
|
return stringMatches(item.title, q, lang)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('name' in item) {
|
||||||
|
return stringMatches(item.name, q, lang) || (item.bio && stringMatches(item.bio, q, lang))
|
||||||
|
}
|
||||||
|
// If it does not match any of the 'slug', 'title', 'name' , 'bio' fields
|
||||||
|
// current element should not be included in the filtered array
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
5
src/utils/isAuthor.ts
Normal file
5
src/utils/isAuthor.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { Author, Topic } from '../graphql/types.gen'
|
||||||
|
|
||||||
|
export const isAuthor = (value: Author | Topic): value is Author => {
|
||||||
|
return 'name' in value
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user