subs-refactoring

This commit is contained in:
Untone 2024-05-21 02:15:52 +03:00
parent bec333f7c3
commit 79f94876f0
24 changed files with 253 additions and 336 deletions

View File

@ -423,14 +423,14 @@
"subscriber": "subscriber", "subscriber": "subscriber",
"subscriber_rp": "subscriber", "subscriber_rp": "subscriber",
"subscribers": "subscribers", "subscribers": "subscribers",
"SubscriberWithCount": "{count, plural, =0 {no followers} one {{count} follower} other {{count} followers}}", "FollowersWithCount": "{count, plural, =0 {no followers} one {{count} follower} other {{count} followers}}",
"subscribing...": "subscribing...", "subscribing...": "subscribing...",
"subscription": "subscription", "subscription": "subscription",
"Subscription": "Subscription", "Subscription": "Subscription",
"subscription_rp": "subscription", "subscription_rp": "subscription",
"subscriptions": "subscriptions", "subscriptions": "subscriptions",
"Subscriptions": "Subscriptions", "Subscriptions": "Subscriptions",
"SubscriptionWithCount": "{count, plural, =0 {no subscriptions} one {{count} subscription} other {{count} subscriptions}}", "FollowsWithCount": "{count, plural, =0 {no subscriptions} one {{count} subscription} other {{count} subscriptions}}",
"Substrate": "Substrate", "Substrate": "Substrate",
"Success": "Success", "Success": "Success",
"Successfully authorized": "Authorization successful", "Successfully authorized": "Authorization successful",

View File

@ -451,11 +451,11 @@
"subscriber": "подписчик", "subscriber": "подписчик",
"subscriber_rp": "подписчика", "subscriber_rp": "подписчика",
"subscribers": "подписчиков", "subscribers": "подписчиков",
"SubscriberWithCount": "{count, plural, =0 {нет подписчиков} one {{count} подписчик} few {{count} подписчика} other {{count} подписчиков}}", "FollowersWithCount": "{count, plural, =0 {нет подписчиков} one {{count} подписчик} few {{count} подписчика} other {{count} подписчиков}}",
"subscribing...": "Подписка...", "subscribing...": "Подписка...",
"Subscription": "Подписка", "Subscription": "Подписка",
"Subscriptions": "Подписки", "Subscriptions": "Подписки",
"SubscriptionWithCount": "{count, plural, =0 {нет подписок} one {{count} подписка} few {{count} подписки} other {{count} подписок}}", "FollowsWithCount": "{count, plural, =0 {нет подписок} one {{count} подписка} few {{count} подписки} other {{count} подписок}}",
"Substrate": "Подложка", "Substrate": "Подложка",
"Success": "Успешно", "Success": "Успешно",
"Successfully authorized": "Авторизация успешна", "Successfully authorized": "Авторизация успешна",

View File

@ -31,16 +31,7 @@ export const AudioPlayer = (props: Props) => {
const [isPlaying, setIsPlaying] = createSignal(false) const [isPlaying, setIsPlaying] = createSignal(false)
const currentTack = createMemo(() => props.media[currentTrackIndex()]) const currentTack = createMemo(() => props.media[currentTrackIndex()])
createEffect(on(currentTrackIndex, () => setCurrentTrackDuration(0), { defer: true }))
createEffect(
on(
() => currentTrackIndex(),
() => {
setCurrentTrackDuration(0)
},
{ defer: true },
),
)
const handlePlayMedia = async (trackIndex: number) => { const handlePlayMedia = async (trackIndex: number) => {
setIsPlaying(!isPlaying() || trackIndex !== currentTrackIndex()) setIsPlaying(!isPlaying() || trackIndex !== currentTrackIndex())

View File

@ -1,6 +1,6 @@
import { openPage } from '@nanostores/router' import { openPage } from '@nanostores/router'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Match, Show, Switch, createEffect, createMemo, createSignal } from 'solid-js' import { Match, Show, Switch, createEffect, createMemo, createSignal, on } from 'solid-js'
import { useFollowing } from '../../../context/following' import { useFollowing } from '../../../context/following'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
@ -34,17 +34,19 @@ export const AuthorBadge = (props: Props) => {
const { author, requireAuthentication } = useSession() const { author, requireAuthentication } = useSession()
const { follow, unfollow, follows, following } = useFollowing() const { follow, unfollow, follows, following } = useFollowing()
const [isMobileView, setIsMobileView] = createSignal(false) const [isMobileView, setIsMobileView] = createSignal(false)
const [isFollowed, setIsFollowed] = createSignal<boolean>() const [isFollowed, setIsFollowed] = createSignal<boolean>(
follows?.authors?.some((authorEntity) => authorEntity.id === props.author?.id),
createEffect(() => { )
if (!(follows && props.author)) return createEffect(() => setIsMobileView(!mediaMatches.sm))
const followed = follows?.authors?.some((authorEntity) => authorEntity.id === props.author?.id) createEffect(
setIsFollowed(followed) on(
}) [() => follows?.authors, () => props.author, following],
([followingAuthors, currentAuthor, _]) => {
createEffect(() => { setIsFollowed(followingAuthors?.some((followedAuthor) => followedAuthor.id === currentAuthor?.id))
setIsMobileView(!mediaMatches.sm) },
}) { defer: true },
),
)
const { changeSearchParams } = useRouter() const { changeSearchParams } = useRouter()
const { t, formatDate, lang } = useLocalize() const { t, formatDate, lang } = useLocalize()
@ -132,7 +134,7 @@ export const AuthorBadge = (props: Props) => {
<Show when={props.author.slug !== author()?.slug && !props.nameOnly}> <Show when={props.author.slug !== author()?.slug && !props.nameOnly}>
<div class={styles.actions}> <div class={styles.actions}>
<FollowingButton <FollowingButton
action={() => handleFollowClick()} action={handleFollowClick}
isFollowed={isFollowed()} isFollowed={isFollowed()}
actionMessageType={following()?.slug === props.author.slug ? following().type : undefined} actionMessageType={following()?.slug === props.author.slug ? following().type : undefined}
/> />

View File

@ -429,7 +429,7 @@
} }
} }
.subscribersContainer { .followersContainer {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
font-size: 1.4rem; font-size: 1.4rem;
@ -440,7 +440,7 @@
} }
} }
.subscribers { .followers {
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
display: inline-flex; display: inline-flex;
@ -456,7 +456,7 @@
margin-right: 0; margin-right: 0;
} }
.subscribersItem { .followersItem {
position: relative; position: relative;
&:nth-child(1) { &:nth-child(1) {
@ -473,7 +473,7 @@
} }
} }
.subscribersCounter { .followsCounter {
font-weight: 500; font-weight: 500;
margin-left: 1rem; margin-left: 1rem;
} }
@ -481,7 +481,7 @@
&:hover { &:hover {
background: none !important; background: none !important;
.subscribersCounter { .followsCounter {
background: var(--background-color-invert); background: var(--background-color-invert);
} }
} }

View File

@ -27,7 +27,7 @@ import styles from './AuthorCard.module.scss'
type Props = { type Props = {
author: Author author: Author
followers?: Author[] followers?: Author[]
following?: Array<Author | Topic> flatFollows?: Array<Author | Topic>
} }
export const AuthorCard = (props: Props) => { export const AuthorCard = (props: Props) => {
const { t, lang } = useLocalize() const { t, lang } = useLocalize()
@ -39,7 +39,7 @@ export const AuthorCard = (props: Props) => {
const { follow, unfollow, follows, following } = useFollowing() const { follow, unfollow, follows, following } = useFollowing()
onMount(() => { onMount(() => {
setAuthorSubs(props.following) setAuthorSubs(props.flatFollows)
}) })
createEffect(() => { createEffect(() => {
@ -71,15 +71,15 @@ export const AuthorCard = (props: Props) => {
} }
createEffect(() => { createEffect(() => {
if (props.following) { if (props.flatFollows) {
if (followsFilter() === 'authors') { if (followsFilter() === 'authors') {
setAuthorSubs(props.following.filter((s) => 'name' in s)) setAuthorSubs(props.flatFollows.filter((s) => 'name' in s))
} else if (followsFilter() === 'topics') { } else if (followsFilter() === 'topics') {
setAuthorSubs(props.following.filter((s) => 'title' in s)) setAuthorSubs(props.flatFollows.filter((s) => 'title' in s))
} else if (followsFilter() === 'communities') { } else if (followsFilter() === 'communities') {
setAuthorSubs(props.following.filter((s) => 'title' in s)) setAuthorSubs(props.flatFollows.filter((s) => 'title' in s))
} else { } else {
setAuthorSubs(props.following) setAuthorSubs(props.flatFollows)
} }
} }
}) })
@ -108,6 +108,73 @@ export const AuthorCard = (props: Props) => {
return t('Follow') return t('Follow')
}) })
const FollowersModalView = () => (
<>
<h2>{t('Followers')}</h2>
<div class={styles.listWrapper}>
<div class="row">
<div class="col-24">
<For each={props.followers}>{(follower: Author) => <AuthorBadge author={follower} />}</For>
</div>
</div>
</div>
</>
)
const FollowingModalView = () => (
<>
<h2>{t('Subscriptions')}</h2>
<ul class="view-switcher">
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'all',
})}
>
<button type="button" onClick={() => setFollowsFilter('all')}>
{t('All')}
</button>
<span class="view-switcher__counter">{props.flatFollows.length}</span>
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'authors',
})}
>
<button type="button" onClick={() => setFollowsFilter('authors')}>
{t('Authors')}
</button>
<span class="view-switcher__counter">{props.flatFollows.filter((s) => 'name' in s).length}</span>
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'topics',
})}
>
<button type="button" onClick={() => setFollowsFilter('topics')}>
{t('Topics')}
</button>
<span class="view-switcher__counter">{props.flatFollows.filter((s) => 'title' in s).length}</span>
</li>
</ul>
<br />
<div class={styles.listWrapper}>
<div class="row">
<div class="col-24">
<For each={authorSubs()}>
{(subscription) =>
isAuthor(subscription) ? (
<AuthorBadge author={subscription} subscriptionsMode={true} />
) : (
<TopicBadge topic={subscription} subscriptionsMode={true} />
)
}
</For>
</div>
</div>
</div>
</>
)
return ( return (
<div class={clsx(styles.author, 'row')}> <div class={clsx(styles.author, 'row')}>
<div class="col-md-5"> <div class="col-md-5">
@ -125,35 +192,30 @@ export const AuthorCard = (props: Props) => {
<Show when={props.author.bio}> <Show when={props.author.bio}>
<div class={styles.authorAbout} innerHTML={props.author.bio} /> <div class={styles.authorAbout} innerHTML={props.author.bio} />
</Show> </Show>
<Show when={props.followers?.length > 0 || props.following?.length > 0}> <Show when={props.followers?.length > 0 || props.flatFollows?.length > 0}>
<div class={styles.subscribersContainer}> <div class={styles.followersContainer}>
<Show when={props.followers && props.followers.length > 0}> <Show when={props.followers && props.followers.length > 0}>
<a href="?m=followers" class={styles.subscribers}> <a href="?m=followers" class={styles.followers}>
<For each={props.followers.slice(0, 3)}> <For each={props.followers.slice(0, 3)}>
{(f) => ( {(f) => (
<Userpic size={'XS'} name={f.name} userpic={f.pic} class={styles.subscribersItem} /> <Userpic size={'XS'} name={f.name} userpic={f.pic} class={styles.followersItem} />
)} )}
</For> </For>
<div class={styles.subscribersCounter}> <div class={styles.followsCounter}>
{t('SubscriberWithCount', { {t('FollowersWithCount', {
count: props.followers.length ?? 0, count: props.followers.length ?? 0,
})} })}
</div> </div>
</a> </a>
</Show> </Show>
<Show when={props.following && props.following.length > 0}> <Show when={props.flatFollows?.length > 0}>
<a href="?m=following" class={styles.subscribers}> <a href="?m=following" class={styles.followers}>
<For each={props.following.slice(0, 3)}> <For each={props.flatFollows.slice(0, 3)}>
{(f) => { {(f) => {
if ('name' in f) { if ('name' in f) {
return ( return (
<Userpic <Userpic size={'XS'} name={f.name} userpic={f.pic} class={styles.followersItem} />
size={'XS'}
name={f.name}
userpic={f.pic}
class={styles.subscribersItem}
/>
) )
} }
@ -163,7 +225,7 @@ export const AuthorCard = (props: Props) => {
size={'XS'} size={'XS'}
name={f.title} name={f.title}
userpic={f.pic} userpic={f.pic}
class={styles.subscribersItem} class={styles.followersItem}
/> />
) )
} }
@ -171,9 +233,9 @@ export const AuthorCard = (props: Props) => {
return null return null
}} }}
</For> </For>
<div class={styles.subscribersCounter}> <div class={styles.followsCounter}>
{t('SubscriptionWithCount', { {t('FollowsWithCount', {
count: props?.following.length ?? 0, count: props?.flatFollows.length ?? 0,
})} })}
</div> </div>
</a> </a>
@ -251,77 +313,12 @@ export const AuthorCard = (props: Props) => {
</ShowOnlyOnClient> </ShowOnlyOnClient>
<Show when={props.followers}> <Show when={props.followers}>
<Modal variant="medium" isResponsive={true} name="followers" maxHeight> <Modal variant="medium" isResponsive={true} name="followers" maxHeight>
<> <FollowersModalView />
<h2>{t('Followers')}</h2>
<div class={styles.listWrapper}>
<div class="row">
<div class="col-24">
<For each={props.followers}>
{(follower: Author) => <AuthorBadge author={follower} />}
</For>
</div>
</div>
</div>
</>
</Modal> </Modal>
</Show> </Show>
<Show when={props.following}> <Show when={props.flatFollows}>
<Modal variant="medium" isResponsive={true} name="following" maxHeight> <Modal variant="medium" isResponsive={true} name="following" maxHeight>
<> <FollowingModalView />
<h2>{t('Subscriptions')}</h2>
<ul class="view-switcher">
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'all',
})}
>
<button type="button" onClick={() => setFollowsFilter('all')}>
{t('All')}
</button>
<span class="view-switcher__counter">{props.following.length}</span>
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'authors',
})}
>
<button type="button" onClick={() => setFollowsFilter('authors')}>
{t('Authors')}
</button>
<span class="view-switcher__counter">
{props.following.filter((s) => 'name' in s).length}
</span>
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'topics',
})}
>
<button type="button" onClick={() => setFollowsFilter('topics')}>
{t('Topics')}
</button>
<span class="view-switcher__counter">
{props.following.filter((s) => 'title' in s).length}
</span>
</li>
</ul>
<br />
<div class={styles.listWrapper}>
<div class="row">
<div class="col-24">
<For each={authorSubs()}>
{(subscription) =>
isAuthor(subscription) ? (
<AuthorBadge author={subscription} subscriptionsMode={true} />
) : (
<TopicBadge topic={subscription} subscriptionsMode={true} />
)
}
</For>
</div>
</div>
</div>
</>
</Modal> </Modal>
</Show> </Show>
</div> </div>

View File

@ -63,18 +63,8 @@ export const PasswordField = (props: Props) => {
} }
} }
createEffect( createEffect(on(error, (er) => er && props.errorMessage?.(er), { defer: true }))
on( createEffect(() => setError(props.setError))
() => error(),
() => {
props.errorMessage?.(error())
},
{ defer: true },
),
)
createEffect(() => {
setError(props.setError)
})
return ( return (
<div class={clsx(styles.PassportField, props.class)}> <div class={clsx(styles.PassportField, props.class)}>

View File

@ -62,7 +62,7 @@ export const TableOfContents = (props: Props) => {
createEffect( createEffect(
on( on(
() => props.body, () => props.body,
() => debouncedUpdateHeadings(), (_) => debouncedUpdateHeadings(),
), ),
) )

View File

@ -40,65 +40,19 @@ const LOAD_MORE_PAGE_SIZE = 9
export const AuthorView = (props: Props) => { export const AuthorView = (props: Props) => {
const { t } = useLocalize() const { t } = useLocalize()
const { followers: myFollowers, follows: myFollows } = useFollowing() const { followers: myFollowers, follows: myFollows } = useFollowing()
const { session, author: me } = useSession() const { author: me } = useSession()
const { sortedArticles } = useArticlesStore({ shouts: props.shouts }) const { sortedArticles } = useArticlesStore({ shouts: props.shouts })
const { page: getPage, searchParams } = useRouter() const { page: getPage, searchParams } = useRouter()
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const [isBioExpanded, setIsBioExpanded] = createSignal(false) const [isBioExpanded, setIsBioExpanded] = createSignal(false)
const [author, setAuthor] = createSignal<Author>() const [author, setAuthor] = createSignal<Author>(props.author)
const [followers, setFollowers] = createSignal([]) const [followers, setFollowers] = createSignal([])
const [following, changeFollowing] = createSignal<Array<Author | Topic>>([]) // flat AuthorFollowsResult const [following, changeFollowing] = createSignal<Array<Author | Topic>>([]) // flat AuthorFollowsResult
const [showExpandBioControl, setShowExpandBioControl] = createSignal(false) const [showExpandBioControl, setShowExpandBioControl] = createSignal(false)
const [commented, setCommented] = createSignal<Reaction[]>() const [commented, setCommented] = createSignal<Reaction[]>()
const modal = MODALS[searchParams().m] const modal = MODALS[searchParams().m]
const [sessionChecked, setSessionChecked] = createSignal(false) // пагинация загрузки ленты постов
createEffect(
on(
[() => sessionChecked(), () => props.authorSlug, () => session()?.user?.app_data?.profile?.slug],
([checked, slug, mySlug]) => {
if (!checked && slug && mySlug === slug) {
setSessionChecked(true)
const appdata = session()?.user.app_data
if (appdata) {
console.info('preloaded my own profile')
setFollowers(myFollowers())
setAuthor(me())
const { authors, topics } = myFollows
changeFollowing([...authors, ...topics])
}
}
},
{ defer: true },
),
)
const bioContainerRef: { current: HTMLDivElement } = { current: null }
const bioWrapperRef: { current: HTMLDivElement } = { current: null }
const fetchData = async (slug: string) => {
try {
const [followsResult, followersResult, authorResult] = await Promise.all([
apiClient.getAuthorFollows({ slug }),
apiClient.getAuthorFollowers({ slug }),
loadAuthor({ slug }),
])
console.info('[components.Author] data loaded')
setAuthor(authorResult)
setFollowers(followersResult || [])
const { authors, topics } = followsResult
changeFollowing([...(authors || []), ...(topics || [])])
} catch (error) {
console.error('[components.Author] fetch error', error)
}
}
const checkBioHeight = () => {
if (bioContainerRef.current) {
setShowExpandBioControl(bioContainerRef.current.offsetHeight > bioWrapperRef.current.offsetHeight)
}
}
const loadMore = async () => { const loadMore = async () => {
saveScrollPosition() saveScrollPosition()
const { hasMore } = await loadShouts({ const { hasMore } = await loadShouts({
@ -110,36 +64,72 @@ export const AuthorView = (props: Props) => {
restoreScrollPosition() restoreScrollPosition()
} }
// загружает профиль и подписки
const [isFetching, setIsFetching] = createSignal(false)
const fetchData = async (slug) => {
setIsFetching(true)
const authorResult = await loadAuthor({ slug })
setAuthor(authorResult)
console.info(`[Author] profile for @${slug} fetched`)
const followsResult = await apiClient.getAuthorFollows({ slug })
const { authors, topics } = followsResult
changeFollowing([...(authors || []), ...(topics || [])])
console.info(`[Author] follows for @${slug} fetched`)
const followersResult = await apiClient.getAuthorFollowers({ slug })
setFollowers(followersResult || [])
console.info(`[Author] followers for @${slug} fetched`)
setIsFetching(false)
}
// проверяет не собственный ли это профиль, иначе - загружает
createEffect(
on([() => me(), () => props.authorSlug], ([myProfile, slug]) => {
const my = slug && myProfile?.slug === slug
if (my) {
console.debug('[Author] my profile precached')
myProfile && setAuthor(myProfile)
setFollowers(myFollowers() || [])
changeFollowing([...(myFollows?.authors || []), ...(myFollows?.topics || [])])
} else if (slug && !isFetching()) {
fetchData(slug)
}
}),
{ defer: true },
)
// догружает ленту и комментарии
createEffect(
on(author, async (profile) => {
if (!commented() && profile) {
await loadMore()
const ccc = await apiClient.getReactionsBy({
by: { comment: true, created_by: profile.id },
})
setCommented(ccc)
}
}),
)
const bioContainerRef: { current: HTMLDivElement } = { current: null }
const bioWrapperRef: { current: HTMLDivElement } = { current: null }
const checkBioHeight = () => {
if (bioContainerRef.current) {
setShowExpandBioControl(bioContainerRef.current.offsetHeight > bioWrapperRef.current.offsetHeight)
}
}
onMount(() => { onMount(() => {
if (!modal) hideModal() if (!modal) hideModal()
fetchData(props.authorSlug)
checkBioHeight() checkBioHeight()
loadMore()
}) })
const pages = createMemo<Shout[][]>(() => const pages = createMemo<Shout[][]>(() =>
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE), splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
) )
const fetchComments = async (commenter: Author) => {
const data = await apiClient.getReactionsBy({
by: { comment: true, created_by: commenter.id },
})
setCommented(data)
}
const authorSlug = createMemo(() => author()?.slug)
createEffect(
on(
() => authorSlug(),
() => {
fetchData(authorSlug())
fetchComments(author())
},
{ defer: true },
),
)
const ogImage = createMemo(() => const ogImage = createMemo(() =>
author()?.pic author()?.pic
? getImageUrl(author()?.pic, { width: 1200 }) ? getImageUrl(author()?.pic, { width: 1200 })
@ -168,7 +158,7 @@ export const AuthorView = (props: Props) => {
<Show when={author()} fallback={<Loading />}> <Show when={author()} fallback={<Loading />}>
<> <>
<div class={styles.authorHeader}> <div class={styles.authorHeader}>
<AuthorCard author={author()} followers={followers() || []} following={following() || []} /> <AuthorCard author={author()} followers={followers() || []} flatFollows={following() || []} />
</div> </div>
<div class={clsx(styles.groupControls, 'row')}> <div class={clsx(styles.groupControls, 'row')}>
<div class="col-md-16"> <div class="col-md-16">

View File

@ -143,16 +143,20 @@ export const FeedView = (props: Props) => {
Promise.all([loadTopComments()]).finally(() => setIsRightColumnLoaded(true)) Promise.all([loadTopComments()]).finally(() => setIsRightColumnLoaded(true))
}) })
createEffect(() => { createEffect(
if (session()?.access_token && !unratedArticles()) { on(
loadUnratedArticles() [() => session(), unratedArticles],
} ([s, seen]) => {
}) if (s?.access_token && !(seen?.length > 0)) loadUnratedArticles()
},
{ defer: true },
),
)
createEffect( createEffect(
on( on(
() => page().route + searchParams().by + searchParams().period + searchParams().visibility, [page, searchParams],
() => { (_, _p) => {
resetSortedArticles() resetSortedArticles()
loadMore() loadMore()
}, },

View File

@ -136,9 +136,7 @@ export const InboxView = (props: Props) => {
} }
createEffect( createEffect(
on( on(messages, () => {
() => messages(),
() => {
if (!messagesContainerRef.current) { if (!messagesContainerRef.current) {
return return
} }
@ -149,8 +147,7 @@ export const InboxView = (props: Props) => {
top: messagesContainerRef.current.scrollHeight, top: messagesContainerRef.current.scrollHeight,
behavior: 'smooth', behavior: 'smooth',
}) })
}, }),
),
{ defer: true }, { defer: true },
) )
const handleScrollMessageContainer = () => { const handleScrollMessageContainer = () => {

View File

@ -87,13 +87,7 @@ export const TopicView = (props: Props) => {
loadReactedTopMonthArticles(topic()?.slug) loadReactedTopMonthArticles(topic()?.slug)
} }
createEffect( createEffect(on(topic, loadRandom, { defer: true }))
on(
() => topic(),
() => loadRandom(),
{ defer: true },
),
)
const title = createMemo( const title = createMemo(
() => () =>

View File

@ -66,7 +66,7 @@ export const InviteMembers = (props: Props) => {
createEffect( createEffect(
on( on(
() => sortedAuthors(), sortedAuthors,
(currentAuthors) => { (currentAuthors) => {
setAuthorsToInvite(currentAuthors.map((author) => ({ ...author, selected: false }))) setAuthorsToInvite(currentAuthors.map((author) => ({ ...author, selected: false })))
}, },

View File

@ -129,8 +129,8 @@ export const Lightbox = (props: Props) => {
createEffect( createEffect(
on( on(
() => zoomLevel(), zoomLevel,
() => { (_) => {
clearTimeout(fadeTimer) clearTimeout(fadeTimer)
fadeTimer = setTimeout(() => { fadeTimer = setTimeout(() => {

View File

@ -61,7 +61,7 @@ export const EditorSwiper = (props: Props) => {
createEffect( createEffect(
on( on(
() => props.images.length, () => props.images.length,
() => { (_) => {
mainSwipeRef.current?.swiper.update() mainSwipeRef.current?.swiper.update()
thumbSwipeRef.current?.swiper.update() thumbSwipeRef.current?.swiper.update()
}, },

View File

@ -45,7 +45,7 @@ export const ImageSwiper = (props: Props) => {
createEffect( createEffect(
on( on(
() => props.images.length, () => props.images.length,
() => { (_) => {
mainSwipeRef.current?.swiper.update() mainSwipeRef.current?.swiper.update()
thumbSwipeRef.current?.swiper.update() thumbSwipeRef.current?.swiper.update()
}, },

View File

@ -94,21 +94,14 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
createEffect( createEffect(
on( on(
() => author(), () => session()?.user.app_data,
(a) => { (appdata) => {
if (a?.id) {
try {
const appdata = session()?.user.app_data
if (appdata) { if (appdata) {
const { authors, followers, topics } = appdata const { authors, followers, topics } = appdata
setFollows({ authors, topics }) setFollows({ authors, topics })
setFollowers(followers) setFollowers(followers)
if (!authors) fetchData() if (!authors) fetchData()
} }
} catch (e) {
console.error(e)
}
}
}, },
), ),
) )

View File

@ -96,15 +96,15 @@ export const SessionProvider = (props: {
// handle auth state callback // handle auth state callback
createEffect( createEffect(
on( on(
() => searchParams()?.state, searchParams,
(state) => { (params) => {
if (state) { if (params?.state) {
setOauthState((_s) => state) setOauthState((_s) => params?.state)
const scope = searchParams()?.scope const scope = params?.scope
? searchParams()?.scope?.toString().split(' ') ? params?.scope?.toString().split(' ')
: ['openid', 'profile', 'email'] : ['openid', 'profile', 'email']
if (scope) console.info(`[context.session] scope: ${scope}`) if (scope) console.info(`[context.session] scope: ${scope}`)
const url = searchParams()?.redirect_uri || searchParams()?.redirectURL || window.location.href const url = params?.redirect_uri || params?.redirectURL || window.location.href
setConfig((c: ConfigType) => ({ ...c, redirectURL: url.split('?')[0] })) setConfig((c: ConfigType) => ({ ...c, redirectURL: url.split('?')[0] }))
changeSearchParams({ mode: 'confirm-email', m: 'auth' }, true) changeSearchParams({ mode: 'confirm-email', m: 'auth' }, true)
} }

View File

@ -1,6 +1,6 @@
import type { PageProps } from './types' import type { PageProps } from './types'
import { Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js' import { Show, createEffect, createMemo, createSignal, on, onCleanup } from 'solid-js'
import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../components/Views/Author' import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../components/Views/Author'
import { Loading } from '../components/_shared/Loading' import { Loading } from '../components/_shared/Loading'
@ -20,38 +20,19 @@ export const AuthorPage = (props: PageProps) => {
Boolean(props.authorShouts) && Boolean(props.author) && props.author.slug === slug(), Boolean(props.authorShouts) && Boolean(props.author) && props.author.slug === slug(),
) )
const preload = () => {
return Promise.all([
loadShouts({
filters: { author: slug(), featured: false },
limit: PRERENDERED_ARTICLES_COUNT,
}),
loadAuthor({ slug: slug() }),
])
}
onMount(async () => {
if (isLoaded()) {
return
}
resetSortedArticles()
await preload()
setIsLoaded(true)
})
createEffect( createEffect(
on( on(slug, async (s) => {
() => slug(), if (s) {
async () => {
setIsLoaded(false) setIsLoaded(false)
resetSortedArticles() resetSortedArticles()
await preload() await loadShouts({
filters: { author: s, featured: false },
limit: PRERENDERED_ARTICLES_COUNT,
})
await loadAuthor({ slug: s })
setIsLoaded(true) setIsLoaded(true)
}, }
{ defer: true }, }),
),
) )
onCleanup(() => resetSortedArticles()) onCleanup(() => resetSortedArticles())

View File

@ -12,10 +12,9 @@ import { LayoutType } from '../types'
export const ExpoPage = (props: PageProps) => { export const ExpoPage = (props: PageProps) => {
const { t } = useLocalize() const { t } = useLocalize()
const { page } = useRouter() const { page } = useRouter()
const getLayout = createMemo<LayoutType>(() => page().params['layout'] as LayoutType) const layout = createMemo(() => page().params['layout'] as LayoutType)
const title = createMemo(() => {
const getTitle = () => { switch (layout()) {
switch (getLayout()) {
case 'audio': { case 'audio': {
return t('Audio') return t('Audio')
} }
@ -32,22 +31,14 @@ export const ExpoPage = (props: PageProps) => {
return t('Art') return t('Art')
} }
} }
} })
createEffect( createEffect(on(title, (t) => (document.title = t), { defer: true }))
on(
() => getLayout(),
() => {
document.title = getTitle()
},
{ defer: true },
),
)
return ( return (
<PageLayout withPadding={true} zeroBottomPadding={true} title={getTitle()}> <PageLayout withPadding={true} zeroBottomPadding={true} title={title()}>
<Topics /> <Topics />
<Expo shouts={props.expoShouts} layout={getLayout()} /> <Expo shouts={props.expoShouts} layout={layout()} />
</PageLayout> </PageLayout>
) )
} }

View File

@ -25,20 +25,9 @@ const handleMyFeedLoadShouts = (options: LoadShoutsOptions) => {
export const FeedPage = () => { export const FeedPage = () => {
const { t } = useLocalize() const { t } = useLocalize()
onCleanup(() => resetSortedArticles())
const { page } = useRouter() const { page } = useRouter()
createEffect(on(page, (_) => resetSortedArticles(), { defer: true }))
createEffect( onCleanup(() => resetSortedArticles())
on(
() => page().route,
() => {
resetSortedArticles()
},
{ defer: true },
),
)
return ( return (
<PageLayout title={t('Feed')}> <PageLayout title={t('Feed')}>

View File

@ -44,10 +44,10 @@ export const ProfileSecurityPage = () => {
createEffect( createEffect(
on( on(
() => session()?.user?.email, () => session()?.user?.email,
() => { (email) => {
setFormData((prevData) => ({ setFormData((prevData) => ({
...prevData, ...prevData,
['email']: session()?.user?.email, email,
})) }))
}, },
), ),

View File

@ -37,19 +37,17 @@ export const TopicPage = (props: PageProps) => {
}) })
createEffect( createEffect(
on( on(slug, async (s) => {
() => slug(), if (s) {
async () => {
setIsLoaded(false) setIsLoaded(false)
resetSortedArticles() resetSortedArticles()
await preload() await preload()
setIsLoaded(true) setIsLoaded(true)
}, }
{ defer: true }, }),
),
) )
onCleanup(() => resetSortedArticles()) onCleanup(resetSortedArticles)
const usePrerenderedData = props.topic?.slug === slug() const usePrerenderedData = props.topic?.slug === slug()

View File

@ -16,7 +16,7 @@ const cssModuleHMR = () => {
modules.forEach((module) => { modules.forEach((module) => {
if (module.id.includes('.scss') || module.id.includes('.css')) { if (module.id.includes('.scss') || module.id.includes('.css')) {
module.isSelfAccepting = true module.isSelfAccepting = true
module.accept() // module.accept()
} }
}) })
}, },