This commit is contained in:
Untone 2024-05-07 18:05:22 +03:00
parent f6043ad223
commit f992cf9377
22 changed files with 158 additions and 30 deletions

10
package-lock.json generated
View File

@ -36,7 +36,6 @@
"@solid-primitives/share": "2.0.4", "@solid-primitives/share": "2.0.4",
"@solid-primitives/storage": "^3.5.0", "@solid-primitives/storage": "^3.5.0",
"@solid-primitives/upload": "0.0.115", "@solid-primitives/upload": "0.0.115",
"@solidjs/meta": "0.29.3",
"@thisbeyond/solid-select": "0.14.0", "@thisbeyond/solid-select": "0.14.0",
"@tiptap/core": "2.2.3", "@tiptap/core": "2.2.3",
"@tiptap/extension-blockquote": "2.2.3", "@tiptap/extension-blockquote": "2.2.3",
@ -4106,15 +4105,6 @@
"solid-js": "^1.6.12" "solid-js": "^1.6.12"
} }
}, },
"node_modules/@solidjs/meta": {
"version": "0.29.3",
"resolved": "https://registry.npmjs.org/@solidjs/meta/-/meta-0.29.3.tgz",
"integrity": "sha512-R2uirgjgyh3FPFh+rb840plF701N6GvM5w81/QeI61QwjXb4QzLkyI/uzXfC5UW8favpUn9KK9ILQeoTl6pX0A==",
"dev": true,
"peerDependencies": {
"solid-js": ">=1.8.4"
}
},
"node_modules/@thisbeyond/solid-select": { "node_modules/@thisbeyond/solid-select": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz", "resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz",

View File

@ -54,7 +54,6 @@
"@solid-primitives/share": "2.0.4", "@solid-primitives/share": "2.0.4",
"@solid-primitives/storage": "^3.5.0", "@solid-primitives/storage": "^3.5.0",
"@solid-primitives/upload": "0.0.115", "@solid-primitives/upload": "0.0.115",
"@solidjs/meta": "0.29.3",
"@thisbeyond/solid-select": "0.14.0", "@thisbeyond/solid-select": "0.14.0",
"@tiptap/core": "2.2.3", "@tiptap/core": "2.2.3",
"@tiptap/extension-blockquote": "2.2.3", "@tiptap/extension-blockquote": "2.2.3",

View File

@ -1,6 +1,6 @@
import type { PageProps, RootSearchParams } from '../pages/types' import type { PageProps, RootSearchParams } from '../pages/types'
import { Meta, MetaProvider } from '@solidjs/meta' import { Meta, MetaProvider } from '../context/meta'
import { Component, createEffect, createMemo } from 'solid-js' import { Component, createEffect, createMemo } from 'solid-js'
import { Dynamic } from 'solid-js/web' import { Dynamic } from 'solid-js/web'

View File

@ -2,7 +2,7 @@ import type { Author, Shout, Topic } from '../../graphql/schema/core.gen'
import { getPagePath } from '@nanostores/router' import { getPagePath } from '@nanostores/router'
import { createPopper } from '@popperjs/core' import { createPopper } from '@popperjs/core'
import { Link, Meta } from '@solidjs/meta' import { Link, Meta } from '../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { install } from 'ga-gtag' import { install } from 'ga-gtag'
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js'

View File

@ -1,6 +1,6 @@
import type { Author } from '../../../graphql/schema/core.gen' import type { Author } from '../../../graphql/schema/core.gen'
import { Meta } from '@solidjs/meta' import { Meta } from '../../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createMemo, createSignal } from 'solid-js' import { For, Show, createMemo, createSignal } from 'solid-js'

View File

@ -1,6 +1,6 @@
import type { Topic } from '../../../graphql/schema/core.gen' import type { Topic } from '../../../graphql/schema/core.gen'
import { Meta } from '@solidjs/meta' import { Meta } from '../../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal } from 'solid-js'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'

View File

@ -1,7 +1,7 @@
import type { Author, Reaction, Shout, Topic } from '../../../graphql/schema/core.gen' import type { Author, Reaction, Shout, Topic } from '../../../graphql/schema/core.gen'
import { getPagePath } from '@nanostores/router' import { getPagePath } from '@nanostores/router'
import { Meta, Title } from '@solidjs/meta' import { Meta, Title } from '../../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on, onMount } from 'solid-js'

View File

@ -1,5 +1,5 @@
import { getPagePath } from '@nanostores/router' import { getPagePath } from '@nanostores/router'
import { Meta } from '@solidjs/meta' import { Meta } from '../../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js'

View File

@ -1,6 +1,6 @@
import { LoadShoutsOptions, Shout, Topic } from '../../graphql/schema/core.gen' import { LoadShoutsOptions, Shout, Topic } from '../../graphql/schema/core.gen'
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js'

View File

@ -1,6 +1,6 @@
import type { JSX } from 'solid-js' import type { JSX } from 'solid-js'
import { Link } from '@solidjs/meta' import { Link } from '../../../context/meta'
import { splitProps } from 'solid-js' import { splitProps } from 'solid-js'
import { getImageUrl } from '../../../utils/getImageUrl' import { getImageUrl } from '../../../utils/getImageUrl'

View File

@ -1,6 +1,6 @@
import type { JSX } from 'solid-js' import type { JSX } from 'solid-js'
import { Title } from '@solidjs/meta' import { Title } from '../../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Show, createEffect, createSignal } from 'solid-js' import { Show, createEffect, createSignal } from 'solid-js'

139
src/context/meta.tsx Normal file
View File

@ -0,0 +1,139 @@
import {
Component,
createContext,
createRenderEffect,
createSignal,
JSX,
onCleanup,
ParentComponent,
useContext,
} from 'solid-js'
import { isServer, spread } from 'solid-js/web'
export const MetaContext = createContext<MetaContextType>()
interface TagDescription {
tag: string
props: Record<string, string>
cleanup?: () => void
}
export interface MetaContextType {
addTag: (tag: TagDescription) => void
removeTag: (tag: TagDescription) => void
}
function initClientProvider() {
const tags = new Map<string, TagDescription>()
function addTag(tag: TagDescription) {
const key = getTagKey(tag)
tags.set(key, tag)
const el = document.createElement(tag.tag)
spread(el, tag.props)
document.head.appendChild(el)
tag.cleanup = () => {
document.head.removeChild(el)
tags.delete(key)
}
}
function removeTag(tag: TagDescription) {
const key = getTagKey(tag)
const existingTag = tags.get(key)
if (existingTag) {
if (existingTag.cleanup) existingTag.cleanup()
tags.delete(key)
}
}
return { addTag, removeTag }
}
function initServerProvider() {
const tags: TagDescription[] = []
function addTag(tagDesc: TagDescription) {
tags.push(tagDesc)
}
function removeTag(tag: TagDescription) {
const index = tags.findIndex((t) => getTagKey(t) === getTagKey(tag))
if (index !== -1) {
tags.splice(index, 1)
}
}
return { addTag, removeTag }
}
export const MetaProvider: ParentComponent = (props) => {
const actions = isServer ? initServerProvider() : initClientProvider()
const [tags, setTags] = createSignal<TagDescription[]>([])
const addTag = (tag: TagDescription) => {
actions.addTag(tag)
setTags([...tags(), tag])
}
const removeTag = (tag: TagDescription) => {
actions.removeTag(tag)
setTags(tags().filter((t) => getTagKey(t) !== getTagKey(tag)))
}
onCleanup(() => {
for (const tag of tags()) {
tag.cleanup?.()
}
})
return <MetaContext.Provider value={{ addTag, removeTag }}>{props.children}</MetaContext.Provider>
}
const getTagKey = (tag: TagDescription) => {
const props = Object.entries(tag.props)
.filter(([k]) => k !== 'children')
.sort()
return `${tag.tag}${JSON.stringify(props)}`
}
export function useHead(tagDesc: TagDescription) {
const c = useContext(MetaContext)
if (!c) throw new Error('<MetaProvider /> should be in the tree')
createRenderEffect(() => {
c.addTag(tagDesc)
return () => {
c.removeTag(tagDesc)
}
})
}
const MetaTag = (tag: string, props: Record<string, string>) => {
useHead({ tag, props })
return null
}
export const Title: Component<JSX.HTMLAttributes<HTMLTitleElement>> = (props) =>
MetaTag('title', props as Record<string, string>)
export const Style: Component<JSX.StyleHTMLAttributes<HTMLStyleElement>> = (props) =>
MetaTag('style', props as Record<string, string>)
export const Meta: Component<JSX.MetaHTMLAttributes<HTMLMetaElement>> = (props) =>
MetaTag('meta', props as Record<string, string>)
export const Link: Component<JSX.LinkHTMLAttributes<HTMLLinkElement>> = (props) =>
MetaTag('link', props as Record<string, string>)
export const Base: Component<JSX.BaseHTMLAttributes<HTMLBaseElement>> = (props) =>
MetaTag('base', props as Record<string, string>)
export const Stylesheet: Component<Omit<JSX.LinkHTMLAttributes<HTMLLinkElement>, 'rel'>> = (props) => (
<Link rel="stylesheet" {...props} />
)

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { Donate } from '../../components/Discours/Donate' import { Donate } from '../../components/Discours/Donate'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { Feedback } from '../../components/Discours/Feedback' import { Feedback } from '../../components/Discours/Feedback'
import { Modal } from '../../components/Nav/Modal' import { Modal } from '../../components/Nav/Modal'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,4 +1,4 @@
import { Meta } from '@solidjs/meta' import { Meta } from '../../context/meta'
import { StaticPage } from '../../components/Views/StaticPage' import { StaticPage } from '../../components/Views/StaticPage'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'

View File

@ -1,5 +1,5 @@
import { redirectPage } from '@nanostores/router' import { redirectPage } from '@nanostores/router'
import { Meta } from '@solidjs/meta' import { Meta } from '../context/meta'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { AuthGuard } from '../components/AuthGuard' import { AuthGuard } from '../components/AuthGuard'