diff --git a/src/components/Article/CommentsTree.tsx b/src/components/Article/CommentsTree.tsx index a6ac5449..5e4a9c88 100644 --- a/src/components/Article/CommentsTree.tsx +++ b/src/components/Article/CommentsTree.tsx @@ -10,7 +10,6 @@ import { useReactions } from '../../context/reactions' import { byCreated } from '../../utils/sortby' import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated' import { useLocalize } from '../../context/localize' -import Cookie from 'js-cookie' type CommentsOrder = 'createdAt' | 'rating' | 'newOnly' @@ -108,7 +107,7 @@ export const CommentsTree = (props: Props) => { return ( <>
-

+

{t('Comments')} {comments().length.toString() || ''} 0}>  +{newReactions().length} @@ -161,7 +160,7 @@ export const CommentsTree = (props: Props) => { + - + {props.article.stat?.commented ?? ''} +
{ )}
-
+
{ const { cover, layout, slug, authors, stat, body } = props.article - const { changeSearchParam } = useRouter() - const scrollToComments = () => { - openPage(router, 'article', { slug: slug }) - changeSearchParam('scrollTo', 'comments') - } - return (
{
-
{formattedDate()}
@@ -184,10 +172,10 @@ export const ArticleCard = (props: ArticleCardProps) => {

diff --git a/src/components/Nav/ProfilePopup.tsx b/src/components/Nav/ProfilePopup.tsx index 1d5bfd5b..b19ae5ff 100644 --- a/src/components/Nav/ProfilePopup.tsx +++ b/src/components/Nav/ProfilePopup.tsx @@ -2,10 +2,9 @@ import { useSession } from '../../context/session' import type { PopupProps } from '../_shared/Popup' import { Popup } from '../_shared/Popup' import styles from '../_shared/Popup/Popup.module.scss' -import { getPagePath, openPage } from '@nanostores/router' -import { router, useRouter } from '../../stores/router' +import { getPagePath } from '@nanostores/router' +import { router } from '../../stores/router' import { useLocalize } from '../../context/localize' -import type { AuthorPageSearchParams } from '../Views/Author' type ProfilePopupProps = Omit @@ -16,12 +15,6 @@ export const ProfilePopup = (props: ProfilePopupProps) => { } = useSession() const { t } = useLocalize() - const { changeSearchParam } = useRouter() - - const openAuthorComments = () => { - openPage(router, 'author', { slug: userSlug() }) - changeSearchParam('by', 'commented') - } return ( @@ -36,13 +29,7 @@ export const ProfilePopup = (props: ProfilePopupProps) => { {t('Subscriptions')}
  • - { - event.preventDefault() - openAuthorComments() - }} - > + {t('Comments')}
  • diff --git a/src/components/Views/Author.tsx b/src/components/Views/Author.tsx index 535c915a..b4e94de5 100644 --- a/src/components/Views/Author.tsx +++ b/src/components/Views/Author.tsx @@ -54,7 +54,11 @@ export const AuthorView = (props: AuthorProps) => { const { searchParams, changeSearchParam } = useRouter() - changeSearchParam('by', 'rating') + onMount(() => { + if (!searchParams().by) { + changeSearchParam('by', 'rating') + } + }) const loadMore = async () => { saveScrollPosition() diff --git a/src/components/Views/Search.tsx b/src/components/Views/Search.tsx index e2717b6a..215c1e13 100644 --- a/src/components/Views/Search.tsx +++ b/src/components/Views/Search.tsx @@ -26,7 +26,7 @@ export const SearchView = (props: Props) => { const [query, setQuery] = createSignal(props.query) const [offset, setOffset] = createSignal(0) - const { searchParams, handleClientRouteLinkClick } = useRouter() + const { searchParams } = useRouter() let searchEl: HTMLInputElement const handleQueryChange = (_ev) => { setQuery(searchEl.value) @@ -72,18 +72,14 @@ export const SearchView = (props: Props) => { selected: searchParams().by === 'relevance' }} > - - {t('By relevance')} - + {t('By relevance')}
  • - - {t('Top rated')} - + {t('Top rated')}
  • diff --git a/src/pages/article.page.tsx b/src/pages/article.page.tsx index 8378bf37..bece4351 100644 --- a/src/pages/article.page.tsx +++ b/src/pages/article.page.tsx @@ -7,6 +7,7 @@ import { useRouter } from '../stores/router' import { Loading } from '../components/_shared/Loading' import { ReactionsProvider } from '../context/reactions' import { FullArticle } from '../components/Article/FullArticle' +import { setPageLoadManagerPromise } from '../utils/pageLoadManager' export const ArticlePage = (props: PageProps) => { const shouts = props.article ? [props.article] : [] @@ -33,7 +34,9 @@ export const ArticlePage = (props: PageProps) => { const articleValue = articleEntities()[slug()] if (!articleValue || !articleValue.body) { - await loadShout(slug()) + const loadShoutPromise = loadShout(slug()) + setPageLoadManagerPromise(loadShoutPromise) + await loadShoutPromise } }) diff --git a/src/stores/router.ts b/src/stores/router.ts index ad7afc8d..de4f3128 100644 --- a/src/stores/router.ts +++ b/src/stores/router.ts @@ -2,6 +2,7 @@ import type { Accessor } from 'solid-js' import { createRouter, createSearchParams } from '@nanostores/router' import { isServer } from 'solid-js/web' import { useStore } from '@nanostores/solid' +import { getPageLoadManagerPromise } from '../utils/pageLoadManager' export const ROUTES = { home: '/', @@ -40,9 +41,8 @@ const routerStore = createRouter(ROUTES, { export const router = routerStore -const handleClientRouteLinkClick = (event) => { - const link = event.target.closest('a') - if ( +const checkOpenOnClient = (link: HTMLAnchorElement, event) => { + return ( link && event.button === 0 && link.target !== '_blank' && @@ -52,48 +52,84 @@ const handleClientRouteLinkClick = (event) => { !event.ctrlKey && !event.shiftKey && !event.altKey - ) { - const url = new URL(link.href) - if (url.origin === location.origin) { - event.preventDefault() + ) +} - if (url.pathname) { - routerStore.open(url.pathname) - } +const scrollToHash = (hash: string) => { + let selector = hash - if (url.search) { - const params = Object.fromEntries(new URLSearchParams(url.search)) - searchParamsStore.open(params) - } + if (/^#\d+/.test(selector)) { + // id="1" fix + // https://stackoverflow.com/questions/20306204/using-queryselector-with-ids-that-are-numbers + selector = `[id="${selector.replace('#', '')}"]` + } - if (url.hash) { - let selector = url.hash + const anchor = document.querySelector(selector) + const headerOffset = 80 // 100px for header + const elementPosition = anchor ? anchor.getBoundingClientRect().top : 0 + const newScrollTop = elementPosition + window.scrollY - headerOffset - if (/^#\d+/.test(selector)) { - // id="1" fix - // https://stackoverflow.com/questions/20306204/using-queryselector-with-ids-that-are-numbers - selector = `[id="${selector.replace('#', '')}"]` - } + window.scrollTo({ + top: newScrollTop, + behavior: 'smooth' + }) +} - const anchor = document.querySelector(selector) - const headerOffset = 80 // 100px for header - const elementPosition = anchor ? anchor.getBoundingClientRect().top : 0 - const newScrollTop = elementPosition + window.scrollY - headerOffset +const handleClientRouteLinkClick = async (event) => { + const link = event.target.closest('a') - window.scrollTo({ - top: newScrollTop, - behavior: 'smooth' - }) + if (!checkOpenOnClient(link, event)) { + return + } - return - } + const url = new URL(link.href) + if (url.origin !== location.origin) { + return + } - window.scrollTo({ - top: 0, - left: 0 - }) + event.preventDefault() + + if (url.pathname) { + routerStore.open(url.pathname) + } + + if (url.search) { + const params = Object.fromEntries(new URLSearchParams(url.search)) + searchParamsStore.open(params) + } + + if (!url.hash) { + window.scrollTo({ + top: 0, + left: 0 + }) + + return + } + + await getPageLoadManagerPromise() + + const images = document.querySelectorAll('img') + + let imagesLoaded = 0 + + const imageLoadEventHandler = () => { + imagesLoaded++ + if (imagesLoaded === images.length) { + scrollToHash(url.hash) + images.forEach((image) => image.removeEventListener('load', imageLoadEventHandler)) + images.forEach((image) => image.removeEventListener('error', imageLoadEventHandler)) } } + + images.forEach((image) => { + if (image.complete) { + imagesLoaded++ + } + + image.addEventListener('load', imageLoadEventHandler) + image.addEventListener('error', imageLoadEventHandler) + }) } export const initRouter = (pathname: string, search: Record) => { @@ -134,7 +170,6 @@ export const useRouter = = Record< return { page, searchParams, - changeSearchParam, - handleClientRouteLinkClick + changeSearchParam } } diff --git a/src/utils/pageLoadManager.ts b/src/utils/pageLoadManager.ts new file mode 100644 index 00000000..d04a3d3d --- /dev/null +++ b/src/utils/pageLoadManager.ts @@ -0,0 +1,11 @@ +const pageLoadManager: { + promise: Promise +} = { promise: Promise.resolve() } + +export const getPageLoadManagerPromise = () => { + return pageLoadManager.promise +} + +export const setPageLoadManagerPromise = (promise: Promise) => { + pageLoadManager.promise = promise +}