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 (
<>
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
+}
+
{t('Comments')} {comments().length.toString() || ''} 0}>
+{newReactions().length}
@@ -161,7 +160,7 @@ export const CommentsTree = (props: Props) => {
+
{t('To write a comment, you must')}{' '}
{t('sign up')}
diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx
index 628d2b3f..3257076d 100644
--- a/src/components/Article/FullArticle.tsx
+++ b/src/components/Article/FullArticle.tsx
@@ -66,19 +66,6 @@ export const FullArticle = (props: ArticleProps) => {
props.article.topics[0]
)
- onMount(() => {
- const windowHash = window.location.hash
- if (windowHash?.length > 0) {
- const comments = document.querySelector(windowHash)
- if (comments) {
- window.scrollTo({
- top: comments.getBoundingClientRect().top,
- behavior: 'smooth'
- })
- }
- }
- })
-
onMount(async () => {
await loadReactionsBy({
by: { shout: props.article.slug }
@@ -105,20 +92,6 @@ export const FullArticle = (props: ArticleProps) => {
actions: { loadReactionsBy }
} = useReactions()
- let commentsRef: HTMLDivElement | undefined
- const scrollToComments = () => {
- if (!isReactionsLoaded()) {
- return
- }
- commentsRef.scrollIntoView({ behavior: 'smooth' })
- }
- const { searchParams } = useRouter()
- createEffect(() => {
- if (searchParams()?.scrollTo === 'comments') {
- scrollToComments()
- }
- })
-
return (
<>
{props.article.title}
@@ -212,10 +185,10 @@ export const FullArticle = (props: ArticleProps) => {
- scrollToComments()}>
+
- {/*{props.article.stat?.commented || ''}*/}
-
+ {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) => {
-
+