feed period select (#340)

* feed period select

* fix, unused code removed

* Fix styles

* Fix styles

* Fix styles

---------

Co-authored-by: Igor Lobanov <igor.lobanov@onetwotrip.com>
Co-authored-by: ilya-bkv <i.yablokov@ccmp.me>
This commit is contained in:
Igor Lobanov 2023-12-20 09:07:57 +01:00 committed by GitHub
parent 88d35ce2bc
commit 3a7d138eef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 260 additions and 92 deletions

View File

@ -357,9 +357,12 @@
"This comment has not yet been rated": "This comment has not yet been rated",
"This email is already taken. If it's you": "This email is already taken. If it's you",
"This functionality is currently not available, we would like to work on this issue. Use the download link.": "This functionality is currently not available, we would like to work on this issue. Use the download link.",
"This month": "This month",
"This post has not been rated yet": "This post has not been rated yet",
"This way we&nbsp;ll realize that you&nbsp;re a real person and&nbsp;ll take your vote into account. And&nbsp;you&nbsp;ll see how others voted": "This way we&nbsp;ll realize that you&nbsp;re a real person and&nbsp;ll take your vote into account. And&nbsp;you&nbsp;ll see how others voted",
"This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed": "This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed",
"This week": "This week",
"This year": "This year",
"To leave a comment please": "To leave a comment please",
"To write a comment, you must": "To write a comment, you must",
"Top authors": "Authors rating",

View File

@ -377,9 +377,12 @@
"This comment has not yet been rated": "Этот комментарий еще пока никто не оценил",
"This email is already taken. If it's you": "Такой email уже зарегистрирован. Если это вы",
"This functionality is currently not available, we would like to work on this issue. Use the download link.": "В данный момент этот функционал не доступен, бы работаем над этой проблемой. Воспользуйтесь загрузкой по ссылке.",
"This month": "За месяц",
"This post has not been rated yet": "Эту публикацию еще пока никто не оценил",
"This way we&nbsp;ll realize that you&nbsp;re a real person and&nbsp;ll take your vote into account. And&nbsp;you&nbsp;ll see how others voted": "Так мы&nbsp;поймем, что вы&nbsp;реальный человек, и&nbsp;учтем ваш голос. А&nbsp;вы&nbsp;увидите, как проголосовали другие",
"This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed": "Так вы&nbsp;сможете подписаться на&nbsp;авторов, интересные темы и&nbsp;настроить свою ленту",
"This week": "За неделю",
"This year": "За год",
"To leave a comment please": "Чтобы оставить комментарий, необходимо",
"To write a comment, you must": "Чтобы написать комментарий, необходимо",
"Top authors": "Рейтинг авторов",

View File

@ -135,7 +135,7 @@ export const FullArticle = (props: Props) => {
scrollTo(commentsRef.current)
}
const { searchParams, changeSearchParam } = useRouter<ArticlePageSearchParams>()
const { searchParams, changeSearchParams } = useRouter<ArticlePageSearchParams>()
createEffect(() => {
if (props.scrollToComments) {
@ -146,7 +146,7 @@ export const FullArticle = (props: Props) => {
createEffect(() => {
if (searchParams()?.scrollTo === 'comments' && commentsRef.current) {
scrollToComments()
changeSearchParam({
changeSearchParams({
scrollTo: null,
})
}
@ -158,7 +158,7 @@ export const FullArticle = (props: Props) => {
`[id='comment_${searchParams().commentId}']`,
)
changeSearchParam({ commentId: null })
changeSearchParams({ commentId: null })
if (commentElement) {
scrollTo(commentElement)

View File

@ -13,7 +13,7 @@ type Props = {
export const AuthGuard = (props: Props) => {
const { isAuthenticated, isSessionLoaded } = useSession()
const { changeSearchParam } = useRouter<RootSearchParams & AuthModalSearchParams>()
const { changeSearchParams } = useRouter<RootSearchParams & AuthModalSearchParams>()
createEffect(() => {
if (props.disabled) {
@ -23,7 +23,7 @@ export const AuthGuard = (props: Props) => {
if (isAuthenticated()) {
hideModal()
} else {
changeSearchParam(
changeSearchParams(
{
source: 'authguard',
modal: 'auth',

View File

@ -29,7 +29,7 @@ export const AuthorBadge = (props: Props) => {
subscriptions,
actions: { loadSubscriptions, requireAuthentication },
} = useSession()
const { changeSearchParam } = useRouter()
const { changeSearchParams } = useRouter()
const { t, formatDate } = useLocalize()
const subscribed = createMemo(() =>
subscriptions().authors.some((author) => author.slug === props.author.slug),
@ -54,7 +54,7 @@ export const AuthorBadge = (props: Props) => {
const initChat = () => {
requireAuthentication(() => {
openPage(router, `inbox`)
changeSearchParam({
changeSearchParams({
initChat: props.author.id.toString(),
})
}, 'discussions')

View File

@ -72,11 +72,11 @@ export const AuthorCard = (props: Props) => {
})
// TODO: reimplement AuthorCard
const { changeSearchParam } = useRouter()
const { changeSearchParams } = useRouter()
const initChat = () => {
requireAuthentication(() => {
openPage(router, `inbox`)
changeSearchParam({
changeSearchParams({
initChat: props.author.id.toString(),
})
}, 'discussions')

View File

@ -7,7 +7,7 @@ import styles from './Hero.module.scss'
export default () => {
const { t } = useLocalize()
const { changeSearchParam } = useRouter<AuthModalSearchParams>()
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
return (
<div class={styles.aboutDiscours}>
@ -28,7 +28,7 @@ export default () => {
class="button"
onClick={() => {
showModal('auth')
changeSearchParam({
changeSearchParams({
mode: 'register',
})
}}

View File

@ -98,11 +98,11 @@ export const ArticleCard = (props: ArticleCardProps) => {
const canEdit = () => props.article.authors?.some((a) => a.slug === user()?.slug)
const { changeSearchParam } = useRouter()
const { changeSearchParams } = useRouter()
const scrollToComments = (event) => {
event.preventDefault()
openPage(router, 'article', { slug: props.article.slug })
changeSearchParam({
changeSearchParams({
scrollTo: 'comments',
})
}

View File

@ -20,7 +20,7 @@ type FormFields = {
type ValidationErrors = Partial<Record<keyof FormFields, string | JSX.Element>>
export const ForgotPasswordForm = () => {
const { changeSearchParam } = useRouter<AuthModalSearchParams>()
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
const { t, lang } = useLocalize()
const handleEmailInput = (newEmail: string) => {
setValidationErrors(({ email: _notNeeded, ...rest }) => rest)
@ -119,7 +119,7 @@ export const ForgotPasswordForm = () => {
href="#"
onClick={(event) => {
event.preventDefault()
changeSearchParam({
changeSearchParams({
mode: 'register',
})
}}
@ -141,7 +141,7 @@ export const ForgotPasswordForm = () => {
<span
class={styles.authLink}
onClick={() =>
changeSearchParam({
changeSearchParams({
mode: 'login',
})
}

View File

@ -47,7 +47,7 @@ export const LoginForm = () => {
actions: { signIn },
} = useSession()
const { changeSearchParam } = useRouter<AuthModalSearchParams>()
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
const [password, setPassword] = createSignal('')
@ -201,7 +201,7 @@ export const LoginForm = () => {
<span
class="link"
onClick={() =>
changeSearchParam({
changeSearchParams({
mode: 'forgot-password',
})
}
@ -218,7 +218,7 @@ export const LoginForm = () => {
<span
class={styles.authLink}
onClick={() =>
changeSearchParam({
changeSearchParams({
mode: 'register',
})
}

View File

@ -32,7 +32,7 @@ const handleEmailInput = (newEmail: string) => {
}
export const RegisterForm = () => {
const { changeSearchParam } = useRouter<AuthModalSearchParams>()
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
const { t } = useLocalize()
const { emailChecks } = useEmailChecks()
@ -202,7 +202,7 @@ export const RegisterForm = () => {
href="#"
onClick={(event) => {
event.preventDefault()
changeSearchParam({
changeSearchParams({
mode: 'login',
})
}}
@ -255,7 +255,7 @@ export const RegisterForm = () => {
<span
class={styles.authLink}
onClick={() =>
changeSearchParam({
changeSearchParams({
mode: 'login',
})
}

View File

@ -42,7 +42,7 @@ export const NotificationView = (props: Props) => {
actions: { markNotificationAsRead, hideNotificationsPanel },
} = useNotifications()
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
const { changeSearchParams } = useRouter<ArticlePageSearchParams>()
const { t, formatDate, formatTime } = useLocalize()
@ -139,7 +139,7 @@ export const NotificationView = (props: Props) => {
openPage(router, 'article', { slug: data().shout.slug })
if (data().reactionIds) {
changeSearchParam({ commentId: data().reactionIds[0].toString() })
changeSearchParams({ commentId: data().reactionIds[0].toString() })
}
}

View File

@ -31,7 +31,7 @@ const ALPHABET = [...'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫ
export const AllAuthorsView = (props: Props) => {
const { t, lang } = useLocalize()
const [limit, setLimit] = createSignal(PAGE_SIZE)
const { searchParams, changeSearchParam } = useRouter<AllAuthorsPageSearchParams>()
const { searchParams, changeSearchParams } = useRouter<AllAuthorsPageSearchParams>()
const { sortedAuthors } = useAuthorsStore({
authors: props.authors,
sortBy: searchParams().by || 'shouts',
@ -41,7 +41,7 @@ export const AllAuthorsView = (props: Props) => {
createEffect(() => {
if (!searchParams().by) {
changeSearchParam({
changeSearchParams({
by: 'shouts',
})
}

View File

@ -31,7 +31,7 @@ const ALPHABET = [...'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫ
export const AllTopicsView = (props: Props) => {
const { t, lang } = useLocalize()
const { searchParams, changeSearchParam } = useRouter<AllTopicsPageSearchParams>()
const { searchParams, changeSearchParams } = useRouter<AllTopicsPageSearchParams>()
const [limit, setLimit] = createSignal(PAGE_SIZE)
const { sortedTopics } = useTopicsStore({
@ -43,7 +43,7 @@ export const AllTopicsView = (props: Props) => {
createEffect(() => {
if (!searchParams().by) {
changeSearchParam({
changeSearchParams({
by: 'shouts',
})
}

View File

@ -46,7 +46,7 @@ export const Expo = (props: Props) => {
})
const getLoadShoutsFilters = (additionalFilters: LoadShoutsFilters = {}): LoadShoutsFilters => {
const filters = { ...additionalFilters }
const filters = { visibility: 'public', ...additionalFilters }
if (props.layout) {
filters.layout = props.layout

View File

@ -1,7 +1,4 @@
.feedFilter {
margin-bottom: 4.8rem;
margin-top: 0.2em;
@include media-breakpoint-down(md) {
margin-right: 4rem !important;
}
@ -192,3 +189,25 @@
font-weight: 500;
}
.filtersContainer {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4rem;
.feedFilter {
margin-top: 0;
margin-bottom: 0;
& > li {
margin-bottom: 0;
}
}
}
.periodSwitcher {
font-size: 14px;
font-weight: 700;
line-height: 18px;
}

View File

@ -3,7 +3,7 @@ import type { Author, LoadShoutsOptions, Reaction, Shout } from '../../../graphq
import { getPagePath } from '@nanostores/router'
import { Meta } from '@solidjs/meta'
import { clsx } from 'clsx'
import { createEffect, createSignal, For, on, onMount, Show } from 'solid-js'
import { createEffect, createMemo, createSignal, For, on, onMount, Show } from 'solid-js'
import { useLocalize } from '../../../context/localize'
import { useReactions } from '../../../context/reactions'
@ -13,6 +13,8 @@ import { useTopAuthorsStore } from '../../../stores/zine/topAuthors'
import { useTopicsStore } from '../../../stores/zine/topics'
import { apiClient } from '../../../utils/apiClient'
import { getImageUrl } from '../../../utils/getImageUrl'
import { getServerDate } from '../../../utils/getServerDate'
import { DropDown } from '../../_shared/DropDown'
import { Icon } from '../../_shared/Icon'
import { Loading } from '../../_shared/Loading'
import { CommentDate } from '../../Article/CommentDate'
@ -28,8 +30,16 @@ import stylesTopic from '../../Feed/CardTopic.module.scss'
export const FEED_PAGE_SIZE = 20
const UNRATED_ARTICLES_COUNT = 5
type FeedPeriod = 'week' | 'month' | 'year'
type PeriodItem = {
value: FeedPeriod
title: string
}
type FeedSearchParams = {
by: 'publish_date' | 'rating' | 'last_comment'
period: FeedPeriod
}
const getOrderBy = (by: FeedSearchParams['by']) => {
@ -53,7 +63,16 @@ type Props = {
export const Feed = (props: Props) => {
const { t } = useLocalize()
const { page, searchParams } = useRouter<FeedSearchParams>()
const monthPeriod: PeriodItem = { value: 'month', title: t('This month') }
const periods: PeriodItem[] = [
{ value: 'week', title: t('This week') },
monthPeriod,
{ value: 'year', title: t('This year') },
]
const { page, searchParams, changeSearchParams } = useRouter<FeedSearchParams>()
const [isLoading, setIsLoading] = createSignal(false)
const [isRightColumnLoaded, setIsRightColumnLoaded] = createSignal(false)
@ -64,6 +83,16 @@ export const Feed = (props: Props) => {
const [topComments, setTopComments] = createSignal<Reaction[]>([])
const [unratedArticles, setUnratedArticles] = createSignal<Shout[]>([])
const currentPeriod = createMemo(() => {
const period = periods.find((p) => p.value === searchParams().period)
if (!period) {
return monthPeriod
}
return period
})
const {
actions: { loadReactionsBy },
} = useReactions()
@ -86,7 +115,7 @@ export const Feed = (props: Props) => {
createEffect(
on(
() => page().route + searchParams().by,
() => page().route + searchParams().by + searchParams().period,
() => {
resetSortedArticles()
loadMore()
@ -94,6 +123,21 @@ export const Feed = (props: Props) => {
{ defer: true },
),
)
const getFromDate = (period: FeedPeriod): Date => {
const now = new Date()
switch (period) {
case 'week': {
return new Date(now.setDate(now.getDate() - 7))
}
case 'month': {
return new Date(now.setMonth(now.getMonth() - 1))
}
case 'year': {
return new Date(now.setFullYear(now.getFullYear() - 1))
}
}
}
const loadFeedShouts = () => {
const options: LoadShoutsOptions = {
limit: FEED_PAGE_SIZE,
@ -106,6 +150,12 @@ export const Feed = (props: Props) => {
options.order_by = orderBy
}
if (searchParams().by && searchParams().by !== 'publish_date') {
const period = searchParams().period || 'month'
const fromDate = getFromDate(period)
options.filters = { fromDate: getServerDate(fromDate) }
}
return props.loadShouts(options)
}
@ -148,32 +198,49 @@ export const Feed = (props: Props) => {
</div>
<div class="col-md-12 offset-xl-1">
<ul class={clsx(styles.feedFilter, 'view-switcher')}>
<li
class={clsx({
'view-switcher__item--selected': searchParams().by === 'publish_date' || !searchParams().by,
})}
>
<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>
<div class={styles.filtersContainer}>
<ul class={clsx('view-switcher', styles.feedFilter)}>
<li
class={clsx({
'view-switcher__item--selected':
searchParams().by === 'publish_date' || !searchParams().by,
})}
>
<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',
})}
>
<span class="link" onClick={() => changeSearchParams({ by: 'rating' })}>
{t('Top rated')}
</span>
</li>
<li
class={clsx({
'view-switcher__item--selected': searchParams().by === 'last_comment',
})}
>
<span class="link" onClick={() => changeSearchParams({ by: 'last_comment' })}>
{t('Most commented')}
</span>
</li>
</ul>
<Show when={searchParams().by && searchParams().by !== 'publish_date'}>
<div>
<DropDown
options={periods}
currentOption={currentPeriod()}
triggerCssClass={styles.periodSwitcher}
onChange={(period) => changeSearchParams({ period: period.value })}
/>
</div>
</Show>
</div>
<Show when={!isLoading()} fallback={<Loading />}>
<Show when={sortedArticles().length > 0}>

View File

@ -115,9 +115,7 @@ export const HomeView = (props: Props) => {
wrapper={'top-article'}
nodate={true}
/>
<Row3 articles={sortedArticles().slice(6, 9)} nodate={true} />
<Beside
beside={sortedArticles()[9]}
title={t('Top authors')}
@ -125,15 +123,11 @@ export const HomeView = (props: Props) => {
wrapper={'author'}
nodate={true}
/>
<Show when={topMonthArticles()}>
<ArticleCardSwiper title={t('Top month articles')} slides={topMonthArticles()} />
</Show>
<Row2 articles={sortedArticles().slice(10, 12)} nodate={true} />
<RowShort articles={sortedArticles().slice(12, 16)} />
<Row1 article={sortedArticles()[16]} nodate={true} />
<Row3 articles={sortedArticles().slice(17, 20)} nodate={true} />
<Row3
@ -141,13 +135,10 @@ export const HomeView = (props: Props) => {
header={<h2>{t('Top commented')}</h2>}
nodate={true}
/>
{randomLayout()}
<Show when={topArticles()}>
<ArticleCardSwiper title={t('Favorite')} slides={topArticles()} />
</Show>
<Beside
beside={sortedArticles()[20]}
title={t('Top topics')}
@ -156,11 +147,8 @@ export const HomeView = (props: Props) => {
isTopicCompact={true}
nodate={true}
/>
<Row3 articles={sortedArticles().slice(21, 24)} nodate={true} />
<Banner />
<Row2 articles={sortedArticles().slice(24, 26)} nodate={true} />
<Row3 articles={sortedArticles().slice(26, 29)} nodate={true} />
<Row2 articles={sortedArticles().slice(29, 31)} nodate={true} />

View File

@ -52,7 +52,7 @@ export const InboxView = () => {
const [isClear, setClear] = createSignal(false)
const { session } = useSession()
const currentUserId = createMemo(() => session()?.user.id)
const { changeSearchParam, searchParams } = useRouter<InboxSearchParams>()
const { changeSearchParams, searchParams } = useRouter<InboxSearchParams>()
// Поиск по диалогам
const getQuery = (query) => {
if (query().length >= 2) {
@ -67,7 +67,7 @@ export const InboxView = () => {
const handleOpenChat = async (chat: Chat) => {
setCurrentDialog(chat)
changeSearchParam({
changeSearchParams({
chat: chat.id,
})
try {
@ -126,7 +126,7 @@ export const InboxView = () => {
try {
const newChat = await createChat([Number(searchParams().initChat)], '')
await loadChats()
changeSearchParam({
changeSearchParams({
initChat: null,
chat: newChat.chat.id,
})

View File

@ -41,7 +41,7 @@ const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
export const TopicView = (props: Props) => {
const { t } = useLocalize()
const { searchParams, changeSearchParam } = useRouter<TopicsPageSearchParams>()
const { searchParams, changeSearchParams } = useRouter<TopicsPageSearchParams>()
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
@ -125,7 +125,7 @@ export const TopicView = (props: Props) => {
<button
type="button"
onClick={() =>
changeSearchParam({
changeSearchParams({
by: 'recent',
})
}
@ -135,17 +135,17 @@ export const TopicView = (props: Props) => {
</li>
{/*TODO: server sort*/}
{/*<li classList={{ 'view-switcher__item--selected': getSearchParams().by === 'rating' }}>*/}
{/* <button type="button" onClick={() => changeSearchParam('by', 'rating')}>*/}
{/* <button type="button" onClick={() => changeSearchParams('by', 'rating')}>*/}
{/* {t('Popular')}*/}
{/* </button>*/}
{/*</li>*/}
{/*<li classList={{ 'view-switcher__item--selected': getSearchParams().by === 'viewed' }}>*/}
{/* <button type="button" onClick={() => changeSearchParam('by', 'viewed')}>*/}
{/* <button type="button" onClick={() => changeSearchParams('by', 'viewed')}>*/}
{/* {t('Views')}*/}
{/* </button>*/}
{/*</li>*/}
{/*<li classList={{ 'view-switcher__item--selected': getSearchParams().by === 'commented' }}>*/}
{/* <button type="button" onClick={() => changeSearchParam('by', 'commented')}>*/}
{/* <button type="button" onClick={() => changeSearchParams('by', 'commented')}>*/}
{/* {t('Discussing')}*/}
{/* </button>*/}
{/*</li>*/}

View File

@ -0,0 +1,7 @@
.chevron {
vertical-align: top;
&.rotate {
transform: rotate(180deg);
}
}

View File

@ -0,0 +1,69 @@
import type { PopupProps } from '../Popup'
import { clsx } from 'clsx'
import { createSignal, For, Show } from 'solid-js'
import { Popup } from '../Popup'
import styles from './DropDown.module.scss'
export type Option = {
value: string | number
title: string
}
type Props<TOption> = {
class?: string
popupProps?: PopupProps
options: TOption[]
currentOption: TOption
triggerCssClass?: string
onChange: (option: TOption) => void
}
const Chevron = (props: { class?: string }) => {
return (
<svg
class={props.class}
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
>
<path d="M13.5 6L9 12L4.5 6H13.5Z" fill="#141414" />
</svg>
)
}
export const DropDown = <TOption extends Option = Option>(props: Props<TOption>) => {
const [isPopupVisible, setIsPopupVisible] = createSignal(false)
return (
<Show when={props.currentOption} keyed={true}>
<Popup
trigger={
<div class={props.triggerCssClass}>
{props.currentOption.title}{' '}
<Chevron
class={clsx(styles.chevron, {
[styles.rotate]: isPopupVisible(),
})}
/>
</div>
}
variant="tiny"
onVisibilityChange={(isVisible) => setIsPopupVisible(isVisible)}
{...props.popupProps}
>
<For each={props.options.filter((p) => p.value !== props.currentOption.value)}>
{(option) => (
<div class="link" onClick={() => props.onChange(option)}>
{option.title}
</div>
)}
</For>
</Popup>
</Show>
)
}

View File

@ -0,0 +1 @@
export { DropDown } from './DropDown'

View File

@ -32,7 +32,9 @@ export const Popup = (props: PopupProps) => {
useOutsideClickHandler({
containerRef,
predicate: () => isVisible(),
handler: () => setIsVisible(false),
handler: () => {
setIsVisible(false)
},
})
const toggle = () => setIsVisible((oldVisible) => !oldVisible)

View File

@ -32,7 +32,7 @@ export function useLocalize() {
export const LocalizeProvider = (props: { children: JSX.Element }) => {
const [lang, setLang] = createSignal<Language>(i18next.language === 'en' ? 'en' : 'ru')
const { searchParams, changeSearchParam } = useRouter<{
const { searchParams, changeSearchParams } = useRouter<{
lng: string
}>()
@ -46,7 +46,7 @@ export const LocalizeProvider = (props: { children: JSX.Element }) => {
changeLanguage(lng)
setLang(lng)
Cookie.set('lng', lng)
changeSearchParam({ lng: null }, true)
changeSearchParams({ lng: null }, true)
})
const formatTime = (date: Date, options: Intl.DateTimeFormatOptions = {}) => {

View File

@ -12,7 +12,10 @@ import { loadMyFeed, loadShouts, resetSortedArticles } from '../stores/zine/arti
const handleFeedLoadShouts = (options: LoadShoutsOptions) => {
return loadShouts({
...options,
filters: { visibility: 'community' },
filters: {
visibility: 'community',
...options.filters,
},
})
}

View File

@ -138,7 +138,7 @@ export const useRouter = <TSearchParams extends Record<string, string> = Record<
const page = useStore(routerStore)
const searchParams = useStore(searchParamsStore) as unknown as Accessor<TSearchParams>
const changeSearchParam = (newValues: Partial<TSearchParams>, replace = false) => {
const changeSearchParams = (newValues: Partial<TSearchParams>, replace = false) => {
const newSearchParams = { ...searchParamsStore.get() }
Object.keys(newValues).forEach((key) => {
@ -155,6 +155,6 @@ export const useRouter = <TSearchParams extends Record<string, string> = Record<
return {
page,
searchParams,
changeSearchParam,
changeSearchParams,
}
}

View File

@ -42,13 +42,13 @@ export const MODALS: Record<ModalType, ModalType> = {
const [modal, setModal] = createSignal<ModalType>(null)
const { searchParams, changeSearchParam } = useRouter<
const { searchParams, changeSearchParams } = useRouter<
AuthModalSearchParams & ConfirmEmailSearchParams & RootSearchParams
>()
export const showModal = (modalType: ModalType, modalSource?: AuthModalSource) => {
if (modalSource) {
changeSearchParam({
changeSearchParams({
source: modalSource,
})
}
@ -70,7 +70,7 @@ export const hideModal = () => {
newSearchParams.mode = null
}
changeSearchParam(newSearchParams, true)
changeSearchParams(newSearchParams, true)
setModal(null)
}

View File

@ -204,7 +204,7 @@ a:hover,
a:visited,
a:link,
.link {
border-bottom: 1px solid rgb(0 0 0 / 30%);
border-bottom: 2px solid rgb(0 0 0 / 30%);
text-decoration: none;
cursor: pointer;
}
@ -624,6 +624,10 @@ figure {
margin-bottom: 0.6em;
white-space: nowrap;
.link {
border-bottom: none;
}
&:last-child {
margin-right: 0;
}
@ -645,9 +649,10 @@ figure {
}
a,
.link,
.linkReplacement,
button {
border-bottom: 2px solid transparent;
border-bottom: 1px solid transparent;
color: var(--link-color);
cursor: pointer;
font-weight: inherit;
@ -662,6 +667,7 @@ figure {
font-weight: bold;
a,
.link,
.linkReplacement,
button {
border-bottom: 2px solid #000;