Header fixes
This commit is contained in:
commit
78e351e71b
|
@ -33,6 +33,7 @@
|
||||||
"@nanostores/persistent": "^0.7.0",
|
"@nanostores/persistent": "^0.7.0",
|
||||||
"@nanostores/router": "^0.7.0",
|
"@nanostores/router": "^0.7.0",
|
||||||
"@nanostores/solid": "^0.3.0",
|
"@nanostores/solid": "^0.3.0",
|
||||||
|
"@solid-primitives/memo": "^1.0.2",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"google-translate-api-x": "^10.4.1",
|
"google-translate-api-x": "^10.4.1",
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.0",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { Icon } from '../Nav/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
interface BesideProps {
|
interface BesideProps {
|
||||||
title: string
|
title?: string
|
||||||
values: any[]
|
values: any[]
|
||||||
beside: Shout
|
beside: Shout
|
||||||
wrapper: 'topic' | 'author' | 'article' | 'top-article'
|
wrapper: 'topic' | 'author' | 'article' | 'top-article'
|
||||||
|
|
|
@ -3,21 +3,27 @@ import { Header } from '../Nav/Header'
|
||||||
import { Footer } from '../Discours/Footer'
|
import { Footer } from '../Discours/Footer'
|
||||||
|
|
||||||
import '../../styles/app.scss'
|
import '../../styles/app.scss'
|
||||||
|
import { Show } from 'solid-js'
|
||||||
|
|
||||||
type Props = {
|
type MainLayoutProps = {
|
||||||
headerTitle?: string
|
headerTitle?: string
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
isHeaderFixed?: boolean
|
isHeaderFixed?: boolean
|
||||||
|
hideFooter?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MainLayout = (props: Props) => {
|
export const MainLayout = (props: MainLayoutProps) => {
|
||||||
const isHeaderFixed = props.isHeaderFixed !== undefined ? props.isHeaderFixed : true
|
const isHeaderFixed = props.isHeaderFixed !== undefined ? props.isHeaderFixed : true
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header title={props.headerTitle} isHeaderFixed={isHeaderFixed} />
|
<Header title={props.headerTitle} isHeaderFixed={isHeaderFixed} />
|
||||||
<main class="main-content">{props.children}</main>
|
<main class="main-content" classList={{ 'main-content--no-padding': !isHeaderFixed }}>
|
||||||
<Footer />
|
{props.children}
|
||||||
|
</main>
|
||||||
|
<Show when={props.hideFooter !== true}>
|
||||||
|
<Footer />
|
||||||
|
</Show>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
.mainHeader {
|
.mainHeader {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
margin-bottom: 2.2rem;
|
margin-bottom: 2.2rem;
|
||||||
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
|
@ -11,6 +12,12 @@
|
||||||
padding: 0 divide($container-padding-x, 2);
|
padding: 0 divide($container-padding-x, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
.icon {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerFixed {
|
.headerFixed {
|
||||||
|
@ -63,6 +70,19 @@
|
||||||
height: 80px;
|
height: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 32px;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: left;
|
||||||
|
transition: height 0.2s;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 100px;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
width: 175px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
border: none;
|
border: none;
|
||||||
color: #000;
|
color: #000;
|
||||||
|
@ -73,19 +93,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.searchIcon {
|
|
||||||
height: 32px;
|
|
||||||
object-fit: contain;
|
|
||||||
object-position: left;
|
|
||||||
transition: height 0.2s;
|
|
||||||
vertical-align: middle;
|
|
||||||
width: 100px;
|
|
||||||
|
|
||||||
@include media-breakpoint-up(lg) {
|
|
||||||
width: 175px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.usernav {
|
.usernav {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
@ -268,6 +275,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
margin-right: 0.3em;
|
margin-right: 0.3em;
|
||||||
|
transition: filter 0.3s;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ export const Header = (props: Props) => {
|
||||||
</For>
|
</For>
|
||||||
<li class={styles.headerSearch}>
|
<li class={styles.headerSearch}>
|
||||||
<a href="#">
|
<a href="#">
|
||||||
<Icon name="search" iconClassName={styles.searchIcon} />
|
<Icon name="search" class={styles.icon} iconClassName={styles.searchIcon} />
|
||||||
{t('Search')}
|
{t('Search')}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -6,7 +6,8 @@ import { t } from '../../utils/intl'
|
||||||
import { locale } from '../../stores/ui'
|
import { locale } from '../../stores/ui'
|
||||||
|
|
||||||
export const NavTopics = (props: { topics: Topic[] }) => {
|
export const NavTopics = (props: { topics: Topic[] }) => {
|
||||||
const tag = (t: Topic) => (/[ЁА-яё]/.test(t.title || '') && locale() !== 'ru' ? t.slug : t.title)
|
const tag = (topic: Topic) =>
|
||||||
|
/[ЁА-яё]/.test(topic.title || '') && locale() !== 'ru' ? topic.slug : topic.title
|
||||||
|
|
||||||
// TODO: something about subtopics
|
// TODO: something about subtopics
|
||||||
return (
|
return (
|
||||||
|
@ -14,10 +15,10 @@ export const NavTopics = (props: { topics: Topic[] }) => {
|
||||||
<ul class="topics">
|
<ul class="topics">
|
||||||
<Show when={props.topics.length > 0}>
|
<Show when={props.topics.length > 0}>
|
||||||
<For each={props.topics}>
|
<For each={props.topics}>
|
||||||
{(t: Topic) => (
|
{(topic) => (
|
||||||
<li class="item">
|
<li class="item">
|
||||||
<a href={`/topic/${t.slug}`}>
|
<a href={`/topic/${topic.slug}`}>
|
||||||
<span>#{tag(t)}</span>
|
<span>#{tag(topic)}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { MainLayout } from '../Layouts/MainLayout'
|
||||||
|
|
||||||
export const FourOuFourPage = () => {
|
export const FourOuFourPage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout isHeaderFixed={false}>
|
<MainLayout isHeaderFixed={false} hideFooter={true}>
|
||||||
<FourOuFourView />
|
<FourOuFourView />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// import 'solid-devtools'
|
// import 'solid-devtools'
|
||||||
|
|
||||||
import { setLocale } from '../stores/ui'
|
import { setLocale } from '../stores/ui'
|
||||||
import { Component, createEffect, createMemo, lazy } from 'solid-js'
|
import { Component, createEffect, createMemo } from 'solid-js'
|
||||||
import { Routes, useRouter } from '../stores/router'
|
import { Routes, useRouter } from '../stores/router'
|
||||||
import { Dynamic, isServer } from 'solid-js/web'
|
import { Dynamic, isServer } from 'solid-js/web'
|
||||||
import { getLogger } from '../utils/logger'
|
import { getLogger } from '../utils/logger'
|
||||||
|
|
|
@ -8,22 +8,26 @@ import { TopicCard } from '../Topic/Card'
|
||||||
import { session } from '../../stores/auth'
|
import { session } from '../../stores/auth'
|
||||||
import { useStore } from '@nanostores/solid'
|
import { useStore } from '@nanostores/solid'
|
||||||
import '../../styles/AllTopics.scss'
|
import '../../styles/AllTopics.scss'
|
||||||
|
import { getLogger } from '../../utils/logger'
|
||||||
|
|
||||||
|
const log = getLogger('AllTopicsView')
|
||||||
|
|
||||||
type AllTopicsPageSearchParams = {
|
type AllTopicsPageSearchParams = {
|
||||||
by: 'shouts' | 'authors' | 'title' | ''
|
by: 'shouts' | 'authors' | 'title' | ''
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
type AllTopicsViewProps = {
|
||||||
topics: Topic[]
|
topics: Topic[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AllTopicsView = (props: Props) => {
|
export const AllTopicsView = (props: AllTopicsViewProps) => {
|
||||||
const { getSearchParams, changeSearchParam } = useRouter<AllTopicsPageSearchParams>()
|
const { getSearchParams, changeSearchParam } = useRouter<AllTopicsPageSearchParams>()
|
||||||
|
|
||||||
const { sortedTopics } = useTopicsStore({
|
const { sortedTopics } = useTopicsStore({
|
||||||
topics: props.topics,
|
topics: props.topics,
|
||||||
sortBy: getSearchParams().by || 'shouts'
|
sortBy: getSearchParams().by || 'shouts'
|
||||||
})
|
})
|
||||||
|
|
||||||
const auth = useStore(session)
|
const auth = useStore(session)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../Nav/Icon'
|
||||||
import styles from '../../styles/FourOuFour.module.scss'
|
import styles from '../../styles/FourOuFour.module.scss'
|
||||||
import clsx from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
export const FourOuFourView = (_props) => {
|
export const FourOuFourView = (_props) => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
} from '../../stores/zine/articles'
|
} from '../../stores/zine/articles'
|
||||||
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
||||||
import { locale } from '../../stores/ui'
|
import { locale } from '../../stores/ui'
|
||||||
|
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
||||||
|
|
||||||
const log = getLogger('home view')
|
const log = getLogger('home view')
|
||||||
|
|
||||||
|
@ -30,9 +31,9 @@ type HomeProps = {
|
||||||
randomTopics: Topic[]
|
randomTopics: Topic[]
|
||||||
recentPublishedArticles: Shout[]
|
recentPublishedArticles: Shout[]
|
||||||
}
|
}
|
||||||
|
const PRERENDERED_ARTICLES_COUNT = 5
|
||||||
const CLIENT_LOAD_ARTICLES_COUNT = 30
|
const CLIENT_LOAD_ARTICLES_COUNT = 29
|
||||||
const LOAD_MORE_ARTICLES_COUNT = 30
|
const LOAD_MORE_PAGE_SIZE = 16 // Row1 + Row3 + Row2 + Beside (3 + 1) + Row1 + Row 2 + Row3
|
||||||
|
|
||||||
export const HomeView = (props: HomeProps) => {
|
export const HomeView = (props: HomeProps) => {
|
||||||
const {
|
const {
|
||||||
|
@ -54,7 +55,9 @@ export const HomeView = (props: HomeProps) => {
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
loadTopArticles()
|
loadTopArticles()
|
||||||
loadTopMonthArticles()
|
loadTopMonthArticles()
|
||||||
loadPublishedArticles({ limit: CLIENT_LOAD_ARTICLES_COUNT, offset: sortedArticles().length })
|
if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) {
|
||||||
|
loadPublishedArticles({ limit: CLIENT_LOAD_ARTICLES_COUNT, offset: sortedArticles().length })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const randomLayout = createMemo(() => {
|
const randomLayout = createMemo(() => {
|
||||||
|
@ -80,10 +83,25 @@ export const HomeView = (props: HomeProps) => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = async () => {
|
||||||
loadPublishedArticles({ limit: LOAD_MORE_ARTICLES_COUNT, offset: sortedArticles().length })
|
saveScrollPosition()
|
||||||
|
await loadPublishedArticles({ limit: LOAD_MORE_PAGE_SIZE, offset: sortedArticles().length })
|
||||||
|
restoreScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pages = createMemo<Shout[][]>(() => {
|
||||||
|
return sortedArticles()
|
||||||
|
.slice(PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT)
|
||||||
|
.reduce((acc, article, index) => {
|
||||||
|
if (index % LOAD_MORE_PAGE_SIZE === 0) {
|
||||||
|
acc.push([])
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[acc.length - 1].push(article)
|
||||||
|
return acc
|
||||||
|
}, [] as Shout[][])
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={locale()}>
|
<Show when={locale()}>
|
||||||
<NavTopics topics={randomTopics()} />
|
<NavTopics topics={randomTopics()} />
|
||||||
|
@ -102,15 +120,12 @@ export const HomeView = (props: HomeProps) => {
|
||||||
|
|
||||||
<Row3 articles={sortedArticles().slice(6, 9)} />
|
<Row3 articles={sortedArticles().slice(6, 9)} />
|
||||||
|
|
||||||
{/*FIXME: ?*/}
|
<Beside
|
||||||
<Show when={topAuthors().length === 5}>
|
beside={sortedArticles()[9]}
|
||||||
<Beside
|
title={t('Top authors')}
|
||||||
beside={sortedArticles().slice(8, 9)[0]}
|
values={topAuthors()}
|
||||||
title={t('Top authors')}
|
wrapper={'author'}
|
||||||
values={topAuthors()}
|
/>
|
||||||
wrapper={'author'}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<Slider title={t('Top month articles')} articles={topMonthArticles()} />
|
<Slider title={t('Top month articles')} articles={topMonthArticles()} />
|
||||||
|
|
||||||
|
@ -118,9 +133,9 @@ export const HomeView = (props: HomeProps) => {
|
||||||
|
|
||||||
<RowShort articles={sortedArticles().slice(12, 16)} />
|
<RowShort articles={sortedArticles().slice(12, 16)} />
|
||||||
|
|
||||||
<Row1 article={sortedArticles().slice(15, 16)[0]} />
|
<Row1 article={sortedArticles()[16]} />
|
||||||
<Row3 articles={sortedArticles().slice(17, 20)} />
|
<Row3 articles={sortedArticles().slice(17, 20)} />
|
||||||
<Row3 articles={topCommentedArticles()} header={<h2>{t('Top commented')}</h2>} />
|
<Row3 articles={topCommentedArticles().slice(0, 3)} header={<h2>{t('Top commented')}</h2>} />
|
||||||
|
|
||||||
{randomLayout()}
|
{randomLayout()}
|
||||||
|
|
||||||
|
@ -144,7 +159,19 @@ export const HomeView = (props: HomeProps) => {
|
||||||
<Row3 articles={sortedArticles().slice(31, 34)} />
|
<Row3 articles={sortedArticles().slice(31, 34)} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<For each={sortedArticles().slice(35)}>{(article) => <Row1 article={article} />}</For>
|
<For each={pages()}>
|
||||||
|
{(page) => (
|
||||||
|
<>
|
||||||
|
<Row1 article={page[0]} />
|
||||||
|
<Row3 articles={page.slice(1, 4)} />
|
||||||
|
<Row2 articles={page.slice(4, 6)} />
|
||||||
|
<Beside values={page.slice(6, 9)} beside={page[9]} wrapper="article" />
|
||||||
|
<Row1 article={page[10]} />
|
||||||
|
<Row2 articles={page.slice(11, 13)} />
|
||||||
|
<Row3 articles={page.slice(13, 16)} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
|
||||||
<p class="load-more-container">
|
<p class="load-more-container">
|
||||||
<button class="button" onClick={loadMore}>
|
<button class="button" onClick={loadMore}>
|
||||||
|
|
|
@ -324,11 +324,11 @@ export type Query = {
|
||||||
getUserRoles: Array<Maybe<Role>>
|
getUserRoles: Array<Maybe<Role>>
|
||||||
getUsersBySlugs: Array<Maybe<User>>
|
getUsersBySlugs: Array<Maybe<User>>
|
||||||
isEmailUsed: Scalars['Boolean']
|
isEmailUsed: Scalars['Boolean']
|
||||||
myCandidates: Array<Maybe<Shout>>
|
|
||||||
reactionsByAuthor: Array<Maybe<Reaction>>
|
reactionsByAuthor: Array<Maybe<Reaction>>
|
||||||
reactionsByShout: Array<Maybe<Reaction>>
|
reactionsByShout: Array<Maybe<Reaction>>
|
||||||
reactionsForShouts: Array<Maybe<Reaction>>
|
reactionsForShouts: Array<Maybe<Reaction>>
|
||||||
recentAll: Array<Maybe<Shout>>
|
recentAll: Array<Maybe<Shout>>
|
||||||
|
recentCandidates: Array<Maybe<Shout>>
|
||||||
recentCommented: Array<Maybe<Shout>>
|
recentCommented: Array<Maybe<Shout>>
|
||||||
recentPublished: Array<Maybe<Shout>>
|
recentPublished: Array<Maybe<Shout>>
|
||||||
recentReacted: Array<Maybe<Shout>>
|
recentReacted: Array<Maybe<Shout>>
|
||||||
|
@ -397,11 +397,6 @@ export type QueryIsEmailUsedArgs = {
|
||||||
email: Scalars['String']
|
email: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryMyCandidatesArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryReactionsByAuthorArgs = {
|
export type QueryReactionsByAuthorArgs = {
|
||||||
limit: Scalars['Int']
|
limit: Scalars['Int']
|
||||||
offset: Scalars['Int']
|
offset: Scalars['Int']
|
||||||
|
@ -425,6 +420,11 @@ export type QueryRecentAllArgs = {
|
||||||
offset: Scalars['Int']
|
offset: Scalars['Int']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type QueryRecentCandidatesArgs = {
|
||||||
|
limit: Scalars['Int']
|
||||||
|
offset: Scalars['Int']
|
||||||
|
}
|
||||||
|
|
||||||
export type QueryRecentCommentedArgs = {
|
export type QueryRecentCommentedArgs = {
|
||||||
limit: Scalars['Int']
|
limit: Scalars['Int']
|
||||||
offset: Scalars['Int']
|
offset: Scalars['Int']
|
||||||
|
|
|
@ -69,6 +69,10 @@ export const handleClientRouteLinkClick = (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
// TODO: search params
|
// TODO: search params
|
||||||
routerStore.open(url.pathname)
|
routerStore.open(url.pathname)
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { byStat } from '../../utils/sortby'
|
||||||
|
|
||||||
import { getLogger } from '../../utils/logger'
|
import { getLogger } from '../../utils/logger'
|
||||||
import { createMemo, createSignal } from 'solid-js'
|
import { createMemo, createSignal } from 'solid-js'
|
||||||
|
import { createLazyMemo } from '@solid-primitives/memo'
|
||||||
|
|
||||||
const log = getLogger('articles store')
|
const log = getLogger('articles store')
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ const [articleEntities, setArticleEntities] = createSignal<{ [articleSlug: strin
|
||||||
const [topArticles, setTopArticles] = createSignal<Shout[]>([])
|
const [topArticles, setTopArticles] = createSignal<Shout[]>([])
|
||||||
const [topMonthArticles, setTopMonthArticles] = createSignal<Shout[]>([])
|
const [topMonthArticles, setTopMonthArticles] = createSignal<Shout[]>([])
|
||||||
|
|
||||||
const articlesByAuthor = createMemo(() => {
|
const articlesByAuthor = createLazyMemo(() => {
|
||||||
return Object.values(articleEntities()).reduce((acc, article) => {
|
return Object.values(articleEntities()).reduce((acc, article) => {
|
||||||
article.authors.forEach((author) => {
|
article.authors.forEach((author) => {
|
||||||
if (!acc[author.slug]) {
|
if (!acc[author.slug]) {
|
||||||
|
@ -28,7 +29,7 @@ const articlesByAuthor = createMemo(() => {
|
||||||
}, {} as { [authorSlug: string]: Shout[] })
|
}, {} as { [authorSlug: string]: Shout[] })
|
||||||
})
|
})
|
||||||
|
|
||||||
const articlesByTopic = createMemo(() => {
|
const articlesByTopic = createLazyMemo(() => {
|
||||||
return Object.values(articleEntities()).reduce((acc, article) => {
|
return Object.values(articleEntities()).reduce((acc, article) => {
|
||||||
article.topics.forEach((topic) => {
|
article.topics.forEach((topic) => {
|
||||||
if (!acc[topic.slug]) {
|
if (!acc[topic.slug]) {
|
||||||
|
@ -41,7 +42,7 @@ const articlesByTopic = createMemo(() => {
|
||||||
}, {} as { [authorSlug: string]: Shout[] })
|
}, {} as { [authorSlug: string]: Shout[] })
|
||||||
})
|
})
|
||||||
|
|
||||||
const articlesByLayout = createMemo(() => {
|
const articlesByLayout = createLazyMemo(() => {
|
||||||
return Object.values(articleEntities()).reduce((acc, article) => {
|
return Object.values(articleEntities()).reduce((acc, article) => {
|
||||||
if (!acc[article.layout]) {
|
if (!acc[article.layout]) {
|
||||||
acc[article.layout] = []
|
acc[article.layout] = []
|
||||||
|
@ -53,13 +54,13 @@ const articlesByLayout = createMemo(() => {
|
||||||
}, {} as { [layout: string]: Shout[] })
|
}, {} as { [layout: string]: Shout[] })
|
||||||
})
|
})
|
||||||
|
|
||||||
const topViewedArticles = createMemo(() => {
|
const topViewedArticles = createLazyMemo(() => {
|
||||||
const result = Object.values(articleEntities())
|
const result = Object.values(articleEntities())
|
||||||
result.sort(byStat('viewed'))
|
result.sort(byStat('viewed'))
|
||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
const topCommentedArticles = createMemo(() => {
|
const topCommentedArticles = createLazyMemo(() => {
|
||||||
const result = Object.values(articleEntities())
|
const result = Object.values(articleEntities())
|
||||||
result.sort(byStat('commented'))
|
result.sort(byStat('commented'))
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -3,8 +3,8 @@ import type { Author } from '../../graphql/types.gen'
|
||||||
import { byCreated } from '../../utils/sortby'
|
import { byCreated } from '../../utils/sortby'
|
||||||
|
|
||||||
import { getLogger } from '../../utils/logger'
|
import { getLogger } from '../../utils/logger'
|
||||||
import { Accessor, createMemo, createSignal } from 'solid-js'
|
import { createSignal } from 'solid-js'
|
||||||
import type { Signal } from 'solid-js'
|
import { createLazyMemo } from '@solid-primitives/memo'
|
||||||
|
|
||||||
const log = getLogger('authors store')
|
const log = getLogger('authors store')
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ const [sortAllBy, setSortAllBy] = createSignal<AuthorsSortBy>('created')
|
||||||
const [authorEntities, setAuthorEntities] = createSignal<{ [authorSlug: string]: Author }>({})
|
const [authorEntities, setAuthorEntities] = createSignal<{ [authorSlug: string]: Author }>({})
|
||||||
const [authorsByTopic, setAuthorsByTopic] = createSignal<{ [topicSlug: string]: Author[] }>({})
|
const [authorsByTopic, setAuthorsByTopic] = createSignal<{ [topicSlug: string]: Author[] }>({})
|
||||||
|
|
||||||
const sortedAuthors = createMemo(() => {
|
const sortedAuthors = createLazyMemo(() => {
|
||||||
const authors = Object.values(authorEntities())
|
const authors = Object.values(authorEntities())
|
||||||
switch (sortAllBy()) {
|
switch (sortAllBy()) {
|
||||||
case 'created': {
|
case 'created': {
|
||||||
|
|
|
@ -39,8 +39,8 @@ export const loadReactions = async ({
|
||||||
limit: number
|
limit: number
|
||||||
offset: number
|
offset: number
|
||||||
}): Promise<void> => {
|
}): Promise<void> => {
|
||||||
const reactions = await apiClient.getReactionsForShouts({ shoutSlugs, limit, offset })
|
const reactionsForShouts = await apiClient.getReactionsForShouts({ shoutSlugs, limit, offset })
|
||||||
reactionsOrdered.set(reactions)
|
reactionsOrdered.set(reactionsForShouts)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createReaction = async (reaction: Reaction) =>
|
export const createReaction = async (reaction: Reaction) =>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { apiClient } from '../../utils/apiClient'
|
||||||
import type { Topic } from '../../graphql/types.gen'
|
import type { Topic } from '../../graphql/types.gen'
|
||||||
import { byCreated, byTopicStatDesc } from '../../utils/sortby'
|
import { byCreated, byTopicStatDesc } from '../../utils/sortby'
|
||||||
import { getLogger } from '../../utils/logger'
|
import { getLogger } from '../../utils/logger'
|
||||||
|
import { createLazyMemo } from '@solid-primitives/memo'
|
||||||
|
|
||||||
const log = getLogger('topics store')
|
const log = getLogger('topics store')
|
||||||
|
|
||||||
|
@ -16,8 +17,8 @@ const [topicEntities, setTopicEntities] = createSignal<{ [topicSlug: string]: To
|
||||||
const [randomTopics, setRandomTopics] = createSignal<Topic[]>([])
|
const [randomTopics, setRandomTopics] = createSignal<Topic[]>([])
|
||||||
const [topicsByAuthor, setTopicByAuthor] = createSignal<{ [authorSlug: string]: Topic[] }>({})
|
const [topicsByAuthor, setTopicByAuthor] = createSignal<{ [authorSlug: string]: Topic[] }>({})
|
||||||
|
|
||||||
const sortedTopics = createMemo(() => {
|
const sortedTopics = createLazyMemo<Topic[]>(() => {
|
||||||
const topics = Object.values(topicEntities)
|
const topics = Object.values(topicEntities())
|
||||||
const sortAllByValue = sortAllBy()
|
const sortAllByValue = sortAllBy()
|
||||||
|
|
||||||
switch (sortAllByValue) {
|
switch (sortAllByValue) {
|
||||||
|
@ -38,6 +39,7 @@ const sortedTopics = createMemo(() => {
|
||||||
default:
|
default:
|
||||||
log.error(`Unknown sort: ${sortAllByValue}`)
|
log.error(`Unknown sort: ${sortAllByValue}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return topics
|
return topics
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
.main-header {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-logo {
|
.main-logo {
|
||||||
height: 80px !important;
|
height: 80px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
.main-content {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
padding-top: 0 !important;
|
padding-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +15,7 @@ main {
|
||||||
.errorPage {
|
.errorPage {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 35%;
|
top: 35%;
|
||||||
transform: translateY(-43%);
|
transform: translateY(-45%);
|
||||||
|
|
||||||
.image-link:hover {
|
.image-link:hover {
|
||||||
background: none;
|
background: none;
|
||||||
|
|
|
@ -26,7 +26,6 @@ html {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
overscroll-behavior-y: none;
|
overscroll-behavior-y: none;
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
@ -537,6 +536,10 @@ figcaption {
|
||||||
transition: all 1s ease;
|
transition: all 1s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.main-content--no-padding {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
|
|
||||||
|
|
16
src/utils/scroll.ts
Normal file
16
src/utils/scroll.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const scrollPosition = {
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveScrollPosition = () => {
|
||||||
|
scrollPosition.top = window.scrollY
|
||||||
|
scrollPosition.left = window.scrollX
|
||||||
|
}
|
||||||
|
|
||||||
|
export const restoreScrollPosition = () => {
|
||||||
|
window.scroll({
|
||||||
|
top: scrollPosition.top,
|
||||||
|
left: scrollPosition.left
|
||||||
|
})
|
||||||
|
}
|
13
yarn.lock
13
yarn.lock
|
@ -2707,6 +2707,14 @@
|
||||||
"@solid-primitives/rootless" "^1.1.3"
|
"@solid-primitives/rootless" "^1.1.3"
|
||||||
"@solid-primitives/utils" "^3.0.2"
|
"@solid-primitives/utils" "^3.0.2"
|
||||||
|
|
||||||
|
"@solid-primitives/memo@^1.0.2":
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solid-primitives/memo/-/memo-1.0.2.tgz#7a33216e665a94ac85413be206dacf3f295221d0"
|
||||||
|
integrity sha512-I4BKJAItiRxjR1ngc+gWsdpiz3V79LQdgxxRlFPp3K+8Oi2dolXweDlLKKX5qec8cSuhV99gTfsxEoVBMkzNgQ==
|
||||||
|
dependencies:
|
||||||
|
"@solid-primitives/scheduled" "1.0.1"
|
||||||
|
"@solid-primitives/utils" "^3.0.2"
|
||||||
|
|
||||||
"@solid-primitives/platform@^0.0.101":
|
"@solid-primitives/platform@^0.0.101":
|
||||||
version "0.0.101"
|
version "0.0.101"
|
||||||
resolved "https://registry.yarnpkg.com/@solid-primitives/platform/-/platform-0.0.101.tgz#7bfa879152a59169589e2dc999aac8ceb63233c7"
|
resolved "https://registry.yarnpkg.com/@solid-primitives/platform/-/platform-0.0.101.tgz#7bfa879152a59169589e2dc999aac8ceb63233c7"
|
||||||
|
@ -2737,6 +2745,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@solid-primitives/utils" "^3.0.2"
|
"@solid-primitives/utils" "^3.0.2"
|
||||||
|
|
||||||
|
"@solid-primitives/scheduled@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@solid-primitives/scheduled/-/scheduled-1.0.1.tgz#e5b07452f39d27504c4ba1caa64d65020110c017"
|
||||||
|
integrity sha512-zRyW9L4nYdL0yZktvJz/Ye9kVNa6UW26L71sZEqzzHnxvDidbT+mln4np7jqFrAeGiWMwWnRDR/ZvM0FK85jMw==
|
||||||
|
|
||||||
"@solid-primitives/scheduled@^1.0.1":
|
"@solid-primitives/scheduled@^1.0.1":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@solid-primitives/scheduled/-/scheduled-1.0.2.tgz#8c2e8511b9b361c22c13e78377dc4168cc9c0452"
|
resolved "https://registry.yarnpkg.com/@solid-primitives/scheduled/-/scheduled-1.0.2.tgz#8c2e8511b9b361c22c13e78377dc4168cc9c0452"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user