Feature/auth guard update (#230)
- add AuthGuard on myFeed - make AuthGuard closable with redirect to MainPage
This commit is contained in:
parent
bfc6599149
commit
8a71cb41ea
|
@ -60,7 +60,6 @@ export const FullArticle = (props: Props) => {
|
||||||
}, 'bookmark')
|
}, 'bookmark')
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('!!! props.article.media:', props.article.media)
|
|
||||||
const body = createMemo(() => {
|
const body = createMemo(() => {
|
||||||
if (props.article.layout === 'literature') {
|
if (props.article.layout === 'literature') {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -4,11 +4,16 @@ import { hideModal, showModal } from '../../stores/ui'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AuthGuard = (props: Props) => {
|
export const AuthGuard = (props: Props) => {
|
||||||
const { isAuthenticated, isSessionLoaded } = useSession()
|
const { isAuthenticated, isSessionLoaded } = useSession()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
if (props.disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
if (isSessionLoaded()) {
|
if (isSessionLoaded()) {
|
||||||
if (isAuthenticated()) {
|
if (isAuthenticated()) {
|
||||||
hideModal()
|
hideModal()
|
||||||
|
@ -18,5 +23,5 @@ export const AuthGuard = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return <Show when={isSessionLoaded() && isAuthenticated()}>{props.children}</Show>
|
return <Show when={(isSessionLoaded() && isAuthenticated()) || props.disabled}>{props.children}</Show>
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { hideModal, useModalStore } from '../../../stores/ui'
|
||||||
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
|
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
|
||||||
|
|
||||||
import styles from './Modal.module.scss'
|
import styles from './Modal.module.scss'
|
||||||
|
import { redirectPage } from '@nanostores/router'
|
||||||
|
import { router } from '../../../stores/router'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string
|
name: string
|
||||||
|
@ -22,9 +24,11 @@ export const Modal = (props: Props) => {
|
||||||
const allowClose = createMemo(() => props.allowClose !== false)
|
const allowClose = createMemo(() => props.allowClose !== false)
|
||||||
const handleHide = () => {
|
const handleHide = () => {
|
||||||
if (modal() && allowClose()) {
|
if (modal() && allowClose()) {
|
||||||
hideModal()
|
|
||||||
props.onClose && props.onClose()
|
props.onClose && props.onClose()
|
||||||
|
} else {
|
||||||
|
redirectPage(router, 'home')
|
||||||
}
|
}
|
||||||
|
hideModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
useEscKeyDownHandler(handleHide)
|
useEscKeyDownHandler(handleHide)
|
||||||
|
@ -45,22 +49,20 @@ export const Modal = (props: Props) => {
|
||||||
onClick={(event) => event.stopPropagation()}
|
onClick={(event) => event.stopPropagation()}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
<Show when={allowClose()}>
|
<div class={styles.close} onClick={handleHide}>
|
||||||
<div class={styles.close} onClick={handleHide}>
|
<svg
|
||||||
<svg
|
class={styles.icon}
|
||||||
class={styles.icon}
|
width="16"
|
||||||
width="16"
|
height="18"
|
||||||
height="18"
|
viewBox="0 0 16 18"
|
||||||
viewBox="0 0 16 18"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
d="M7.99987 7.52552L14.1871 0.92334L15.9548 2.80968L9.76764 9.41185L15.9548 16.014L14.1871 17.9004L7.99987 11.2982L1.81269 17.9004L0.0449219 16.014L6.23211 9.41185L0.0449225 2.80968L1.81269 0.92334L7.99987 7.52552Z"
|
||||||
d="M7.99987 7.52552L14.1871 0.92334L15.9548 2.80968L9.76764 9.41185L15.9548 16.014L14.1871 17.9004L7.99987 11.2982L1.81269 17.9004L0.0449219 16.014L6.23211 9.41185L0.0449225 2.80968L1.81269 0.92334L7.99987 7.52552Z"
|
fill="currentColor"
|
||||||
fill="currentColor"
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
</div>
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -18,6 +18,9 @@ import stylesTopic from '../Feed/CardTopic.module.scss'
|
||||||
import stylesBeside from '../../components/Feed/Beside.module.scss'
|
import stylesBeside from '../../components/Feed/Beside.module.scss'
|
||||||
import { CommentDate } from '../Article/CommentDate'
|
import { CommentDate } from '../Article/CommentDate'
|
||||||
import { Loading } from '../_shared/Loading'
|
import { Loading } from '../_shared/Loading'
|
||||||
|
import { ConditionalWrapper } from '../_shared/ConditionalWrapper'
|
||||||
|
import { AuthGuard } from '../AuthGuard'
|
||||||
|
import { useSession } from '../../context/session'
|
||||||
|
|
||||||
export const FEED_PAGE_SIZE = 20
|
export const FEED_PAGE_SIZE = 20
|
||||||
|
|
||||||
|
@ -37,9 +40,17 @@ const getOrderBy = (by: FeedSearchParams['by']) => {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const routesWithAuthGuard = new Set([
|
||||||
|
'feedMy',
|
||||||
|
'feedNotifications',
|
||||||
|
'feedBookmarks',
|
||||||
|
'feedCollaborations',
|
||||||
|
'feedDiscussions'
|
||||||
|
])
|
||||||
export const FeedView = () => {
|
export const FeedView = () => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { page, searchParams } = useRouter<FeedSearchParams>()
|
const { page, searchParams } = useRouter<FeedSearchParams>()
|
||||||
|
const { isAuthenticated } = useSession()
|
||||||
const [isLoading, setIsLoading] = createSignal(false)
|
const [isLoading, setIsLoading] = createSignal(false)
|
||||||
|
|
||||||
// state
|
// state
|
||||||
|
@ -69,7 +80,6 @@ export const FeedView = () => {
|
||||||
{ defer: true }
|
{ defer: true }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const loadFeedShouts = () => {
|
const loadFeedShouts = () => {
|
||||||
const options: LoadShoutsOptions = {
|
const options: LoadShoutsOptions = {
|
||||||
limit: FEED_PAGE_SIZE,
|
limit: FEED_PAGE_SIZE,
|
||||||
|
@ -82,7 +92,7 @@ export const FeedView = () => {
|
||||||
options.order_by = orderBy
|
options.order_by = orderBy
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page().route === 'feedMy') {
|
if (routesWithAuthGuard.has(page().route) && isAuthenticated()) {
|
||||||
return loadMyFeed(options)
|
return loadMyFeed(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,155 +126,157 @@ export const FeedView = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div class="wide-container feed">
|
<AuthGuard disabled={!routesWithAuthGuard.has(page().route)}>
|
||||||
<div class="row">
|
<div class="wide-container feed">
|
||||||
<div class={clsx('col-md-5 col-xl-4', styles.feedNavigation)}>
|
<div class="row">
|
||||||
<Sidebar authors={sortedAuthors()} />
|
<div class={clsx('col-md-5 col-xl-4', styles.feedNavigation)}>
|
||||||
</div>
|
<Sidebar authors={sortedAuthors()} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="col-md-12 offset-xl-1">
|
<div class="col-md-12 offset-xl-1">
|
||||||
<ul class={clsx(styles.feedFilter, 'view-switcher')}>
|
<ul class={clsx(styles.feedFilter, 'view-switcher')}>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
'view-switcher__item--selected':
|
'view-switcher__item--selected':
|
||||||
searchParams().by === 'publish_date' || !searchParams().by
|
searchParams().by === 'publish_date' || !searchParams().by
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<a href={getPagePath(router, page().route)}>{t('Recent')}</a>
|
<a href={getPagePath(router, page().route)}>{t('Recent')}</a>
|
||||||
</li>
|
|
||||||
{/*<li>*/}
|
|
||||||
{/* <a href="/feed/?by=views">{t('Most read')}</a>*/}
|
|
||||||
{/*</li>*/}
|
|
||||||
<li
|
|
||||||
class={clsx({
|
|
||||||
'view-switcher__item--selected': searchParams().by === 'rating'
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<a href={`${getPagePath(router, page().route)}?by=rating`}>{t('Top rated')}</a>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
class={clsx({
|
|
||||||
'view-switcher__item--selected': searchParams().by === 'last_comment'
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<a href={`${getPagePath(router, page().route)}?by=last_comment`}>{t('Most commented')}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<Show when={!isLoading()} fallback={<Loading />}>
|
|
||||||
<Show when={sortedArticles().length > 0}>
|
|
||||||
<For each={sortedArticles().slice(0, 4)}>
|
|
||||||
{(article) => <ArticleCard article={article} settings={{ isFeedMode: true }} />}
|
|
||||||
</For>
|
|
||||||
|
|
||||||
<div class={styles.asideSection}>
|
|
||||||
<div class={stylesBeside.besideColumnTitle}>
|
|
||||||
<h4>{t('Popular authors')}</h4>
|
|
||||||
<a href="/authors">
|
|
||||||
{t('All authors')}
|
|
||||||
<Icon name="arrow-right" class={stylesBeside.icon} />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul class={stylesBeside.besideColumn}>
|
|
||||||
<For each={topAuthors().slice(0, 5)}>
|
|
||||||
{(author) => (
|
|
||||||
<li>
|
|
||||||
<AuthorCard
|
|
||||||
author={author}
|
|
||||||
hideWriteButton={true}
|
|
||||||
hasLink={true}
|
|
||||||
truncateBio={true}
|
|
||||||
isTextButton={true}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<For each={sortedArticles().slice(4)}>
|
|
||||||
{(article) => <ArticleCard article={article} settings={{ isFeedMode: true }} />}
|
|
||||||
</For>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<Show when={isLoadMoreButtonVisible()}>
|
|
||||||
<p class="load-more-container">
|
|
||||||
<button class="button" onClick={loadMore}>
|
|
||||||
{t('Load more')}
|
|
||||||
</button>
|
|
||||||
</p>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<aside class={clsx('col-md-7 col-xl-6 offset-xl-1', styles.feedAside)}>
|
|
||||||
<section class={styles.asideSection}>
|
|
||||||
<h4>{t('Comments')}</h4>
|
|
||||||
<For each={topComments()}>
|
|
||||||
{(comment) => {
|
|
||||||
return (
|
|
||||||
<div class={styles.comment}>
|
|
||||||
<div class={clsx('text-truncate', styles.commentBody)}>
|
|
||||||
<a
|
|
||||||
href={`${getPagePath(router, 'article', {
|
|
||||||
slug: comment.shout.slug
|
|
||||||
})}?commentId=${comment.id}`}
|
|
||||||
innerHTML={comment.body}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class={styles.commentDetails}>
|
|
||||||
<AuthorCard
|
|
||||||
author={comment.createdBy as Author}
|
|
||||||
isFeedMode={true}
|
|
||||||
hideWriteButton={true}
|
|
||||||
hideFollow={true}
|
|
||||||
hasLink={true}
|
|
||||||
/>
|
|
||||||
<CommentDate comment={comment} isShort={true} isLastInRow={true} />
|
|
||||||
</div>
|
|
||||||
<div class={clsx('text-truncate', styles.commentArticleTitle)}>
|
|
||||||
<a href={`/${comment.shout.slug}`}>{comment.shout.title}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<Show when={topTopics().length > 0}>
|
|
||||||
<section class={styles.asideSection}>
|
|
||||||
<h4>{t('Hot topics')}</h4>
|
|
||||||
<For each={topTopics().slice(0, 7)}>
|
|
||||||
{(topic) => (
|
|
||||||
<span class={clsx(stylesTopic.shoutTopic, styles.topic)}>
|
|
||||||
<a href={`/topic/${topic.slug}`}>{topic.title}</a>{' '}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</section>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<section class={clsx(styles.asideSection, styles.pinnedLinks)}>
|
|
||||||
<h4>{t('Knowledge base')}</h4>
|
|
||||||
<ul class="nodash">
|
|
||||||
<li>
|
|
||||||
<a href={getPagePath(router, 'guide')}>Как устроен Дискурс</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
{/*<li>*/}
|
||||||
<a href="/how-to-write-a-good-article">Как создать хороший текст</a>
|
{/* <a href="/feed/?by=views">{t('Most read')}</a>*/}
|
||||||
|
{/*</li>*/}
|
||||||
|
<li
|
||||||
|
class={clsx({
|
||||||
|
'view-switcher__item--selected': searchParams().by === 'rating'
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<a href={`${getPagePath(router, page().route)}?by=rating`}>{t('Top rated')}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li
|
||||||
<a href="#">Правила конструктивных дискуссий</a>
|
class={clsx({
|
||||||
</li>
|
'view-switcher__item--selected': searchParams().by === 'last_comment'
|
||||||
<li>
|
})}
|
||||||
<a href={getPagePath(router, 'principles')}>Принципы сообщества</a>
|
>
|
||||||
|
<a href={`${getPagePath(router, page().route)}?by=last_comment`}>{t('Most commented')}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
|
||||||
</aside>
|
<Show when={!isLoading()} fallback={<Loading />}>
|
||||||
|
<Show when={sortedArticles().length > 0}>
|
||||||
|
<For each={sortedArticles().slice(0, 4)}>
|
||||||
|
{(article) => <ArticleCard article={article} settings={{ isFeedMode: true }} />}
|
||||||
|
</For>
|
||||||
|
|
||||||
|
<div class={styles.asideSection}>
|
||||||
|
<div class={stylesBeside.besideColumnTitle}>
|
||||||
|
<h4>{t('Popular authors')}</h4>
|
||||||
|
<a href="/authors">
|
||||||
|
{t('All authors')}
|
||||||
|
<Icon name="arrow-right" class={stylesBeside.icon} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class={stylesBeside.besideColumn}>
|
||||||
|
<For each={topAuthors().slice(0, 5)}>
|
||||||
|
{(author) => (
|
||||||
|
<li>
|
||||||
|
<AuthorCard
|
||||||
|
author={author}
|
||||||
|
hideWriteButton={true}
|
||||||
|
hasLink={true}
|
||||||
|
truncateBio={true}
|
||||||
|
isTextButton={true}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<For each={sortedArticles().slice(4)}>
|
||||||
|
{(article) => <ArticleCard article={article} settings={{ isFeedMode: true }} />}
|
||||||
|
</For>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={isLoadMoreButtonVisible()}>
|
||||||
|
<p class="load-more-container">
|
||||||
|
<button class="button" onClick={loadMore}>
|
||||||
|
{t('Load more')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</Show>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class={clsx('col-md-7 col-xl-6 offset-xl-1', styles.feedAside)}>
|
||||||
|
<section class={styles.asideSection}>
|
||||||
|
<h4>{t('Comments')}</h4>
|
||||||
|
<For each={topComments()}>
|
||||||
|
{(comment) => {
|
||||||
|
return (
|
||||||
|
<div class={styles.comment}>
|
||||||
|
<div class={clsx('text-truncate', styles.commentBody)}>
|
||||||
|
<a
|
||||||
|
href={`${getPagePath(router, 'article', {
|
||||||
|
slug: comment.shout.slug
|
||||||
|
})}?commentId=${comment.id}`}
|
||||||
|
innerHTML={comment.body}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class={styles.commentDetails}>
|
||||||
|
<AuthorCard
|
||||||
|
author={comment.createdBy as Author}
|
||||||
|
isFeedMode={true}
|
||||||
|
hideWriteButton={true}
|
||||||
|
hideFollow={true}
|
||||||
|
hasLink={true}
|
||||||
|
/>
|
||||||
|
<CommentDate comment={comment} isShort={true} isLastInRow={true} />
|
||||||
|
</div>
|
||||||
|
<div class={clsx('text-truncate', styles.commentArticleTitle)}>
|
||||||
|
<a href={`/${comment.shout.slug}`}>{comment.shout.title}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Show when={topTopics().length > 0}>
|
||||||
|
<section class={styles.asideSection}>
|
||||||
|
<h4>{t('Hot topics')}</h4>
|
||||||
|
<For each={topTopics().slice(0, 7)}>
|
||||||
|
{(topic) => (
|
||||||
|
<span class={clsx(stylesTopic.shoutTopic, styles.topic)}>
|
||||||
|
<a href={`/topic/${topic.slug}`}>{topic.title}</a>{' '}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</section>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<section class={clsx(styles.asideSection, styles.pinnedLinks)}>
|
||||||
|
<h4>{t('Knowledge base')}</h4>
|
||||||
|
<ul class="nodash">
|
||||||
|
<li>
|
||||||
|
<a href={getPagePath(router, 'guide')}>Как устроен Дискурс</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/how-to-write-a-good-article">Как создать хороший текст</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#">Правила конструктивных дискуссий</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href={getPagePath(router, 'principles')}>Принципы сообщества</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</AuthGuard>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,6 @@ export const GrowingTextarea = (props: Props) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('!!! initialValue:', props.initialValue)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class={clsx(styles.GrowingTextarea, {
|
class={clsx(styles.GrowingTextarea, {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user