commit
4b29bdad0f
|
@ -3,5 +3,6 @@ public
|
||||||
*.cjs
|
*.cjs
|
||||||
src/graphql/*.gen.ts
|
src/graphql/*.gen.ts
|
||||||
src/legacy_*
|
src/legacy_*
|
||||||
|
src/components/EditorExample
|
||||||
dist/
|
dist/
|
||||||
.vercel/
|
.vercel/
|
||||||
|
|
|
@ -74,6 +74,8 @@ module.exports = {
|
||||||
'unicorn/numeric-separators-style': 'off',
|
'unicorn/numeric-separators-style': 'off',
|
||||||
'unicorn/prefer-node-protocol': 'off',
|
'unicorn/prefer-node-protocol': 'off',
|
||||||
|
|
||||||
|
'promise/always-return': 'off',
|
||||||
|
|
||||||
eqeqeq: 'error',
|
eqeqeq: 'error',
|
||||||
'no-param-reassign': 'error',
|
'no-param-reassign': 'error',
|
||||||
'no-nested-ternary': 'error',
|
'no-nested-ternary': 'error',
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
|
[0.6.1]
|
||||||
|
[+] auth ver. 0.9
|
||||||
|
[+] load-by interfaces for shouts, authors and messages
|
||||||
|
[+] inbox logix and markup
|
||||||
|
[-] old views counting
|
||||||
|
|
||||||
[0.6.0]
|
[0.6.0]
|
||||||
[+] editor enabled
|
[+] hybrid routing ssr/spa
|
||||||
|
[+] 'expo' pages
|
||||||
|
[-] layout term usage with an exception
|
||||||
|
[-] less nanostores
|
||||||
|
[+] inbox
|
||||||
|
[+] css modules
|
||||||
|
[+] draft editor
|
||||||
|
[+] solid-driven storages
|
||||||
|
|
||||||
[0.5.1]
|
[0.5.1]
|
||||||
[+] nanostores-base global store
|
[+] nanostores-base global store
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { defineConfig, AstroUserConfig } from 'astro/config'
|
import { defineConfig, AstroUserConfig } from 'astro/config'
|
||||||
import vercel from '@astrojs/vercel/serverless'
|
import vercel from '@astrojs/vercel/serverless'
|
||||||
import solidJs from '@astrojs/solid-js'
|
import solidJs from '@astrojs/solid-js'
|
||||||
import type { CSSOptions } from 'vite'
|
import type { CSSOptions, PluginOption } from 'vite'
|
||||||
import defaultGenerateScopedName from 'postcss-modules/build/generateScopedName'
|
import defaultGenerateScopedName from 'postcss-modules/build/generateScopedName'
|
||||||
import { isDev } from './src/utils/config'
|
import { isDev } from './src/utils/config'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
@ -37,6 +37,7 @@ const astroConfig: AstroUserConfig = {
|
||||||
adapter: vercel(),
|
adapter: vercel(),
|
||||||
vite: {
|
vite: {
|
||||||
build: {
|
build: {
|
||||||
|
chunkSizeWarningLimit: 777,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
plugins: [visualizer()],
|
plugins: [visualizer()],
|
||||||
output: {
|
output: {
|
||||||
|
@ -67,7 +68,7 @@ const astroConfig: AstroUserConfig = {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
external: ['@aws-sdk/clients/s3']
|
external: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
css
|
css
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
overwrite: true
|
overwrite: true
|
||||||
schema: 'https://newapi.discours.io/graphql'
|
schema: 'https://testapi.discours.io/graphql'
|
||||||
generates:
|
generates:
|
||||||
src/graphql/introspec.gen.ts:
|
src/graphql/introspec.gen.ts:
|
||||||
plugins:
|
plugins:
|
||||||
|
|
100
package.json
100
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "discoursio-webapp",
|
"name": "discoursio-webapp",
|
||||||
"version": "0.5.1",
|
"version": "0.6.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -30,113 +30,113 @@
|
||||||
"vercel-build": "astro build"
|
"vercel-build": "astro build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mailgun.js": "^8.0.1"
|
"mailgun.js": "^8.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/solid-js": "^1.1.0",
|
"@astrojs/solid-js": "^1.2.3",
|
||||||
"@astrojs/vercel": "^2.1.0",
|
"@astrojs/vercel": "^2.3.3",
|
||||||
"@babel/core": "^7.18.13",
|
"@babel/core": "^7.20.2",
|
||||||
"@graphql-codegen/cli": "^2.12.1",
|
"@graphql-codegen/cli": "^2.13.12",
|
||||||
"@graphql-codegen/typescript": "^2.7.3",
|
"@graphql-codegen/typescript": "^2.8.2",
|
||||||
"@graphql-codegen/typescript-operations": "^2.5.3",
|
"@graphql-codegen/typescript-operations": "^2.5.7",
|
||||||
"@graphql-codegen/typescript-urql": "^3.7.0",
|
"@graphql-codegen/typescript-urql": "^3.7.3",
|
||||||
"@graphql-codegen/urql-introspection": "^2.2.1",
|
"@graphql-codegen/urql-introspection": "^2.2.1",
|
||||||
"@graphql-tools/url-loader": "^7.16.4",
|
"@graphql-tools/url-loader": "^7.16.16",
|
||||||
"@graphql-typed-document-node/core": "^3.1.1",
|
"@graphql-typed-document-node/core": "^3.1.1",
|
||||||
"@nanostores/persistent": "^0.7.0",
|
|
||||||
"@nanostores/router": "^0.7.0",
|
"@nanostores/router": "^0.7.0",
|
||||||
"@nanostores/solid": "^0.3.0",
|
"@nanostores/solid": "^0.3.0",
|
||||||
"@popperjs/core": "^2.11.6",
|
"@popperjs/core": "^2.11.6",
|
||||||
"@solid-devtools/debugger": "^0.13.1",
|
"@solid-devtools/debugger": "^0.14.0",
|
||||||
"@solid-devtools/logger": "^0.4.9",
|
"@solid-devtools/logger": "^0.5.0",
|
||||||
"@solid-primitives/memo": "^1.0.2",
|
"@solid-primitives/memo": "^1.1.2",
|
||||||
|
"@solid-primitives/storage": "^1.3.3",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
"@types/node": "^18.7.19",
|
"@types/node": "^18.11.9",
|
||||||
"@types/uuid": "^8.3.4",
|
"@types/uuid": "^8.3.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.29.0",
|
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||||
"@typescript-eslint/parser": "^5.35.1",
|
"@typescript-eslint/parser": "^5.43.0",
|
||||||
"@urql/core": "^3.0.1",
|
"@urql/core": "^3.0.5",
|
||||||
"@urql/devtools": "^2.0.3",
|
"@urql/devtools": "^2.0.3",
|
||||||
"@urql/exchange-auth": "^1.0.0",
|
"@urql/exchange-auth": "^1.0.0",
|
||||||
"@urql/exchange-graphcache": "^5.0.0",
|
"@urql/exchange-graphcache": "^5.0.5",
|
||||||
"astro": "^1.1.1",
|
"astro": "^1.6.8",
|
||||||
"astro-eslint-parser": "^0.9.0",
|
"astro-eslint-parser": "^0.9.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"bootstrap": "5.1.3",
|
"bootstrap": "5.1.3",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^1.2.1",
|
||||||
"cookie": "^0.5.0",
|
"cookie": "^0.5.0",
|
||||||
"cookie-signature": "^1.2.0",
|
"cookie-signature": "^1.2.0",
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.27.0",
|
||||||
"eslint-config-stylelint": "^17.0.0",
|
"eslint-config-stylelint": "^17.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.5.0",
|
"eslint-import-resolver-typescript": "^3.5.2",
|
||||||
"eslint-plugin-astro": "^0.21.0",
|
"eslint-plugin-astro": "^0.21.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||||
"eslint-plugin-promise": "^6.0.1",
|
"eslint-plugin-promise": "^6.1.1",
|
||||||
"eslint-plugin-solid": "^0.7.3",
|
"eslint-plugin-solid": "^0.8.0",
|
||||||
"eslint-plugin-sonarjs": "^0.16.0",
|
"eslint-plugin-sonarjs": "^0.16.0",
|
||||||
"eslint-plugin-unicorn": "^44.0.2",
|
"eslint-plugin-unicorn": "^44.0.2",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"graphql-tag": "^2.12.6",
|
"graphql-tag": "^2.12.6",
|
||||||
"graphql-ws": "^5.11.2",
|
"graphql-ws": "^5.11.2",
|
||||||
"hast-util-select": "^5.0.2",
|
"hast-util-select": "^5.0.2",
|
||||||
"husky": "^8.0.1",
|
"husky": "^8.0.2",
|
||||||
"idb": "^7.1.0",
|
"idb": "^7.1.1",
|
||||||
"jest": "^29.2.1",
|
"jest": "^29.3.1",
|
||||||
"lint-staged": "^13.0.3",
|
"lint-staged": "^13.0.3",
|
||||||
"loglevel": "^1.8.0",
|
"loglevel": "^1.8.1",
|
||||||
"loglevel-plugin-prefix": "^0.8.4",
|
"loglevel-plugin-prefix": "^0.8.4",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"markdown-it-container": "^3.0.0",
|
"markdown-it-container": "^3.0.0",
|
||||||
"markdown-it-implicit-figures": "^0.10.0",
|
"markdown-it-implicit-figures": "^0.10.0",
|
||||||
"markdown-it-mark": "^3.0.1",
|
"markdown-it-mark": "^3.0.1",
|
||||||
"markdown-it-replace-link": "^1.1.0",
|
"markdown-it-replace-link": "^1.1.0",
|
||||||
"nanostores": "^0.7.0",
|
"nanostores": "^0.7.1",
|
||||||
"orderedmap": "^2.1.0",
|
"orderedmap": "^2.1.0",
|
||||||
"postcss": "^8.4.16",
|
"postcss": "^8.4.19",
|
||||||
"postcss-modules": "^5.0.0",
|
"postcss-modules": "5.0.0",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"prettier-eslint": "^15.0.1",
|
"prettier-eslint": "^15.0.1",
|
||||||
"prosemirror-commands": "^1.3.1",
|
"prosemirror-commands": "^1.3.1",
|
||||||
"prosemirror-dropcursor": "^1.6.0",
|
"prosemirror-dropcursor": "^1.6.1",
|
||||||
"prosemirror-example-setup": "^1.2.1",
|
"prosemirror-example-setup": "^1.2.1",
|
||||||
"prosemirror-gapcursor": "^1.3.1",
|
"prosemirror-gapcursor": "^1.3.1",
|
||||||
"prosemirror-history": "^1.3.0",
|
"prosemirror-history": "^1.3.0",
|
||||||
"prosemirror-inputrules": "^1.2.0",
|
"prosemirror-inputrules": "^1.2.0",
|
||||||
"prosemirror-keymap": "^1.2.0",
|
"prosemirror-keymap": "^1.2.0",
|
||||||
"prosemirror-markdown": "^1.9.4",
|
"prosemirror-markdown": "^1.10.1",
|
||||||
"prosemirror-menu": "^1.2.1",
|
"prosemirror-menu": "^1.2.1",
|
||||||
"prosemirror-model": "^1.16.0",
|
"prosemirror-model": "^1.18.2",
|
||||||
"prosemirror-schema-list": "^1.2.2",
|
"prosemirror-schema-list": "^1.2.2",
|
||||||
"prosemirror-state": "^1.4.1",
|
"prosemirror-state": "^1.4.2",
|
||||||
"prosemirror-view": "^1.28.1",
|
"prosemirror-view": "^1.29.1",
|
||||||
"rollup": "~2.79.1",
|
"rollup": "^2.79.1",
|
||||||
"rollup-plugin-visualizer": "^5.8.2",
|
"rollup-plugin-visualizer": "^5.8.3",
|
||||||
"sass": "^1.55.0",
|
"sass": "^1.56.1",
|
||||||
"solid-devtools": "^0.20.1",
|
"solid-devtools": "^0.22.0",
|
||||||
"solid-js": "^1.6.0",
|
"solid-js": "^1.6.2",
|
||||||
"solid-js-form": "^0.1.5",
|
"solid-js-form": "^0.1.5",
|
||||||
"solid-jsx": "^0.9.1",
|
"solid-jsx": "^0.9.1",
|
||||||
"solid-social": "^0.9.0",
|
"solid-social": "^0.9.0",
|
||||||
"solid-utils": "^0.8.1",
|
"solid-utils": "^0.8.1",
|
||||||
"sort-package-json": "^2.0.0",
|
"sort-package-json": "^2.1.0",
|
||||||
"stylelint": "^14.12.1",
|
"stylelint": "^14.15.0",
|
||||||
"stylelint-config-css-modules": "^4.1.0",
|
"stylelint-config-css-modules": "^4.1.0",
|
||||||
"stylelint-config-prettier-scss": "^0.0.1",
|
"stylelint-config-prettier-scss": "^0.0.1",
|
||||||
"stylelint-config-standard-scss": "^6.0.0",
|
"stylelint-config-standard-scss": "^6.1.0",
|
||||||
"stylelint-order": "^5.0.0",
|
"stylelint-order": "^5.0.0",
|
||||||
"stylelint-scss": "^4.3.0",
|
"stylelint-scss": "^4.3.0",
|
||||||
"swiper": "^8.4.2",
|
"swiper": "^8.4.4",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.8.3",
|
"typescript": "^4.8.4",
|
||||||
"undici": "^5.10.0",
|
"undici": "^5.12.0",
|
||||||
"unique-names-generator": "^4.7.1",
|
"unique-names-generator": "^4.7.1",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"vite": "^3.1.3",
|
"vite": "^3.2.4",
|
||||||
"ws": "^8.9.0",
|
"ws": "^8.11.0",
|
||||||
"y-prosemirror": "^1.2.0",
|
"y-prosemirror": "^1.2.0",
|
||||||
"y-protocols": "^1.0.5",
|
"y-protocols": "^1.0.5",
|
||||||
"y-webrtc": "^10.2.3",
|
"y-webrtc": "^10.2.3",
|
||||||
"yjs": "^13.5.41"
|
"yjs": "^13.5.42"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10965
pnpm-lock.yaml
Normal file
10965
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
66
src/components/Article/AudioPlayer.tsx
Normal file
66
src/components/Article/AudioPlayer.tsx
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import { createEffect, createMemo, createSignal, onMount, For } from 'solid-js'
|
||||||
|
|
||||||
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
|
import { Soundwave } from './Soundwave'
|
||||||
|
|
||||||
|
type MediaItem = any
|
||||||
|
|
||||||
|
export default (props: { shout: Shout }) => {
|
||||||
|
const media = createMemo<any[]>(() => {
|
||||||
|
if (props.shout.media) {
|
||||||
|
console.debug(props.shout.media)
|
||||||
|
return [...JSON.parse(props.shout.media)]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
})
|
||||||
|
let audioRef: HTMLAudioElement
|
||||||
|
const [currentTrack, setCurrentTrack] = createSignal(media()[0])
|
||||||
|
const [paused, setPaused] = createSignal(true)
|
||||||
|
const togglePlayPause = () => setPaused(!paused())
|
||||||
|
const playMedia = (m: MediaItem) => {
|
||||||
|
audioRef.src = m.get('src')
|
||||||
|
audioRef.play()
|
||||||
|
}
|
||||||
|
const [audioContext, setAudioContext] = createSignal<AudioContext>()
|
||||||
|
onMount(() => setAudioContext(new AudioContext()))
|
||||||
|
createEffect(() => (paused() ? audioRef.play : audioRef.pause)())
|
||||||
|
return (
|
||||||
|
<div class="audio-container">
|
||||||
|
<div class="audio-img">
|
||||||
|
<img
|
||||||
|
class="ligthbox-img lazyload zoom-in"
|
||||||
|
width="320"
|
||||||
|
height="320"
|
||||||
|
alt={props.shout.title}
|
||||||
|
title={props.shout.title}
|
||||||
|
src={props.shout.cover}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="audio-player-list">
|
||||||
|
<div class="player current-track">
|
||||||
|
<div class="player-title">{currentTrack().title}</div>
|
||||||
|
<i class="fas fa-pause fa-3x fa-fw" onClick={togglePlayPause} />
|
||||||
|
<div class="player-progress">
|
||||||
|
<Soundwave context={audioContext()} url={currentTrack().src} />
|
||||||
|
<span class="track-position">{`${audioRef.currentTime} / ${audioRef.duration}`}</span>
|
||||||
|
</div>
|
||||||
|
<audio ref={audioRef} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="all-tracks">
|
||||||
|
<For each={media()}>
|
||||||
|
{(m: MediaItem) => (
|
||||||
|
<li>
|
||||||
|
<div class="player-status">
|
||||||
|
<i class="fas fa-play fa-fw" onClick={() => playMedia(m)} />
|
||||||
|
</div>
|
||||||
|
<span class="track-title">{m.title}</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import './Comment.scss'
|
import './Comment.scss'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { Show, createMemo } from 'solid-js'
|
import { Show, createMemo } from 'solid-js'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import { capitalize } from '../../utils'
|
import { capitalize } from '../../utils'
|
||||||
import './Full.scss'
|
import './Full.scss'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import ArticleComment from './Comment'
|
import ArticleComment from './Comment'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { createMemo, For, onMount, Show } from 'solid-js'
|
import { createMemo, For, onMount, Show } from 'solid-js'
|
||||||
import type { Author, Reaction, Shout } from '../../graphql/types.gen'
|
import type { Author, Reaction, Shout } from '../../graphql/types.gen'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { showModal } from '../../stores/ui'
|
import { showModal } from '../../stores/ui'
|
||||||
import { incrementView } from '../../stores/zine/articles'
|
|
||||||
import MD from './MD'
|
import MD from './MD'
|
||||||
import { SharePopup } from './SharePopup'
|
import { SharePopup } from './SharePopup'
|
||||||
import { useSession } from '../../context/session'
|
import { useSession } from '../../context/session'
|
||||||
|
@ -39,11 +38,6 @@ const formatDate = (date: Date) => {
|
||||||
|
|
||||||
export const FullArticle = (props: ArticleProps) => {
|
export const FullArticle = (props: ArticleProps) => {
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
incrementView({ articleSlug: props.article.slug })
|
|
||||||
})
|
|
||||||
|
|
||||||
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
||||||
|
|
||||||
const mainTopic = () =>
|
const mainTopic = () =>
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
export default (props: { src?: string; cover?: string; title?: string }) => {
|
|
||||||
// TODO: styling
|
|
||||||
return (
|
|
||||||
<div class="audio-track">
|
|
||||||
<audio src={props.src} controls={true} />
|
|
||||||
<span class="audio-title">{props.title || ''}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
import styles from '../_shared/Popup.module.scss'
|
import styles from '../_shared/Popup.module.scss'
|
||||||
|
|
109
src/components/Article/Soundwave.tsx
Normal file
109
src/components/Article/Soundwave.tsx
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import { onMount } from 'solid-js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility function for drawing our line segments
|
||||||
|
* @param {AudioContext} ctx the audio context
|
||||||
|
* @param {number} x the x coordinate of the beginning of the line segment
|
||||||
|
* @param {number} height the desired height of the line segment
|
||||||
|
* @param {number} width the desired width of the line segment
|
||||||
|
* @param {boolean} isEven whether or not the segmented is even-numbered
|
||||||
|
*/
|
||||||
|
const drawLineSegment = (ctx, x, height, width, isEven) => {
|
||||||
|
ctx.lineWidth = 1 // how thick the line is
|
||||||
|
ctx.strokeStyle = '#fff' // what color our line is
|
||||||
|
ctx.beginPath()
|
||||||
|
const h = isEven ? height : -height
|
||||||
|
ctx.moveTo(x, 0)
|
||||||
|
ctx.lineTo(x, h)
|
||||||
|
ctx.arc(x + width / 2, h, width / 2, Math.PI, 0, isEven)
|
||||||
|
ctx.lineTo(x + width, 0)
|
||||||
|
ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters the AudioBuffer retrieved from an external source
|
||||||
|
* @param {AudioBuffer} audioBuffer the AudioBuffer from drawAudio()
|
||||||
|
* @returns {Array} an array of floating point numbers
|
||||||
|
*/
|
||||||
|
const filterData = (audioBuffer) => {
|
||||||
|
const rawData = audioBuffer.getChannelData(0) // We only need to work with one channel of data
|
||||||
|
const samples = 70 // Number of samples we want to have in our final data set
|
||||||
|
const blockSize = Math.floor(rawData.length / samples) // the number of samples in each subdivision
|
||||||
|
const filteredData = []
|
||||||
|
for (let i = 0; i < samples; i++) {
|
||||||
|
const blockStart = blockSize * i // the location of the first sample in the block
|
||||||
|
let sum = 0
|
||||||
|
for (let j = 0; j < blockSize; j++) {
|
||||||
|
sum = sum + Math.abs(rawData[blockStart + j]) // find the sum of all the samples in the block
|
||||||
|
}
|
||||||
|
filteredData.push(sum / blockSize) // divide the sum by the block size to get the average
|
||||||
|
}
|
||||||
|
return filteredData
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes the audio data to make a cleaner illustration
|
||||||
|
* @param {Array} filteredData the data from filterData()
|
||||||
|
* @returns {Array} an normalized array of floating point numbers
|
||||||
|
*/
|
||||||
|
const normalizeData = (filteredData) => {
|
||||||
|
const multiplier = Math.pow(Math.max(...filteredData), -1)
|
||||||
|
return filteredData.map((n) => n * multiplier)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SoundwaveProps {
|
||||||
|
url: string
|
||||||
|
context: AudioContext
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Soundwave = (props: SoundwaveProps) => {
|
||||||
|
let canvasRef: HTMLCanvasElement
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the audio file into a canvas element.
|
||||||
|
* @param {Array} normalizedData The filtered array returned from filterData()
|
||||||
|
* @returns {Array} a normalized array of data
|
||||||
|
*/
|
||||||
|
const draw = (normalizedData) => {
|
||||||
|
// set up the canvas
|
||||||
|
const canvas = canvasRef
|
||||||
|
const dpr = window.devicePixelRatio || 1
|
||||||
|
const padding = 20
|
||||||
|
canvas.width = canvas.offsetWidth * dpr
|
||||||
|
canvas.height = (canvas.offsetHeight + padding * 2) * dpr
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
ctx.scale(dpr, dpr)
|
||||||
|
ctx.translate(0, canvas.offsetHeight / 2 + padding) // set Y = 0 to be in the middle of the canvas
|
||||||
|
|
||||||
|
// draw the line segments
|
||||||
|
const width = canvas.offsetWidth / normalizedData.length
|
||||||
|
// eslint-disable-next-line unicorn/no-for-loop
|
||||||
|
for (let i = 0; i < normalizedData.length; i++) {
|
||||||
|
const x = width * i
|
||||||
|
let height = normalizedData[i] * canvas.offsetHeight - padding
|
||||||
|
if (height < 0) {
|
||||||
|
height = 0
|
||||||
|
} else if (height > canvas.offsetHeight / 2) {
|
||||||
|
height = height - canvas.offsetHeight / 2
|
||||||
|
}
|
||||||
|
drawLineSegment(ctx, x, height, width, (i + 1) % 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves audio from an external source, the initializes the drawing function
|
||||||
|
* @param {AudioContext} audioContext the audio context
|
||||||
|
* @param {String} url the url of the audio we'd like to fetch
|
||||||
|
*/
|
||||||
|
const drawAudio = (audioContext, url) => {
|
||||||
|
fetch(url)
|
||||||
|
.then((response) => response.arrayBuffer())
|
||||||
|
.then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer))
|
||||||
|
.then((audioBuffer) => draw(normalizeData(filterData(audioBuffer))))
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
|
onMount(() => {
|
||||||
|
drawAudio(props.context, props.url)
|
||||||
|
})
|
||||||
|
return <canvas ref={canvasRef} />
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author } from '../../graphql/types.gen'
|
||||||
import Userpic from './Userpic'
|
import Userpic from './Userpic'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import styles from './Card.module.scss'
|
import styles from './Card.module.scss'
|
||||||
import { createMemo, For, Show } from 'solid-js'
|
import { createMemo, For, Show } from 'solid-js'
|
||||||
import { translit } from '../../utils/ru2en'
|
import { translit } from '../../utils/ru2en'
|
||||||
|
|
|
@ -20,7 +20,7 @@ export const Donate = () => {
|
||||||
const [period, setPeriod] = createSignal(monthly)
|
const [period, setPeriod] = createSignal(monthly)
|
||||||
const [amount, setAmount] = createSignal(0)
|
const [amount, setAmount] = createSignal(0)
|
||||||
|
|
||||||
onMount(() => {
|
const initiated = () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const {
|
const {
|
||||||
cp: { CloudPayments }
|
cp: { CloudPayments }
|
||||||
|
@ -51,6 +51,16 @@ export const Donate = () => {
|
||||||
provision: 0 // Сумма оплаты встречным предоставлением (сертификаты, др. мат.ценности) (2 знака после запятой)
|
provision: 0 // Сумма оплаты встречным предоставлением (сертификаты, др. мат.ценности) (2 знака после запятой)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.type = 'text/javascript'
|
||||||
|
script.src = 'https://widget.cloudpayments.ru/bundles/cloudpayments.js'
|
||||||
|
script.async = true
|
||||||
|
script.addEventListener('load', initiated)
|
||||||
|
document.head.appendChild(script)
|
||||||
})
|
})
|
||||||
|
|
||||||
const show = () => {
|
const show = () => {
|
||||||
|
@ -103,70 +113,68 @@ export const Donate = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<form class="discours-form donate-form" action="" method="post">
|
||||||
<form class="discours-form donate-form" action="" method="post">
|
<input type="hidden" name="shopId" value="156465" />
|
||||||
<input type="hidden" name="shopId" value="156465" />
|
<input value="148805" name="scid" type="hidden" />
|
||||||
<input value="148805" name="scid" type="hidden" />
|
<input value="0" name="customerNumber" type="hidden" />
|
||||||
<input value="0" name="customerNumber" type="hidden" />
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="donate-buttons-container" ref={amountSwitchElement}>
|
<div class="donate-buttons-container" ref={amountSwitchElement}>
|
||||||
<input type="radio" name="amount" id="fix250" value="250" />
|
<input type="radio" name="amount" id="fix250" value="250" />
|
||||||
<label for="fix250" class="btn donate-value-radio">
|
<label for="fix250" class="btn donate-value-radio">
|
||||||
250 ₽
|
250 ₽
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" name="amount" id="fix500" value="500" checked />
|
<input type="radio" name="amount" id="fix500" value="500" checked />
|
||||||
<label for="fix500" class="btn donate-value-radio">
|
<label for="fix500" class="btn donate-value-radio">
|
||||||
500 ₽
|
500 ₽
|
||||||
</label>
|
</label>
|
||||||
<input type="radio" name="amount" id="fix1000" value="1000" />
|
<input type="radio" name="amount" id="fix1000" value="1000" />
|
||||||
<label for="fix1000" class="btn donate-value-radio">
|
<label for="fix1000" class="btn donate-value-radio">
|
||||||
1000 ₽
|
1000 ₽
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
class="form-control donate-input"
|
class="form-control donate-input"
|
||||||
required
|
required
|
||||||
ref={customAmountElement}
|
ref={customAmountElement}
|
||||||
type="number"
|
type="number"
|
||||||
name="sum"
|
name="sum"
|
||||||
placeholder={t('Another amount')}
|
placeholder={t('Another amount')}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group" id="payment-type" classList={{ showing: showingPayment() }}>
|
<div class="form-group" id="payment-type" classList={{ showing: showingPayment() }}>
|
||||||
<div class="btn-group payment-choose" data-toggle="buttons">
|
<div class="btn-group payment-choose" data-toggle="buttons">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
id="once"
|
id="once"
|
||||||
name="once"
|
name="once"
|
||||||
onClick={() => setPeriod(once)}
|
onClick={() => setPeriod(once)}
|
||||||
checked={period() === once}
|
checked={period() === once}
|
||||||
/>
|
/>
|
||||||
<label for="once" class="btn payment-type" classList={{ active: period() === once }}>
|
<label for="once" class="btn payment-type" classList={{ active: period() === once }}>
|
||||||
{t('One time')}
|
{t('One time')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
id="monthly"
|
id="monthly"
|
||||||
name="monthly"
|
name="monthly"
|
||||||
onClick={() => setPeriod(monthly)}
|
onClick={() => setPeriod(monthly)}
|
||||||
checked={period() === monthly}
|
checked={period() === monthly}
|
||||||
/>
|
/>
|
||||||
<label for="monthly" class="btn payment-type" classList={{ active: period() === monthly }}>
|
<label for="monthly" class="btn payment-type" classList={{ active: period() === monthly }}>
|
||||||
{t('Every month')}
|
{t('Every month')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<a href={''} class="btn send-btn donate" onClick={show}>
|
<a href={''} class="btn send-btn donate" onClick={show}>
|
||||||
{t('Help discours to grow')}
|
{t('Help discours to grow')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createMemo, For } from 'solid-js'
|
import { createMemo, For } from 'solid-js'
|
||||||
import styles from './Footer.module.scss'
|
import styles from './Footer.module.scss'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import Subscribe from './Subscribe'
|
import Subscribe from './Subscribe'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { locale } from '../../stores/ui'
|
import { locale } from '../../stores/ui'
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { AuthorCard } from '../Author/Card'
|
||||||
import { TopicCard } from '../Topic/Card'
|
import { TopicCard } from '../Topic/Card'
|
||||||
import styles from './Beside.module.scss'
|
import styles from './Beside.module.scss'
|
||||||
import type { Author, Shout, Topic, User } from '../../graphql/types.gen'
|
import type { Author, Shout, Topic, User } from '../../graphql/types.gen'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { createMemo, For, Show } from 'solid-js'
|
||||||
import type { Shout } from '../../graphql/types.gen'
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
import { capitalize } from '../../utils'
|
import { capitalize } from '../../utils'
|
||||||
import { translit } from '../../utils/ru2en'
|
import { translit } from '../../utils/ru2en'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import styles from './Card.module.scss'
|
import styles from './Card.module.scss'
|
||||||
import { locale } from '../../stores/ui'
|
import { locale } from '../../stores/ui'
|
||||||
import { handleClientRouteLinkClick } from '../../stores/router'
|
import { handleClientRouteLinkClick } from '../../stores/router'
|
||||||
|
@ -98,7 +98,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
||||||
<div class={styles.shoutCardContent}>
|
<div class={styles.shoutCardContent}>
|
||||||
<Show when={layout && layout !== 'article' && !(props.settings?.noicon || props.settings?.noimage)}>
|
<Show when={layout && layout !== 'article' && !(props.settings?.noicon || props.settings?.noimage)}>
|
||||||
<div class={styles.shoutCardType}>
|
<div class={styles.shoutCardType}>
|
||||||
<a href={`/topic/${mainTopic.slug}`}>
|
<a href={`/expo/${layout}`}>
|
||||||
<Icon name={layout} class={styles.icon} />
|
<Icon name={layout} class={styles.icon} />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,10 +39,12 @@ export default (props: ArticleListProps) => {
|
||||||
setLoadingMore(false)
|
setLoadingMore(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const x: number = Math.floor(articles().length / 6)
|
||||||
|
// eslint-disable-next-line unicorn/new-for-builtins
|
||||||
|
const numbers: number[] = [...Array(x).keys()]
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<div class="article-preview">{t('Loading')}</div>}>
|
<Suspense fallback={<div class="article-preview">{t('Loading')}</div>}>
|
||||||
<For each={[...Array.from({ length: Math.floor(articles().length / 6) }).keys()]}>
|
<For each={numbers}>
|
||||||
{() => <Block6 articles={articles().slice(0, Math.min(6, articles().length))} />}
|
{() => <Block6 articles={articles().slice(0, Math.min(6, articles().length))} />}
|
||||||
</For>
|
</For>
|
||||||
<a href={''} onClick={handleMore} classList={{ disabled: loadingMore() }}>
|
<a href={''} onClick={handleMore} classList={{ disabled: loadingMore() }}>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { For } from 'solid-js'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author } from '../../graphql/types.gen'
|
||||||
import { useAuthorsStore } from '../../stores/zine/authors'
|
import { useAuthorsStore } from '../../stores/zine/authors'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { useTopicsStore } from '../../stores/zine/topics'
|
import { useTopicsStore } from '../../stores/zine/topics'
|
||||||
import { useArticlesStore } from '../../stores/zine/articles'
|
import { useArticlesStore } from '../../stores/zine/articles'
|
||||||
import { useSeenStore } from '../../stores/zine/seen'
|
import { useSeenStore } from '../../stores/zine/seen'
|
||||||
|
@ -13,7 +13,7 @@ type FeedSidebarProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeedSidebar = (props: FeedSidebarProps) => {
|
export const FeedSidebar = (props: FeedSidebarProps) => {
|
||||||
const { getSeen: seen } = useSeenStore()
|
const { seen } = useSeenStore()
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
const { authorEntities } = useAuthorsStore({ authors: props.authors })
|
const { authorEntities } = useAuthorsStore({ authors: props.authors })
|
||||||
const { articlesByTopic } = useArticlesStore()
|
const { articlesByTopic } = useArticlesStore()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'swiper/scss/pagination'
|
||||||
import './Slider.scss'
|
import './Slider.scss'
|
||||||
import type { Shout } from '../../graphql/types.gen'
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
import { createEffect, createMemo, createSignal, Show, For } from 'solid-js'
|
import { createEffect, createMemo, createSignal, Show, For } from 'solid-js'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
|
|
||||||
interface SliderProps {
|
interface SliderProps {
|
||||||
title?: string
|
title?: string
|
||||||
|
|
35
src/components/Inbox/DialogAvatar.module.scss
Normal file
35
src/components/Inbox/DialogAvatar.module.scss
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
.DialogAvatar {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.online::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
background: #2bb452;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
top: -2px;
|
||||||
|
right: -2px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
> img,
|
||||||
|
> .letter {
|
||||||
|
display: block;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .letter {
|
||||||
|
margin-bottom: -2px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 10px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
48
src/components/Inbox/DialogAvatar.tsx
Normal file
48
src/components/Inbox/DialogAvatar.tsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { Show, createMemo } from 'solid-js'
|
||||||
|
import './DialogCard.module.scss'
|
||||||
|
import styles from './DialogAvatar.module.scss'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
url: string
|
||||||
|
name: string
|
||||||
|
online?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
'#001219',
|
||||||
|
'#005f73',
|
||||||
|
'#0a9396',
|
||||||
|
'#94d2bd',
|
||||||
|
'#ee9b00',
|
||||||
|
'#ca6702',
|
||||||
|
'#ae2012',
|
||||||
|
'#9b2226',
|
||||||
|
'#668cff',
|
||||||
|
'#c34cfe',
|
||||||
|
'#e699ff',
|
||||||
|
'#6633ff'
|
||||||
|
]
|
||||||
|
|
||||||
|
const getById = (letter: string) =>
|
||||||
|
colors[Math.abs(Number(BigInt(letter.toLowerCase().codePointAt(0) - 97) % BigInt(colors.length)))]
|
||||||
|
|
||||||
|
const DialogAvatar = (props: Props) => {
|
||||||
|
const nameFirstLetter = props.name.slice(0, 1)
|
||||||
|
const randomBg = createMemo(() => {
|
||||||
|
return getById(nameFirstLetter)
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={clsx(styles.DialogAvatar, props.online && styles.online)}
|
||||||
|
style={{ 'background-color': `${randomBg()}` }}
|
||||||
|
>
|
||||||
|
<Show when={props.url} fallback={() => <div class={styles.letter}>{nameFirstLetter}</div>}>
|
||||||
|
<img src={props.url} alt={props.name} />
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DialogAvatar
|
70
src/components/Inbox/DialogCard.module.scss
Normal file
70
src/components/Inbox/DialogCard.module.scss
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
.DialogCard {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 12px;
|
||||||
|
transition: background 0.3s ease-in-out;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
flex-basis: 40px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
flex-basis: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
|
.name,
|
||||||
|
.message {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: #141414;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
color: #9fa1a7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
|
||||||
|
.time {
|
||||||
|
text-align: right;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter {
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0 8px;
|
||||||
|
background: #d00820;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #fff;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 6px;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/components/Inbox/DialogCard.tsx
Normal file
56
src/components/Inbox/DialogCard.tsx
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import styles from './DialogCard.module.scss'
|
||||||
|
import DialogAvatar from './DialogAvatar'
|
||||||
|
import type { Author, AuthResult } from '../../graphql/types.gen'
|
||||||
|
import { useSession } from '../../context/session'
|
||||||
|
import { createMemo } from 'solid-js'
|
||||||
|
import { apiClient } from '../../utils/apiClient'
|
||||||
|
|
||||||
|
type DialogProps = {
|
||||||
|
online?: boolean
|
||||||
|
message?: string
|
||||||
|
counter?: number
|
||||||
|
author?: Author
|
||||||
|
}
|
||||||
|
|
||||||
|
const createChat = async ({ title, members }: { title?: string; members?: string[] }): Promise<void> => {
|
||||||
|
await apiClient.createChat({ title, members })
|
||||||
|
}
|
||||||
|
|
||||||
|
const DialogCard = (props: DialogProps) => {
|
||||||
|
const { session } = useSession()
|
||||||
|
const currentSession = createMemo<AuthResult>(() => session)
|
||||||
|
|
||||||
|
const handleOpenChat = async () => {
|
||||||
|
try {
|
||||||
|
const initChat = await apiClient.createChat({
|
||||||
|
title: 'test chat',
|
||||||
|
members: [props.author.slug, currentSession().user.slug]
|
||||||
|
})
|
||||||
|
// console.log('!!! test:', test)
|
||||||
|
} catch (error) {
|
||||||
|
console.log('!!! errr:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={styles.DialogCard} onClick={handleOpenChat}>
|
||||||
|
<div class={styles.avatar}>
|
||||||
|
<DialogAvatar name={props.author.name} url={props.author.userpic} online={props.online} />
|
||||||
|
</div>
|
||||||
|
<div class={styles.row}>
|
||||||
|
<div class={styles.name}>{props.author.name}</div>
|
||||||
|
<div class={styles.message}>
|
||||||
|
Указать предпочтительные языки для результатов поиска можно в разделе
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class={styles.activity}>
|
||||||
|
<div class={styles.time}>22:22</div>
|
||||||
|
<div class={styles.counter}>
|
||||||
|
<span>12</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DialogCard
|
7
src/components/Inbox/Message.module.scss
Normal file
7
src/components/Inbox/Message.module.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.Message {
|
||||||
|
.own {
|
||||||
|
}
|
||||||
|
.body {
|
||||||
|
// message text
|
||||||
|
}
|
||||||
|
}
|
17
src/components/Inbox/Message.tsx
Normal file
17
src/components/Inbox/Message.tsx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './Message.module.scss'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
body: string
|
||||||
|
isOwn: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Message = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.Message, props.isOwn && styles.own)}>
|
||||||
|
<div class={styles.body}>{props.body}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Message
|
46
src/components/Inbox/Search.module.scss
Normal file
46
src/components/Inbox/Search.module.scss
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
.Search {
|
||||||
|
.field {
|
||||||
|
position: relative;
|
||||||
|
background: #fff;
|
||||||
|
border: 2px solid #e8e8e8;
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
height: 40px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 10px 36px 10px 12px;
|
||||||
|
width: 100%;
|
||||||
|
font-family: Muller, Arial, Helvetica, sans-serif;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 15px;
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #858585;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
& + .icon {
|
||||||
|
opacity: 0;
|
||||||
|
right: -30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
transition: 0.3s ease-in-out;
|
||||||
|
position: absolute;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/components/Inbox/Search.tsx
Normal file
27
src/components/Inbox/Search.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import styles from './Search.module.scss'
|
||||||
|
import { createSignal } from 'solid-js'
|
||||||
|
import { Icon } from '../_shared/Icon'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
placeholder: string
|
||||||
|
onChange: (value: () => string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const Search = (props: Props) => {
|
||||||
|
const [value, setValue] = createSignal<string>('')
|
||||||
|
const search = (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
setValue(event.target.value)
|
||||||
|
props.onChange(value)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div class={styles.Search}>
|
||||||
|
<div class={styles.field}>
|
||||||
|
<input type="text" onInput={search} placeholder={props.placeholder} />
|
||||||
|
<Icon name="search" class={styles.icon} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Search
|
|
@ -1,5 +1,5 @@
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
import { Icon } from '../Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
import { hideModal } from '../../../stores/ui'
|
import { hideModal } from '../../../stores/ui'
|
||||||
|
|
||||||
import styles from './SocialProviders.module.scss'
|
import styles from './SocialProviders.module.scss'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { For, Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js'
|
import { For, Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js'
|
||||||
import { Icon } from './Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { Modal } from './Modal'
|
import { Modal } from './Modal'
|
||||||
import { AuthModal } from './AuthModal'
|
import { AuthModal } from './AuthModal'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
|
|
@ -2,8 +2,8 @@ import styles from './Header.module.scss'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { Icon } from './Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { createSignal, onMount, Show } from 'solid-js'
|
import { createSignal, Show } from 'solid-js'
|
||||||
import Notifications from './Notifications'
|
import Notifications from './Notifications'
|
||||||
import { ProfilePopup } from './ProfilePopup'
|
import { ProfilePopup } from './ProfilePopup'
|
||||||
import Userpic from '../Author/Userpic'
|
import Userpic from '../Author/Userpic'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { For, Show } from 'solid-js'
|
import { For, Show } from 'solid-js'
|
||||||
import type { Topic } from '../../graphql/types.gen'
|
import type { Topic } from '../../graphql/types.gen'
|
||||||
import { Icon } from './Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import './Topics.scss'
|
import './Topics.scss'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { locale } from '../../stores/ui'
|
import { locale } from '../../stores/ui'
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { AllAuthorsView } from '../Views/AllAuthors'
|
import { AllAuthorsView } from '../Views/AllAuthors'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { createSignal, onMount, Show } from 'solid-js'
|
import { createSignal, onMount, Show } from 'solid-js'
|
||||||
|
@ -18,11 +18,11 @@ export const AllAuthorsPage = (props: PageProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Show when={isLoaded()} fallback={<Loading />}>
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
<AllAuthorsView authors={props.allAuthors} />
|
<AllAuthorsView authors={props.allAuthors} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { AllTopicsView } from '../Views/AllTopics'
|
import { AllTopicsView } from '../Views/AllTopics'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { createSignal, onMount, Show } from 'solid-js'
|
import { createSignal, onMount, Show } from 'solid-js'
|
||||||
|
@ -18,11 +18,11 @@ export const AllTopicsPage = (props: PageProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Show when={isLoaded()} fallback={<Loading />}>
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
<AllTopicsView topics={props.allTopics} />
|
<AllTopicsView topics={props.allTopics} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { ArticleView } from '../Views/Article'
|
import { ArticleView } from '../Views/Article'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { loadArticle, useArticlesStore } from '../../stores/zine/articles'
|
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles'
|
||||||
import { createMemo, onMount, Show } from 'solid-js'
|
import { createMemo, onMount, Show } from 'solid-js'
|
||||||
import type { Shout } from '../../graphql/types.gen'
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
import { useRouter } from '../../stores/router'
|
import { useRouter } from '../../stores/router'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
|
|
||||||
export const ArticlePage = (props: PageProps) => {
|
export const ArticlePage = (props: PageProps) => {
|
||||||
const sortedArticles = props.article ? [props.article] : []
|
const shouts = props.article ? [props.article] : []
|
||||||
|
|
||||||
const slug = createMemo(() => {
|
const slug = createMemo(() => {
|
||||||
const { page: getPage } = useRouter()
|
const { page: getPage } = useRouter()
|
||||||
|
@ -23,25 +23,25 @@ export const ArticlePage = (props: PageProps) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const { articleEntities } = useArticlesStore({
|
const { articleEntities } = useArticlesStore({
|
||||||
sortedArticles
|
shouts
|
||||||
})
|
})
|
||||||
|
|
||||||
const article = createMemo<Shout>(() => articleEntities()[slug()])
|
const article = createMemo<Shout>(() => articleEntities()[slug()])
|
||||||
|
|
||||||
onMount(() => {
|
onMount(async () => {
|
||||||
const articleValue = articleEntities()[slug()]
|
const articleValue = articleEntities()[slug()]
|
||||||
|
|
||||||
if (!articleValue || !articleValue.body) {
|
if (!articleValue || !articleValue.body) {
|
||||||
loadArticle({ slug: slug() })
|
await loadShoutsBy({ by: { slug: slug() }, limit: 1, offset: 0 })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout headerTitle={article()?.title || ''}>
|
<PageWrap headerTitle={article()?.title || ''}>
|
||||||
<Show when={Boolean(article())} fallback={<Loading />}>
|
<Show when={Boolean(article())} fallback={<Loading />}>
|
||||||
<ArticleView article={article()} />
|
<ArticleView article={article()} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../Views/Author'
|
import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../Views/Author'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { loadAuthorArticles, resetSortedArticles } from '../../stores/zine/articles'
|
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
import { useRouter } from '../../stores/router'
|
import { useRouter } from '../../stores/router'
|
||||||
import { loadAuthor } from '../../stores/zine/authors'
|
import { loadAuthor } from '../../stores/zine/authors'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
|
|
||||||
export const AuthorPage = (props: PageProps) => {
|
export const AuthorPage = (props: PageProps) => {
|
||||||
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.authorArticles) && Boolean(props.author))
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts) && Boolean(props.author))
|
||||||
|
|
||||||
const slug = createMemo(() => {
|
const slug = createMemo(() => {
|
||||||
const { page: getPage } = useRouter()
|
const { page: getPage } = useRouter()
|
||||||
|
@ -27,7 +27,7 @@ export const AuthorPage = (props: PageProps) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadAuthorArticles({ authorSlug: slug(), limit: PRERENDERED_ARTICLES_COUNT })
|
await loadShoutsBy({ by: { author: slug() }, limit: PRERENDERED_ARTICLES_COUNT })
|
||||||
await loadAuthor({ slug: slug() })
|
await loadAuthor({ slug: slug() })
|
||||||
|
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
|
@ -36,11 +36,11 @@ export const AuthorPage = (props: PageProps) => {
|
||||||
onCleanup(() => resetSortedArticles())
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Show when={isLoaded()} fallback={<Loading />}>
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
<AuthorView author={props.author} authorArticles={props.authorArticles} authorSlug={slug()} />
|
<AuthorView author={props.author} shouts={props.shouts} authorSlug={slug()} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
|
|
||||||
export const ConnectPage = () => {
|
export const ConnectPage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<article class="container container--static-page">
|
<article class="container container--static-page">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-10 col-md-8 col-lg-7 col-xl-6 shift-content">
|
<div class="col-sm-10 col-md-8 col-lg-7 col-xl-6 shift-content">
|
||||||
|
@ -39,7 +39,7 @@ export const ConnectPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { lazy, Suspense } from 'solid-js'
|
import { lazy, Suspense } from 'solid-js'
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
|
|
||||||
const CreateView = lazy(() => import('../Views/Create'))
|
const CreateView = lazy(() => import('../Views/Create'))
|
||||||
|
|
||||||
export const CreatePage = () => {
|
export const CreatePage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
<CreateView />
|
<CreateView />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { FeedView } from '../Views/Feed'
|
import { FeedView } from '../Views/Feed'
|
||||||
import { onCleanup } from 'solid-js'
|
import { onCleanup } from 'solid-js'
|
||||||
import { resetSortedArticles } from '../../stores/zine/articles'
|
import { resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
@ -7,9 +7,9 @@ export const FeedPage = () => {
|
||||||
onCleanup(() => resetSortedArticles())
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<FeedView />
|
<FeedView />
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { FourOuFourView } from '../Views/FourOuFour'
|
import { FourOuFourView } from '../Views/FourOuFour'
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
|
|
||||||
export const FourOuFourPage = () => {
|
export const FourOuFourPage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout isHeaderFixed={false} hideFooter={true}>
|
<PageWrap isHeaderFixed={false} hideFooter={true}>
|
||||||
<FourOuFourView />
|
<FourOuFourView />
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import { HomeView, PRERENDERED_ARTICLES_COUNT } from '../Views/Home'
|
import { HomeView, PRERENDERED_ARTICLES_COUNT } from '../Views/Home'
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
|
import { createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { loadPublishedArticles, resetSortedArticles } from '../../stores/zine/articles'
|
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
import { loadRandomTopics } from '../../stores/zine/topics'
|
import { loadRandomTopics } from '../../stores/zine/topics'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
import styles from './HomePage.module.scss'
|
import styles from './HomePage.module.scss'
|
||||||
|
|
||||||
export const HomePage = (props: PageProps) => {
|
export const HomePage = (props: PageProps) => {
|
||||||
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.homeArticles) && Boolean(props.randomTopics))
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts) && Boolean(props.randomTopics))
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadPublishedArticles({ limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
|
await loadShoutsBy({ by: { visibility: 'public' }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
|
||||||
await loadRandomTopics()
|
await loadRandomTopics()
|
||||||
|
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
|
@ -24,11 +24,11 @@ export const HomePage = (props: PageProps) => {
|
||||||
onCleanup(() => resetSortedArticles())
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout class={styles.mainContent}>
|
<PageWrap class={styles.mainContent}>
|
||||||
<Show when={isLoaded()} fallback={<Loading />}>
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
<HomeView randomTopics={props.randomTopics} recentPublishedArticles={props.homeArticles || []} />
|
<HomeView randomTopics={props.randomTopics} shouts={props.shouts || []} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/components/Pages/InboxPage.tsx
Normal file
14
src/components/Pages/InboxPage.tsx
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
|
import { InboxView } from '../Views/Inbox'
|
||||||
|
import type { PageProps } from '../types'
|
||||||
|
|
||||||
|
export const InboxPage = (props: PageProps) => {
|
||||||
|
return (
|
||||||
|
<PageWrap>
|
||||||
|
<InboxView />
|
||||||
|
</PageWrap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for lazy loading
|
||||||
|
export default InboxPage
|
148
src/components/Pages/LayoutShoutsPage.tsx
Normal file
148
src/components/Pages/LayoutShoutsPage.tsx
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
|
import type { PageProps } from '../types'
|
||||||
|
import { createMemo, createSignal, For, onCleanup, onMount, Show } from 'solid-js'
|
||||||
|
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
|
import { useRouter } from '../../stores/router'
|
||||||
|
import { LayoutType, useLayoutsStore } from '../../stores/zine/layouts'
|
||||||
|
import { Loading } from '../Loading'
|
||||||
|
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
||||||
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
|
import { splitToPages } from '../../utils/splitToPages'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import { t } from '../../utils/intl'
|
||||||
|
import { Row3 } from '../Feed/Row3'
|
||||||
|
import { Row2 } from '../Feed/Row2'
|
||||||
|
import { Beside } from '../Feed/Beside'
|
||||||
|
import Slider from '../Feed/Slider'
|
||||||
|
import { Row1 } from '../Feed/Row1'
|
||||||
|
import styles from '../../styles/Topic.module.scss'
|
||||||
|
|
||||||
|
export const PRERENDERED_ARTICLES_COUNT = 21
|
||||||
|
const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
|
||||||
|
|
||||||
|
export const LayoutShoutsPage = (props: PageProps) => {
|
||||||
|
const layout = createMemo<LayoutType>(() => {
|
||||||
|
const { page: getPage } = useRouter()
|
||||||
|
const page = getPage()
|
||||||
|
if (page.route !== 'expo') throw new Error('ts guard')
|
||||||
|
return page.params.layout as LayoutType
|
||||||
|
})
|
||||||
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
|
const { sortedLayoutShouts, loadLayoutShoutsBy } = useLayoutsStore(layout(), props.shouts)
|
||||||
|
const sortedArticles = createMemo<Shout[]>(() => sortedLayoutShouts().get(layout()) || [])
|
||||||
|
const loadMoreLayout = async (kind: LayoutType) => {
|
||||||
|
saveScrollPosition()
|
||||||
|
const { hasMore } = await loadLayoutShoutsBy({
|
||||||
|
by: { layout: kind },
|
||||||
|
limit: LOAD_MORE_PAGE_SIZE,
|
||||||
|
offset: sortedArticles().length
|
||||||
|
})
|
||||||
|
setIsLoadMoreButtonVisible(hasMore)
|
||||||
|
restoreScrollPosition()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) {
|
||||||
|
loadMoreLayout(layout())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const title = createMemo(() => {
|
||||||
|
const l = layout()
|
||||||
|
if (l === 'audio') return t('Audio')
|
||||||
|
if (l === 'video') return t('Video')
|
||||||
|
if (l === 'image') return t('Artworks')
|
||||||
|
return t('Literature')
|
||||||
|
})
|
||||||
|
|
||||||
|
const pages = createMemo<Shout[][]>(() =>
|
||||||
|
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
||||||
|
)
|
||||||
|
const isLoaded = createMemo(() => Boolean(sortedArticles()))
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
if (!isLoaded()) {
|
||||||
|
await loadShoutsBy({ by: { layout: layout() }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
|
const ModeSwitcher = () => (
|
||||||
|
<div class="container">
|
||||||
|
<div class={clsx(styles.groupControls, 'row group__controls')}>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<ul class="view-switcher">
|
||||||
|
<li classList={{ selected: layout() === 'audio' }}>
|
||||||
|
<a href="/expo/audio">{t('Audio')}</a>
|
||||||
|
</li>
|
||||||
|
<li classList={{ selected: layout() === 'video' }}>
|
||||||
|
<a href="/expo/video">{t('Video')}</a>
|
||||||
|
</li>
|
||||||
|
<li classList={{ selected: layout() === 'image' }}>
|
||||||
|
<a href="/expo/image">{t('Artworks')}</a>
|
||||||
|
</li>
|
||||||
|
<li classList={{ selected: layout() === 'literature' }}>
|
||||||
|
<a href="/expo/literature">{t('Literature')}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="mode-switcher">
|
||||||
|
{`${t('Show')} `}
|
||||||
|
<span class="mode-switcher__control">{t('All posts')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
<PageWrap>
|
||||||
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
|
<div class={styles.topicPage}>
|
||||||
|
<Show when={layout() && Boolean(sortedArticles())}>
|
||||||
|
<h1>{title()}</h1>
|
||||||
|
<ModeSwitcher />
|
||||||
|
<Row1 article={sortedArticles()[0]} />
|
||||||
|
<Row2 articles={sortedArticles().slice(1, 3)} />
|
||||||
|
<Slider title={title()} articles={sortedArticles().slice(5, 11)} />
|
||||||
|
<Beside
|
||||||
|
beside={sortedArticles()[12]}
|
||||||
|
title={t('Top viewed')}
|
||||||
|
values={sortedArticles().slice(0, 5)}
|
||||||
|
wrapper={'top-article'}
|
||||||
|
/>
|
||||||
|
<Show when={sortedArticles().length > 5}>
|
||||||
|
<Row3 articles={sortedArticles().slice(13, 16)} />
|
||||||
|
<Row2 articles={sortedArticles().slice(16, 18)} />
|
||||||
|
<Row3 articles={sortedArticles().slice(18, 21)} />
|
||||||
|
<Row3 articles={sortedArticles().slice(21, 24)} />
|
||||||
|
<Row3 articles={sortedArticles().slice(24, 27)} />
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<For each={pages()}>
|
||||||
|
{(page) => (
|
||||||
|
<>
|
||||||
|
<Row3 articles={page.slice(0, 3)} />
|
||||||
|
<Row3 articles={page.slice(3, 6)} />
|
||||||
|
<Row3 articles={page.slice(6, 9)} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
|
||||||
|
<Show when={isLoadMoreButtonVisible()}>
|
||||||
|
<p class="load-more-container">
|
||||||
|
<button class="button" onClick={() => loadMoreLayout(layout())}>
|
||||||
|
{t('Load more')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
</Show>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</PageWrap>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for lazy loading
|
||||||
|
export default LayoutShoutsPage
|
|
@ -1,8 +1,8 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { SearchView } from '../Views/Search'
|
import { SearchView } from '../Views/Search'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { loadSearchResults, resetSortedArticles } from '../../stores/zine/articles'
|
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
import { useRouter } from '../../stores/router'
|
import { useRouter } from '../../stores/router'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
|
|
||||||
|
@ -26,18 +26,18 @@ export const SearchPage = (props: PageProps) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadSearchResults({ query: q(), limit: 50, offset: 0 })
|
await loadShoutsBy({ by: { title: q(), body: q() }, limit: 50, offset: 0 })
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
onCleanup(() => resetSortedArticles())
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Show when={isLoaded()} fallback={<Loading />}>
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
<SearchView results={props.searchResults || []} query={props.searchQuery} />
|
<SearchView results={props.searchResults || []} query={props.searchQuery} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { MainLayout } from '../Layouts/MainLayout'
|
import { PageWrap } from '../_shared/PageWrap'
|
||||||
import { PRERENDERED_ARTICLES_COUNT, TopicView } from '../Views/Topic'
|
import { PRERENDERED_ARTICLES_COUNT, TopicView } from '../Views/Topic'
|
||||||
import type { PageProps } from '../types'
|
import type { PageProps } from '../types'
|
||||||
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
import { createMemo, createSignal, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { loadTopicArticles, resetSortedArticles } from '../../stores/zine/articles'
|
import { loadShoutsBy, resetSortedArticles } from '../../stores/zine/articles'
|
||||||
import { useRouter } from '../../stores/router'
|
import { useRouter } from '../../stores/router'
|
||||||
import { loadTopic } from '../../stores/zine/topics'
|
import { loadTopic } from '../../stores/zine/topics'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
|
|
||||||
export const TopicPage = (props: PageProps) => {
|
export const TopicPage = (props: PageProps) => {
|
||||||
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.topicArticles) && Boolean(props.topic))
|
const [isLoaded, setIsLoaded] = createSignal(Boolean(props.shouts) && Boolean(props.topic))
|
||||||
|
|
||||||
const slug = createMemo(() => {
|
const slug = createMemo(() => {
|
||||||
const { page: getPage } = useRouter()
|
const { page: getPage } = useRouter()
|
||||||
|
@ -27,7 +27,7 @@ export const TopicPage = (props: PageProps) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadTopicArticles({ topicSlug: slug(), limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
|
await loadShoutsBy({ by: { topics: [slug()] }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
|
||||||
await loadTopic({ slug: slug() })
|
await loadTopic({ slug: slug() })
|
||||||
|
|
||||||
setIsLoaded(true)
|
setIsLoaded(true)
|
||||||
|
@ -36,11 +36,11 @@ export const TopicPage = (props: PageProps) => {
|
||||||
onCleanup(() => resetSortedArticles())
|
onCleanup(() => resetSortedArticles())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Show when={isLoaded()} fallback={<Loading />}>
|
<Show when={isLoaded()} fallback={<Loading />}>
|
||||||
<TopicView topic={props.topic} topicArticles={props.topicArticles} topicSlug={slug()} />
|
<TopicView topic={props.topic} shouts={props.shouts} topicSlug={slug()} />
|
||||||
</Show>
|
</Show>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
|
|
||||||
export const DiscussionRulesPage = () => {
|
export const DiscussionRulesPage = () => {
|
||||||
const title = t('Discussion rules')
|
const title = t('Discussion rules')
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<article class="container container--static-page">
|
<article class="container container--static-page">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
||||||
|
@ -114,7 +114,7 @@ export const DiscussionRulesPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
|
|
||||||
// const title = t('Dogma')
|
// const title = t('Dogma')
|
||||||
|
|
||||||
export const DogmaPage = () => {
|
export const DogmaPage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<article class="container container--static-page">
|
<article class="container container--static-page">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
||||||
|
@ -53,7 +53,7 @@ export const DogmaPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createSignal, Show } from 'solid-js'
|
import { createSignal, Show } from 'solid-js'
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
import { Icon } from '../../Nav/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
||||||
export const GuidePage = () => {
|
export const GuidePage = () => {
|
||||||
const title = t('How it works')
|
const title = t('How it works')
|
||||||
|
@ -11,7 +11,7 @@ export const GuidePage = () => {
|
||||||
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
{/*<Meta name="description" content={title} />*/}
|
{/*<Meta name="description" content={title} />*/}
|
||||||
{/*<Meta name="keywords" content={t('Discours') + ',' + title} />*/}
|
{/*<Meta name="keywords" content={t('Discours') + ',' + title} />*/}
|
||||||
{/*<Meta property="og:title" content={title} />*/}
|
{/*<Meta property="og:title" content={title} />*/}
|
||||||
|
@ -283,7 +283,7 @@ export const GuidePage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createSignal, Show } from 'solid-js'
|
import { createSignal, Show } from 'solid-js'
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { Donate } from '../../Discours/Donate'
|
import { Donate } from '../../Discours/Donate'
|
||||||
import { Icon } from '../../Nav/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
||||||
// const title = t('Support us')
|
// const title = t('Support us')
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ export const HelpPage = () => {
|
||||||
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
{/*<Meta name="description">Здесь можно поддержать Дискурс материально.</Meta>*/}
|
{/*<Meta name="description">Здесь можно поддержать Дискурс материально.</Meta>*/}
|
||||||
{/*<Meta name="keywords">Discours.io, помощь, благотворительность</Meta>*/}
|
{/*<Meta name="keywords">Discours.io, помощь, благотворительность</Meta>*/}
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ export const HelpPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { createSignal, Show } from 'solid-js'
|
import { createSignal, Show } from 'solid-js'
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { Modal } from '../../Nav/Modal'
|
import { Modal } from '../../Nav/Modal'
|
||||||
import { Feedback } from '../../Discours/Feedback'
|
import { Feedback } from '../../Discours/Feedback'
|
||||||
import Subscribe from '../../Discours/Subscribe'
|
import Subscribe from '../../Discours/Subscribe'
|
||||||
import Opener from '../../Nav/Opener'
|
import Opener from '../../Nav/Opener'
|
||||||
import { Icon } from '../../Nav/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
||||||
// title={t('Manifest')}
|
// title={t('Manifest')}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export const ManifestPage = () => {
|
||||||
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<Modal name="feedback">
|
<Modal name="feedback">
|
||||||
<Feedback />
|
<Feedback />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -191,7 +191,7 @@ export const ManifestPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
|
|
||||||
// const title = t('Partners')
|
// const title = t('Partners')
|
||||||
|
|
||||||
export const PartnersPage = () => {
|
export const PartnersPage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<article class="container container--static-page">
|
<article class="container container--static-page">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
||||||
|
@ -13,7 +13,7 @@ export const PartnersPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
|
|
||||||
export const PrinciplesPage = () => {
|
export const PrinciplesPage = () => {
|
||||||
const title = t('Principles')
|
const title = t('Principles')
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<article class="container container--static-page">
|
<article class="container container--static-page">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
||||||
|
@ -172,7 +172,7 @@ export const PrinciplesPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
|
|
||||||
// title={t('Projects')}>
|
// title={t('Projects')}>
|
||||||
|
|
||||||
export const ProjectsPage = () => {
|
export const ProjectsPage = () => {
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
<article class="container container--static-page">
|
<article class="container container--static-page">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
<div class="col-md-6 col-xl-7 shift-content order-md-first">
|
||||||
|
@ -13,7 +13,7 @@ export const ProjectsPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createSignal, Show } from 'solid-js'
|
import { createSignal, Show } from 'solid-js'
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { Icon } from '../../Nav/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
||||||
// const title = t('Terms of use')
|
// const title = t('Terms of use')
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ export const TermsOfUsePage = () => {
|
||||||
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
{/*<Meta name="description" content={title} />*/}
|
{/*<Meta name="description" content={title} />*/}
|
||||||
{/*<Meta name="keywords" content={`Discours.io, ${t('Terms of use')}, ${t('Terms of use', 'en')}`} />*/}
|
{/*<Meta name="keywords" content={`Discours.io, ${t('Terms of use')}, ${t('Terms of use', 'en')}`} />*/}
|
||||||
{/*<Meta property="og:title" content={title} />*/}
|
{/*<Meta property="og:title" content={title} />*/}
|
||||||
|
@ -274,7 +274,7 @@ export const TermsOfUsePage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { MainLayout } from '../../Layouts/MainLayout'
|
import { PageWrap } from '../../_shared/PageWrap'
|
||||||
import { t } from '../../../utils/intl'
|
import { t } from '../../../utils/intl'
|
||||||
|
|
||||||
export const ThanksPage = () => {
|
export const ThanksPage = () => {
|
||||||
const title = t('Thank you')
|
const title = t('Thank you')
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<PageWrap>
|
||||||
{/*<Meta name="description" content={title} />*/}
|
{/*<Meta name="description" content={title} />*/}
|
||||||
{/*<Meta name="keywords" content={`Discours.io, ${title}, ${t('Thank you', 'en')}`} />*/}
|
{/*<Meta name="keywords" content={`Discours.io, ${title}, ${t('Thank you', 'en')}`} />*/}
|
||||||
{/*<Meta property="og:title" content={title} />*/}
|
{/*<Meta property="og:title" content={title} />*/}
|
||||||
|
@ -85,7 +85,7 @@ export const ThanksPage = () => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</MainLayout>
|
</PageWrap>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,29 +29,16 @@ import { TermsOfUsePage } from './Pages/about/TermsOfUsePage'
|
||||||
import { ThanksPage } from './Pages/about/ThanksPage'
|
import { ThanksPage } from './Pages/about/ThanksPage'
|
||||||
import { CreatePage } from './Pages/CreatePage'
|
import { CreatePage } from './Pages/CreatePage'
|
||||||
import { ConnectPage } from './Pages/ConnectPage'
|
import { ConnectPage } from './Pages/ConnectPage'
|
||||||
|
import { InboxPage } from './Pages/InboxPage'
|
||||||
|
import { LayoutShoutsPage } from './Pages/LayoutShoutsPage'
|
||||||
import { SessionProvider } from '../context/session'
|
import { SessionProvider } from '../context/session'
|
||||||
|
|
||||||
// TODO: lazy load
|
// TODO: lazy load
|
||||||
// const HomePage = lazy(() => import('./Pages/HomePage'))
|
// const SomePage = lazy(() => import('./Pages/SomePage'))
|
||||||
// const AllTopicsPage = lazy(() => import('./Pages/AllTopicsPage'))
|
|
||||||
// const TopicPage = lazy(() => import('./Pages/TopicPage'))
|
|
||||||
// const AllAuthorsPage = lazy(() => import('./Pages/AllAuthorsPage'))
|
|
||||||
// const AuthorPage = lazy(() => import('./Pages/AuthorPage'))
|
|
||||||
// const FeedPage = lazy(() => import('./Pages/FeedPage'))
|
|
||||||
// const ArticlePage = lazy(() => import('./Pages/ArticlePage'))
|
|
||||||
// const SearchPage = lazy(() => import('./Pages/SearchPage'))
|
|
||||||
// const FourOuFourPage = lazy(() => import('./Pages/FourOuFourPage'))
|
|
||||||
// const DogmaPage = lazy(() => import('./Pages/about/DogmaPage'))
|
|
||||||
// const GuidePage = lazy(() => import('./Pages/about/GuidePage'))
|
|
||||||
// const HelpPage = lazy(() => import('./Pages/about/HelpPage'))
|
|
||||||
// const ManifestPage = lazy(() => import('./Pages/about/ManifestPage'))
|
|
||||||
// const PartnersPage = lazy(() => import('./Pages/about/PartnersPage'))
|
|
||||||
// const ProjectsPage = lazy(() => import('./Pages/about/ProjectsPage'))
|
|
||||||
// const TermsOfUsePage = lazy(() => import('./Pages/about/TermsOfUsePage'))
|
|
||||||
// const ThanksPage = lazy(() => import('./Pages/about/ThanksPage'))
|
|
||||||
// const CreatePage = lazy(() => import('./Pages/about/CreatePage'))
|
|
||||||
|
|
||||||
const pagesMap: Record<keyof Routes, Component<PageProps>> = {
|
const pagesMap: Record<keyof Routes, Component<PageProps>> = {
|
||||||
|
inbox: InboxPage,
|
||||||
|
expo: LayoutShoutsPage,
|
||||||
connect: ConnectPage,
|
connect: ConnectPage,
|
||||||
create: CreatePage,
|
create: CreatePage,
|
||||||
home: HomePage,
|
home: HomePage,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Topic } from '../../graphql/types.gen'
|
import type { Topic } from '../../graphql/types.gen'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import './FloorHeader.scss'
|
import './FloorHeader.scss'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author } from '../../graphql/types.gen'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useAuthorsStore, setAuthorsSort } from '../../stores/zine/authors'
|
import { useAuthorsStore, setAuthorsSort } from '../../stores/zine/authors'
|
||||||
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
||||||
import type { Topic } from '../../graphql/types.gen'
|
import type { Topic } from '../../graphql/types.gen'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics'
|
import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics'
|
||||||
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { createEffect, createSignal, Show, Suspense } from 'solid-js'
|
import { createEffect, createSignal, Show, Suspense } from 'solid-js'
|
||||||
import { FullArticle } from '../Article/FullArticle'
|
import { FullArticle } from '../Article/FullArticle'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import type { Shout } from '../../graphql/types.gen'
|
import type { Shout, Reaction } from '../../graphql/types.gen'
|
||||||
import { loadArticleReactions, useReactionsStore } from '../../stores/zine/reactions'
|
import { useReactionsStore } from '../../stores/zine/reactions'
|
||||||
|
|
||||||
import '../../styles/Article.scss'
|
import '../../styles/Article.scss'
|
||||||
|
|
||||||
interface ArticlePageProps {
|
interface ArticlePageProps {
|
||||||
article: Shout
|
article: Shout
|
||||||
|
reactions?: Reaction[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const ARTICLE_COMMENTS_PAGE_SIZE = 50
|
const ARTICLE_COMMENTS_PAGE_SIZE = 50
|
||||||
|
@ -15,13 +16,20 @@ const ARTICLE_COMMENTS_PAGE_SIZE = 50
|
||||||
export const ArticleView = (props: ArticlePageProps) => {
|
export const ArticleView = (props: ArticlePageProps) => {
|
||||||
const [getCommentsPage] = createSignal(1)
|
const [getCommentsPage] = createSignal(1)
|
||||||
const [getIsCommentsLoading, setIsCommentsLoading] = createSignal(false)
|
const [getIsCommentsLoading, setIsCommentsLoading] = createSignal(false)
|
||||||
const reactionslist = useReactionsStore()
|
const {
|
||||||
|
reactionsByShout,
|
||||||
|
sortedReactions,
|
||||||
|
createReaction,
|
||||||
|
updateReaction,
|
||||||
|
deleteReaction,
|
||||||
|
loadReactionsBy
|
||||||
|
} = useReactionsStore({ reactions: props.reactions })
|
||||||
|
|
||||||
createEffect(async () => {
|
createEffect(async () => {
|
||||||
try {
|
try {
|
||||||
setIsCommentsLoading(true)
|
setIsCommentsLoading(true)
|
||||||
await loadArticleReactions({
|
await loadReactionsBy({
|
||||||
articleSlug: props.article.slug,
|
by: { shout: props.article.slug },
|
||||||
limit: ARTICLE_COMMENTS_PAGE_SIZE,
|
limit: ARTICLE_COMMENTS_PAGE_SIZE,
|
||||||
offset: getCommentsPage() * ARTICLE_COMMENTS_PAGE_SIZE
|
offset: getCommentsPage() * ARTICLE_COMMENTS_PAGE_SIZE
|
||||||
})
|
})
|
||||||
|
@ -36,7 +44,7 @@ export const ArticleView = (props: ArticlePageProps) => {
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<FullArticle
|
<FullArticle
|
||||||
article={props.article}
|
article={props.article}
|
||||||
reactions={reactionslist().filter((r) => r.shout.slug === props.article.slug)}
|
reactions={reactionsByShout()[props.article.slug]}
|
||||||
isCommentsLoading={getIsCommentsLoading()}
|
isCommentsLoading={getIsCommentsLoading()}
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Row3 } from '../Feed/Row3'
|
||||||
import { AuthorFull } from '../Author/Full'
|
import { AuthorFull } from '../Author/Full'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useAuthorsStore } from '../../stores/zine/authors'
|
import { useAuthorsStore } from '../../stores/zine/authors'
|
||||||
import { loadAuthorArticles, useArticlesStore } from '../../stores/zine/articles'
|
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles'
|
||||||
|
|
||||||
import { useTopicsStore } from '../../stores/zine/topics'
|
import { useTopicsStore } from '../../stores/zine/topics'
|
||||||
import { useRouter } from '../../stores/router'
|
import { useRouter } from '../../stores/router'
|
||||||
|
@ -15,7 +15,7 @@ import { splitToPages } from '../../utils/splitToPages'
|
||||||
|
|
||||||
// TODO: load reactions on client
|
// TODO: load reactions on client
|
||||||
type AuthorProps = {
|
type AuthorProps = {
|
||||||
authorArticles: Shout[]
|
shouts: Shout[]
|
||||||
author: Author
|
author: Author
|
||||||
authorSlug: string
|
authorSlug: string
|
||||||
// FIXME author topics fro server
|
// FIXME author topics fro server
|
||||||
|
@ -23,7 +23,7 @@ type AuthorProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorPageSearchParams = {
|
type AuthorPageSearchParams = {
|
||||||
by: '' | 'viewed' | 'rating' | 'commented' | 'recent'
|
by: '' | 'viewed' | 'rating' | 'commented' | 'recent' | 'followed'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PRERENDERED_ARTICLES_COUNT = 12
|
export const PRERENDERED_ARTICLES_COUNT = 12
|
||||||
|
@ -31,7 +31,7 @@ const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
|
||||||
|
|
||||||
export const AuthorView = (props: AuthorProps) => {
|
export const AuthorView = (props: AuthorProps) => {
|
||||||
const { sortedArticles } = useArticlesStore({
|
const { sortedArticles } = useArticlesStore({
|
||||||
sortedArticles: props.authorArticles
|
shouts: props.shouts
|
||||||
})
|
})
|
||||||
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
|
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
|
||||||
const { topicsByAuthor } = useTopicsStore()
|
const { topicsByAuthor } = useTopicsStore()
|
||||||
|
@ -42,8 +42,8 @@ export const AuthorView = (props: AuthorProps) => {
|
||||||
|
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
const { hasMore } = await loadAuthorArticles({
|
const { hasMore } = await loadShoutsBy({
|
||||||
authorSlug: author().slug,
|
by: { author: author().slug },
|
||||||
limit: LOAD_MORE_PAGE_SIZE,
|
limit: LOAD_MORE_PAGE_SIZE,
|
||||||
offset: sortedArticles().length
|
offset: sortedArticles().length
|
||||||
})
|
})
|
||||||
|
@ -76,27 +76,21 @@ export const AuthorView = (props: AuthorProps) => {
|
||||||
<div class="row group__controls">
|
<div class="row group__controls">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<ul class="view-switcher">
|
<ul class="view-switcher">
|
||||||
<li classList={{ selected: !searchParams().by || searchParams().by === 'recent' }}>
|
<li classList={{ selected: searchParams().by === 'rating' }}>
|
||||||
<button type="button" onClick={() => changeSearchParam('by', 'recent')}>
|
<button type="button" onClick={() => changeSearchParam('by', 'rating')}>
|
||||||
{t('Recent')}
|
{t('Popular')}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li classList={{ selected: searchParams().by === 'followed' }}>
|
||||||
|
<button type="button" onClick={() => changeSearchParam('by', 'followed')}>
|
||||||
|
{t('Followers')}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li classList={{ selected: searchParams().by === 'commented' }}>
|
||||||
|
<button type="button" onClick={() => changeSearchParam('by', 'commented')}>
|
||||||
|
{t('Discussing')}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{/*TODO: server sort*/}
|
|
||||||
{/*<li classList={{ selected: getSearchParams().by === 'rating' }}>*/}
|
|
||||||
{/* <button type="button" onClick={() => changeSearchParam('by', 'rating')}>*/}
|
|
||||||
{/* {t('Popular')}*/}
|
|
||||||
{/* </button>*/}
|
|
||||||
{/*</li>*/}
|
|
||||||
{/*<li classList={{ selected: getSearchParams().by === 'viewed' }}>*/}
|
|
||||||
{/* <button type="button" onClick={() => changeSearchParam('by', 'viewed')}>*/}
|
|
||||||
{/* {t('Views')}*/}
|
|
||||||
{/* </button>*/}
|
|
||||||
{/*</li>*/}
|
|
||||||
{/*<li classList={{ selected: getSearchParams().by === 'commented' }}>*/}
|
|
||||||
{/* <button type="button" onClick={() => changeSearchParam('by', 'commented')}>*/}
|
|
||||||
{/* {t('Discussing')}*/}
|
|
||||||
{/* </button>*/}
|
|
||||||
{/*</li>*/}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
|
|
|
@ -1,63 +1,71 @@
|
||||||
import { createMemo, createSignal, For, onMount, Show } from 'solid-js'
|
import { createEffect, createSignal, For, onMount, Show } from 'solid-js'
|
||||||
import '../../styles/Feed.scss'
|
import '../../styles/Feed.scss'
|
||||||
import stylesBeside from '../../components/Feed/Beside.module.scss'
|
import stylesBeside from '../../components/Feed/Beside.module.scss'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { byCreated, sortBy } from '../../utils/sortby'
|
|
||||||
import { TopicCard } from '../Topic/Card'
|
import { TopicCard } from '../Topic/Card'
|
||||||
import { ArticleCard } from '../Feed/Card'
|
import { ArticleCard } from '../Feed/Card'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { FeedSidebar } from '../Feed/Sidebar'
|
import { FeedSidebar } from '../Feed/Sidebar'
|
||||||
import CommentCard from '../Article/Comment'
|
import CommentCard from '../Article/Comment'
|
||||||
import { loadRecentArticles, useArticlesStore } from '../../stores/zine/articles'
|
import { useArticlesStore } from '../../stores/zine/articles'
|
||||||
import { useReactionsStore } from '../../stores/zine/reactions'
|
import { useReactionsStore } from '../../stores/zine/reactions'
|
||||||
import { useAuthorsStore } from '../../stores/zine/authors'
|
import { useAuthorsStore } from '../../stores/zine/authors'
|
||||||
import { useTopicsStore } from '../../stores/zine/topics'
|
import { useTopicsStore } from '../../stores/zine/topics'
|
||||||
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
||||||
import { useSession } from '../../context/session'
|
import { useSession } from '../../context/session'
|
||||||
|
import { Collab, ReactionKind, Shout } from '../../graphql/types.gen'
|
||||||
|
import { collabs, setCollabs } from '../../stores/editor'
|
||||||
|
|
||||||
// const AUTHORSHIP_REACTIONS = [
|
const AUTHORSHIP_REACTIONS = [
|
||||||
// ReactionKind.Accept,
|
ReactionKind.Accept,
|
||||||
// ReactionKind.Reject,
|
ReactionKind.Reject,
|
||||||
// ReactionKind.Propose,
|
ReactionKind.Propose,
|
||||||
// ReactionKind.Ask
|
ReactionKind.Ask
|
||||||
// ]
|
]
|
||||||
|
|
||||||
export const FEED_PAGE_SIZE = 20
|
export const FEED_PAGE_SIZE = 20
|
||||||
|
|
||||||
export const FeedView = () => {
|
export const FeedView = () => {
|
||||||
// state
|
// state
|
||||||
const { sortedArticles } = useArticlesStore()
|
const { sortedArticles, loadShoutsBy } = useArticlesStore()
|
||||||
const reactions = useReactionsStore()
|
const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore({})
|
||||||
const { sortedAuthors } = useAuthorsStore()
|
const { sortedAuthors } = useAuthorsStore()
|
||||||
const { topTopics } = useTopicsStore()
|
const { topTopics } = useTopicsStore()
|
||||||
const { topAuthors } = useTopAuthorsStore()
|
const { topAuthors } = useTopAuthorsStore()
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
|
|
||||||
const topReactions = createMemo(() => sortBy(reactions(), byCreated))
|
|
||||||
|
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
|
|
||||||
// const expectingFocus = createMemo<Shout[]>(() => {
|
|
||||||
// // 1 co-author notifications needs
|
|
||||||
// // TODO: list of articles where you are co-author
|
|
||||||
// // TODO: preload proposals
|
|
||||||
// // TODO: (maybe?) and changes history
|
|
||||||
// console.debug(reactions().filter((r) => r.kind in AUTHORSHIP_REACTIONS))
|
|
||||||
//
|
|
||||||
// // 2 community self-regulating mechanics
|
|
||||||
// // TODO: query all new posts to be rated for publishing
|
|
||||||
// // TODO: query all reactions where user is in authors list
|
|
||||||
// return []
|
|
||||||
// })
|
|
||||||
|
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
const { hasMore } = await loadRecentArticles({ limit: FEED_PAGE_SIZE, offset: sortedArticles().length })
|
const { hasMore } = await loadShoutsBy({
|
||||||
|
by: { visibility: 'community' },
|
||||||
|
limit: FEED_PAGE_SIZE,
|
||||||
|
offset: sortedArticles().length
|
||||||
|
})
|
||||||
setIsLoadMoreButtonVisible(hasMore)
|
setIsLoadMoreButtonVisible(hasMore)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(async () => {
|
||||||
loadMore()
|
// load 5 recent comments overall
|
||||||
|
await loadReactionsBy({ by: {}, limit: 5, offset: 0 })
|
||||||
|
|
||||||
|
// load recent shouts not only published ( visibility = community )
|
||||||
|
await loadMore()
|
||||||
|
|
||||||
|
// TODO: load collabs
|
||||||
|
// await loadCollabs()
|
||||||
|
|
||||||
|
// load recent editing shouts ( visibility = authors )
|
||||||
|
const userslug = session().user.slug
|
||||||
|
await loadShoutsBy({ by: { author: userslug, visibility: 'authors' }, limit: 15, offset: 0 })
|
||||||
|
const collaborativeShouts = sortedArticles().filter((s: Shout, n: number, arr: Shout[]) => {
|
||||||
|
if (s.visibility !== 'authors') {
|
||||||
|
arr.splice(n, 1)
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// load recent reactions on collabs
|
||||||
|
await loadReactionsBy({ by: { shouts: [...collaborativeShouts], body: true }, limit: 5, offset: 0 })
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -118,7 +126,7 @@ export const FeedView = () => {
|
||||||
<aside class="col-md-3">
|
<aside class="col-md-3">
|
||||||
<section class="feed-comments">
|
<section class="feed-comments">
|
||||||
<h4>{t('Comments')}</h4>
|
<h4>{t('Comments')}</h4>
|
||||||
<For each={topReactions()}>
|
<For each={topComments()}>
|
||||||
{(comment) => <CommentCard comment={comment} compact={true} />}
|
{(comment) => <CommentCard comment={comment} compact={true} />}
|
||||||
</For>
|
</For>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { handleClientRouteLinkClick } from '../../stores/router'
|
||||||
// by: '' | 'topics' | 'authors' | 'reacted'
|
// by: '' | 'topics' | 'authors' | 'reacted'
|
||||||
// }
|
// }
|
||||||
|
|
||||||
export const FeedSettingsView = () => {
|
export const FeedSettingsView = (_props) => {
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>{t('Feed settings')}</h1>
|
<h1>{t('Feed settings')}</h1>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import styles from '../../styles/FourOuFour.module.scss'
|
import styles from '../../styles/FourOuFour.module.scss'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
|
|
|
@ -11,15 +11,10 @@ import RowShort from '../Feed/RowShort'
|
||||||
import Slider from '../Feed/Slider'
|
import Slider from '../Feed/Slider'
|
||||||
import Group from '../Feed/Group'
|
import Group from '../Feed/Group'
|
||||||
import type { Shout, Topic } from '../../graphql/types.gen'
|
import type { Shout, Topic } from '../../graphql/types.gen'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useTopicsStore } from '../../stores/zine/topics'
|
import { useTopicsStore } from '../../stores/zine/topics'
|
||||||
import {
|
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles'
|
||||||
loadPublishedArticles,
|
|
||||||
loadTopArticles,
|
|
||||||
loadTopMonthArticles,
|
|
||||||
useArticlesStore
|
|
||||||
} from '../../stores/zine/articles'
|
|
||||||
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
||||||
import { locale } from '../../stores/ui'
|
import { locale } from '../../stores/ui'
|
||||||
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
||||||
|
@ -27,7 +22,7 @@ import { splitToPages } from '../../utils/splitToPages'
|
||||||
|
|
||||||
type HomeProps = {
|
type HomeProps = {
|
||||||
randomTopics: Topic[]
|
randomTopics: Topic[]
|
||||||
recentPublishedArticles: Shout[]
|
shouts: Shout[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PRERENDERED_ARTICLES_COUNT = 5
|
export const PRERENDERED_ARTICLES_COUNT = 5
|
||||||
|
@ -37,26 +32,24 @@ const LOAD_MORE_PAGE_SIZE = 16 // Row1 + Row3 + Row2 + Beside (3 + 1) + Row1 + R
|
||||||
export const HomeView = (props: HomeProps) => {
|
export const HomeView = (props: HomeProps) => {
|
||||||
const {
|
const {
|
||||||
sortedArticles,
|
sortedArticles,
|
||||||
|
articlesByLayout,
|
||||||
topArticles,
|
topArticles,
|
||||||
topMonthArticles,
|
|
||||||
topViewedArticles,
|
|
||||||
topCommentedArticles,
|
topCommentedArticles,
|
||||||
articlesByLayout
|
topMonthArticles,
|
||||||
|
topViewedArticles
|
||||||
} = useArticlesStore({
|
} = useArticlesStore({
|
||||||
sortedArticles: props.recentPublishedArticles
|
shouts: props.shouts
|
||||||
})
|
})
|
||||||
const { randomTopics, topTopics } = useTopicsStore({
|
const { randomTopics, topTopics } = useTopicsStore({
|
||||||
randomTopics: props.randomTopics
|
randomTopics: props.randomTopics
|
||||||
})
|
})
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
|
|
||||||
const { topAuthors } = useTopAuthorsStore()
|
const { topAuthors } = useTopAuthorsStore()
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
loadTopArticles()
|
|
||||||
loadTopMonthArticles()
|
|
||||||
if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) {
|
if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) {
|
||||||
const { hasMore } = await loadPublishedArticles({
|
const { hasMore } = await loadShoutsBy({
|
||||||
|
by: {},
|
||||||
limit: CLIENT_LOAD_ARTICLES_COUNT,
|
limit: CLIENT_LOAD_ARTICLES_COUNT,
|
||||||
offset: sortedArticles().length
|
offset: sortedArticles().length
|
||||||
})
|
})
|
||||||
|
@ -84,7 +77,8 @@ export const HomeView = (props: HomeProps) => {
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
|
|
||||||
const { hasMore } = await loadPublishedArticles({
|
const { hasMore } = await loadShoutsBy({
|
||||||
|
by: { visibility: 'public' },
|
||||||
limit: LOAD_MORE_PAGE_SIZE,
|
limit: LOAD_MORE_PAGE_SIZE,
|
||||||
offset: sortedArticles().length
|
offset: sortedArticles().length
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,59 +1,158 @@
|
||||||
|
import { For, createSignal, Show, onMount, createEffect } from 'solid-js'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author } from '../../graphql/types.gen'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { Icon } from '../Nav/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import '../../styles/Inbox.scss'
|
import { Loading } from '../Loading'
|
||||||
|
import DialogCard from '../Inbox/DialogCard'
|
||||||
|
import Search from '../Inbox/Search'
|
||||||
|
import { useAuthorsStore } from '../../stores/zine/authors'
|
||||||
|
import MarkdownIt from 'markdown-it'
|
||||||
|
// const { session } = useAuthStore()
|
||||||
|
|
||||||
|
import '../../styles/Inbox.scss'
|
||||||
|
// Для моков
|
||||||
|
import { createClient } from '@urql/core'
|
||||||
|
import { findAndLoadGraphQLConfig } from '@graphql-codegen/cli'
|
||||||
|
// import { useAuthStore } from '../../stores/auth'
|
||||||
|
import { useSession } from '../../context/session'
|
||||||
|
|
||||||
|
const md = new MarkdownIt({
|
||||||
|
linkify: true
|
||||||
|
})
|
||||||
|
const OWNER_ID = '501'
|
||||||
|
const client = createClient({
|
||||||
|
url: 'https://graphqlzero.almansi.me/api'
|
||||||
|
})
|
||||||
|
|
||||||
|
// console.log('!!! session:', session)
|
||||||
// interface InboxProps {
|
// interface InboxProps {
|
||||||
// chats?: Chat[]
|
// chats?: Chat[]
|
||||||
// messages?: Message[]
|
// messages?: Message[]
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const messageQuery = `
|
||||||
|
query Comments ($options: PageQueryOptions) {
|
||||||
|
comments(options: $options) {
|
||||||
|
data {
|
||||||
|
id
|
||||||
|
body
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const newMessageQuery = `
|
||||||
|
mutation postComment($messageBody: String!) {
|
||||||
|
createComment(
|
||||||
|
input: { body: $messageBody, email: "test@test.com", name: "User" }
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
body
|
||||||
|
name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const userSearch = (array: Author[], keyword: string) => {
|
||||||
|
const searchTerm = keyword.toLowerCase()
|
||||||
|
return array.filter((value) => {
|
||||||
|
return value.name.toLowerCase().match(new RegExp(searchTerm, 'g'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const postMessage = async (msg: string) => {
|
||||||
|
const response = await client.mutation(newMessageQuery, { messageBody: msg }).toPromise()
|
||||||
|
return response.data.createComment
|
||||||
|
}
|
||||||
|
|
||||||
export const InboxView = () => {
|
export const InboxView = () => {
|
||||||
// TODO: get user session
|
const [messages, setMessages] = createSignal([])
|
||||||
|
const [authors, setAuthors] = createSignal<Author[]>([])
|
||||||
|
const [postMessageText, setPostMessageText] = createSignal('')
|
||||||
|
const [loading, setLoading] = createSignal<boolean>(false)
|
||||||
|
const [currentSlug, setCurrentSlug] = createSignal<Author['slug'] | null>()
|
||||||
|
|
||||||
|
const { session } = useSession()
|
||||||
|
const { sortedAuthors } = useAuthorsStore()
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
setAuthors(sortedAuthors())
|
||||||
|
setCurrentSlug(session()?.user?.slug)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Поиск по диалогам
|
||||||
|
const getQuery = (query) => {
|
||||||
|
if (query().length >= 2) {
|
||||||
|
const match = userSearch(authors(), query())
|
||||||
|
console.log('!!! match:', match)
|
||||||
|
setAuthors(match)
|
||||||
|
} else {
|
||||||
|
setAuthors(sortedAuthors())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchMessages = async (query) => {
|
||||||
|
const response = await client
|
||||||
|
.query(query, {
|
||||||
|
options: { slice: { start: 0, end: 3 } }
|
||||||
|
})
|
||||||
|
.toPromise()
|
||||||
|
if (response.error) console.debug('getMessages', response.error)
|
||||||
|
setMessages(response.data.comments.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
let chatWindow
|
||||||
|
onMount(async () => {
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
await fetchMessages(messageQuery)
|
||||||
|
} catch (error) {
|
||||||
|
setLoading(false)
|
||||||
|
console.error([fetchMessages], error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
chatWindow.scrollTop = chatWindow.scrollHeight
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const post = await postMessage(postMessageText())
|
||||||
|
setMessages((prev) => [...prev, post])
|
||||||
|
setPostMessageText('')
|
||||||
|
chatWindow.scrollTop = chatWindow.scrollHeight
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[post message error]:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let formParent // autoresize ghost element
|
||||||
|
const handleChangeMessage = (event) => {
|
||||||
|
setPostMessageText(event.target.value)
|
||||||
|
}
|
||||||
|
createEffect(() => {
|
||||||
|
formParent.dataset.replicatedValue = postMessageText()
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="messages container">
|
<div class="messages container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="chat-list col-md-4">
|
<div class="chat-list col-md-4">
|
||||||
<form class="chat-list__search">
|
<Search placeholder="Поиск" onChange={getQuery} />
|
||||||
<input type="search" placeholder="Поиск" />
|
|
||||||
<button class="button">+</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="chat-list__types">
|
<div class="chat-list__types">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<strong>
|
<strong>Все</strong>
|
||||||
<a href="/">Все</a>
|
|
||||||
</strong>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/">Переписки</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/">Группы</a>
|
|
||||||
</li>
|
</li>
|
||||||
|
<li>Переписки</li>
|
||||||
|
<li>Группы</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="holder">
|
||||||
<div class="chat-list__users">
|
<div class="dialogs">
|
||||||
<ul>
|
<For each={authors()}>{(author) => <DialogCard author={author} online={true} />}</For>
|
||||||
<li>
|
</div>
|
||||||
<AuthorCard author={{} as Author} hideFollow={true} />
|
|
||||||
<div class="last-message-date">12:15</div>
|
|
||||||
<div class="last-message-text">
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="user--online chat-list__user--current">
|
|
||||||
<AuthorCard author={{} as Author} hideFollow={true} />
|
|
||||||
<div class="last-message-date">19:48</div>
|
|
||||||
<div class="last-message-text">
|
|
||||||
Assumenda delectus deleniti dolores doloribus ducimus, et expedita facere iste laborum,
|
|
||||||
nihil similique suscipit, ut voluptatem. Accusantium consequuntur doloremque ex molestiae
|
|
||||||
nemo.
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -64,59 +163,55 @@ export const InboxView = () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="conversation__messages">
|
<div class="conversation__messages">
|
||||||
<div class="conversation__messages-container">
|
<div class="conversation__messages-container" ref={chatWindow}>
|
||||||
<div class="conversation__message-container conversation__message-container--other">
|
<Show when={loading()}>
|
||||||
<div class="conversation__message">
|
<Loading />
|
||||||
Круто, беру в оборот!
|
</Show>
|
||||||
<div class="conversation__message-details">
|
<For each={messages()}>
|
||||||
<time>14:26</time>
|
{(comment: { body: string; id: string; email: string }) => (
|
||||||
|
<div
|
||||||
|
class={`conversation__message-container
|
||||||
|
${
|
||||||
|
OWNER_ID === comment.id
|
||||||
|
? 'conversation__message-container--own'
|
||||||
|
: 'conversation__message-container--other'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div class="conversation__message">
|
||||||
|
<div innerHTML={md.render(comment.body)} />
|
||||||
|
<div class="conversation__message-details">
|
||||||
|
<time>14:26</time>
|
||||||
|
{comment.email} id: {comment.id}
|
||||||
|
</div>
|
||||||
|
<button class="conversation__context-popup-control">
|
||||||
|
<Icon name="ellipsis" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="conversation__context-popup-control">
|
)}
|
||||||
<Icon name="ellipsis" />
|
</For>
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="conversation__message-container conversation__message-container--own">
|
{/*<div class="conversation__date">*/}
|
||||||
<div class="conversation__message">
|
{/* <time>12 сентября</time>*/}
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut beatae earum iste itaque
|
{/*</div>*/}
|
||||||
libero perspiciatis possimus quod! Accusamus, aliquam amet consequuntur debitis dolorum
|
|
||||||
esse laudantium magni omnis rerum voluptatem voluptates!
|
|
||||||
<div class="conversation__message-details">
|
|
||||||
<time>14:31</time>
|
|
||||||
Отредактировано
|
|
||||||
</div>
|
|
||||||
<button class="conversation__context-popup-control">
|
|
||||||
<Icon name="ellipsis" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="conversation__date">
|
|
||||||
<time>12 сентября</time>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="conversation__message-container conversation__message-container--other">
|
|
||||||
<div class="conversation__message">
|
|
||||||
Нужна грамотная инфраструктура для сообщений, если ожидается нагрузка - надо опираться на
|
|
||||||
это. Но в целом это несложно сделать.
|
|
||||||
<div class="conversation__message-details">
|
|
||||||
<time>10:47</time>
|
|
||||||
</div>
|
|
||||||
<button class="conversation__context-popup-control">
|
|
||||||
<Icon name="ellipsis" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="conversation__message-form">
|
<div class="message-form">
|
||||||
<input type="text" placeholder="Написать сообщение" />
|
<div class="wrapper">
|
||||||
<button type="submit">
|
<div class="grow-wrap" ref={formParent}>
|
||||||
<Icon name="send-message" />
|
<textarea
|
||||||
</button>
|
value={postMessageText()}
|
||||||
</form>
|
rows={1}
|
||||||
|
onInput={(event) => handleChangeMessage(event)}
|
||||||
|
placeholder="Написать сообщение"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit" disabled={postMessageText().length === 0} onClick={handleSubmit}>
|
||||||
|
<Icon name="send-message" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import '../../styles/Search.scss'
|
||||||
import type { Shout } from '../../graphql/types.gen'
|
import type { Shout } from '../../graphql/types.gen'
|
||||||
import { ArticleCard } from '../Feed/Card'
|
import { ArticleCard } from '../Feed/Card'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useArticlesStore, loadSearchResults } from '../../stores/zine/articles'
|
import { useArticlesStore, loadShoutsBy } from '../../stores/zine/articles'
|
||||||
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
|
||||||
|
|
||||||
type SearchPageSearchParams = {
|
type SearchPageSearchParams = {
|
||||||
|
@ -16,7 +16,7 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SearchView = (props: Props) => {
|
export const SearchView = (props: Props) => {
|
||||||
const { sortedArticles } = useArticlesStore({ sortedArticles: props.results })
|
const { sortedArticles } = useArticlesStore({ shouts: props.results })
|
||||||
const [getQuery, setQuery] = createSignal(props.query)
|
const [getQuery, setQuery] = createSignal(props.query)
|
||||||
|
|
||||||
const { searchParams } = useRouter<SearchPageSearchParams>()
|
const { searchParams } = useRouter<SearchPageSearchParams>()
|
||||||
|
@ -28,7 +28,7 @@ export const SearchView = (props: Props) => {
|
||||||
const handleSubmit = (_ev) => {
|
const handleSubmit = (_ev) => {
|
||||||
// TODO page
|
// TODO page
|
||||||
// TODO sort
|
// TODO sort
|
||||||
loadSearchResults({ query: getQuery() })
|
loadShoutsBy({ by: { title: getQuery(), body: getQuery() }, limit: 50 })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { FullTopic } from '../Topic/Full'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useRouter } from '../../stores/router'
|
import { useRouter } from '../../stores/router'
|
||||||
import { useTopicsStore } from '../../stores/zine/topics'
|
import { useTopicsStore } from '../../stores/zine/topics'
|
||||||
import { loadTopicArticles, useArticlesStore } from '../../stores/zine/articles'
|
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles'
|
||||||
import { useAuthorsStore } from '../../stores/zine/authors'
|
import { useAuthorsStore } from '../../stores/zine/authors'
|
||||||
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
|
||||||
import { splitToPages } from '../../utils/splitToPages'
|
import { splitToPages } from '../../utils/splitToPages'
|
||||||
|
@ -22,7 +22,7 @@ type TopicsPageSearchParams = {
|
||||||
|
|
||||||
interface TopicProps {
|
interface TopicProps {
|
||||||
topic: Topic
|
topic: Topic
|
||||||
topicArticles: Shout[]
|
shouts: Shout[]
|
||||||
topicSlug: string
|
topicSlug: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ export const TopicView = (props: TopicProps) => {
|
||||||
|
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
|
|
||||||
const { sortedArticles } = useArticlesStore({ sortedArticles: props.topicArticles })
|
const { sortedArticles } = useArticlesStore({ shouts: props.shouts })
|
||||||
const { topicEntities } = useTopicsStore({ topics: [props.topic] })
|
const { topicEntities } = useTopicsStore({ topics: [props.topic] })
|
||||||
|
|
||||||
const { authorsByTopic } = useAuthorsStore()
|
const { authorsByTopic } = useAuthorsStore()
|
||||||
|
@ -44,8 +44,8 @@ export const TopicView = (props: TopicProps) => {
|
||||||
const loadMore = async () => {
|
const loadMore = async () => {
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
|
|
||||||
const { hasMore } = await loadTopicArticles({
|
const { hasMore } = await loadShoutsBy({
|
||||||
topicSlug: topic().slug,
|
by: { topic: topic().slug },
|
||||||
limit: LOAD_MORE_PAGE_SIZE,
|
limit: LOAD_MORE_PAGE_SIZE,
|
||||||
offset: sortedArticles().length
|
offset: sortedArticles().length
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,7 @@ import '../../styles/app.scss'
|
||||||
import { Show } from 'solid-js'
|
import { Show } from 'solid-js'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
|
||||||
type MainLayoutProps = {
|
type PageWrapProps = {
|
||||||
headerTitle?: string
|
headerTitle?: string
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
isHeaderFixed?: boolean
|
isHeaderFixed?: boolean
|
||||||
|
@ -14,7 +14,7 @@ type MainLayoutProps = {
|
||||||
class?: string
|
class?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MainLayout = (props: MainLayoutProps) => {
|
export const PageWrap = (props: PageWrapProps) => {
|
||||||
const isHeaderFixed = props.isHeaderFixed !== undefined ? props.isHeaderFixed : true
|
const isHeaderFixed = props.isHeaderFixed !== undefined ? props.isHeaderFixed : true
|
||||||
|
|
||||||
return (
|
return (
|
|
@ -1,18 +1,18 @@
|
||||||
// in a separate file to avoid circular dependencies
|
// in a separate file to avoid circular dependencies
|
||||||
import type { Author, Chat, Shout, Topic } from '../graphql/types.gen'
|
import type { Author, Chat, Shout, Topic } from '../graphql/types.gen'
|
||||||
|
import type { LayoutType } from '../stores/zine/layouts'
|
||||||
|
|
||||||
// all the things (she said) that could be passed from the server
|
// all the things (she said) that could be passed from the server
|
||||||
export type PageProps = {
|
export type PageProps = {
|
||||||
randomTopics?: Topic[]
|
randomTopics?: Topic[]
|
||||||
article?: Shout
|
article?: Shout
|
||||||
authorArticles?: Shout[]
|
shouts?: Shout[]
|
||||||
topicArticles?: Shout[]
|
|
||||||
homeArticles?: Shout[]
|
|
||||||
author?: Author
|
author?: Author
|
||||||
allAuthors?: Author[]
|
allAuthors?: Author[]
|
||||||
topic?: Topic
|
topic?: Topic
|
||||||
allTopics?: Topic[]
|
allTopics?: Topic[]
|
||||||
searchQuery?: string
|
searchQuery?: string
|
||||||
|
layout?: LayoutType
|
||||||
// other types?
|
// other types?
|
||||||
searchResults?: Shout[]
|
searchResults?: Shout[]
|
||||||
chats?: Chat[]
|
chats?: Chat[]
|
||||||
|
|
12
src/graphql/mutation/create-chat.ts
Normal file
12
src/graphql/mutation/create-chat.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
mutation CreateChat($title: String, $members: [String]!) {
|
||||||
|
createChat(title: $title, members: $members) {
|
||||||
|
error
|
||||||
|
chat {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -1,9 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
mutation IncrementViewMutation($shout: String!) {
|
|
||||||
incrementView(shout: $shout) {
|
|
||||||
error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query GetShoutBySlugQuery($slug: String!) {
|
|
||||||
getShoutBySlug(slug: $slug) {
|
|
||||||
_id: slug
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
body
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
caption
|
|
||||||
}
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
rating
|
|
||||||
commented
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query ShoutsForAuthorsQuery($slugs: [String]!, $limit: Int!, $offset: Int!) {
|
|
||||||
shoutsByAuthors(slugs: $slugs, limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
slug
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
bio
|
|
||||||
links
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query ShoutsForCommunitiesQuery($slugs: [String]!, $limit: Int!, $offset: Int!) {
|
|
||||||
shoutsByCommunities(slugs: $slugs, limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
slug
|
|
||||||
cover
|
|
||||||
# community { ... }
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query ShoutsBySessionQuery($limit: Int!, $offset: Int!) {
|
|
||||||
shoutsForFeed(limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
slug
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
rating
|
|
||||||
commented
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query ShoutsForTopicsQuery($slugs: [String]!, $limit: Int!, $offset: Int!) {
|
|
||||||
shoutsByTopics(slugs: $slugs, limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
slug
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,14 +1,16 @@
|
||||||
import { gql } from '@urql/core'
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
query RecentPublishedQuery($limit: Int!, $offset: Int!) {
|
query LoadShoutsByQuery($by: ShoutsBy, $limit: Int!, $offset: Int!) {
|
||||||
recentPublished(limit: $limit, offset: $offset) {
|
loadShoutsBy(by: $by, limit: $limit, offset: $offset) {
|
||||||
_id: slug
|
_id: slug
|
||||||
title
|
title
|
||||||
subtitle
|
subtitle
|
||||||
slug
|
slug
|
||||||
layout
|
layout
|
||||||
cover
|
cover
|
||||||
|
# community
|
||||||
|
mainTopic
|
||||||
topics {
|
topics {
|
||||||
title
|
title
|
||||||
body
|
body
|
||||||
|
@ -26,10 +28,8 @@ export default gql`
|
||||||
slug
|
slug
|
||||||
userpic
|
userpic
|
||||||
}
|
}
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
publishedAt
|
|
||||||
createdAt
|
createdAt
|
||||||
|
publishedAt
|
||||||
stat {
|
stat {
|
||||||
_id: viewed
|
_id: viewed
|
||||||
viewed
|
viewed
|
|
@ -1,40 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query RecentAllQuery($limit: Int!, $offset: Int!) {
|
|
||||||
recentAll(limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
slug
|
|
||||||
layout
|
|
||||||
cover
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
createdAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query TopMonthShoutsQuery($limit: Int!, $offset: Int!) {
|
|
||||||
topMonth(limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
slug
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,41 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query TopOverallShoutsQuery($limit: Int!, $offset: Int!) {
|
|
||||||
topOverall(limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
slug
|
|
||||||
layout
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,41 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query TopViewedShoutsQuery($limit: Int!, $offset: Int!) {
|
|
||||||
topViewed(limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
slug
|
|
||||||
layout
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { gql } from '@urql/core'
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
query GetAuthorsBySlugsQuery($slugs: [String]!) {
|
query AuthorLoadByQuery($by: AuthorsBy, $limit: Int, $offset: Int) {
|
||||||
getUsersBySlugs(slugs: $slugs) {
|
loadAuthorsBy(by: $by, limit: $limit, offset: $offset) {
|
||||||
_id: slug
|
_id: slug
|
||||||
slug
|
slug
|
||||||
name
|
name
|
16
src/graphql/query/chat-messages-load-by.ts
Normal file
16
src/graphql/query/chat-messages-load-by.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
query LoadMessagesQuery($by: MessagesBy!, $limit: Int, $offset: Int) {
|
||||||
|
loadMessagesBy(by: $by, limit: $limit, offset: $offset) {
|
||||||
|
error
|
||||||
|
messages {
|
||||||
|
author
|
||||||
|
body
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
seen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
26
src/graphql/query/chats-load.ts
Normal file
26
src/graphql/query/chats-load.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
query GetChatsQuery($limit: Int, $offset: Int) {
|
||||||
|
loadChats(limit: $limit, offset: $offset) {
|
||||||
|
error
|
||||||
|
chats {
|
||||||
|
title
|
||||||
|
description
|
||||||
|
updatedAt
|
||||||
|
messages {
|
||||||
|
id
|
||||||
|
author
|
||||||
|
body
|
||||||
|
replyTo
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
users {
|
||||||
|
slug
|
||||||
|
name
|
||||||
|
userpic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -1,13 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query {
|
|
||||||
getMyCollections {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
desc
|
|
||||||
slug
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query CollectionsUserQuery($slug: String!) {
|
|
||||||
getUserCollections(user: $slug) {
|
|
||||||
id
|
|
||||||
title
|
|
||||||
desc
|
|
||||||
slug
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query GetChatsQuery {
|
|
||||||
myChats {
|
|
||||||
messages {
|
|
||||||
chatId
|
|
||||||
id
|
|
||||||
author
|
|
||||||
body
|
|
||||||
replyTo
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
users {
|
|
||||||
slug
|
|
||||||
name
|
|
||||||
pic
|
|
||||||
}
|
|
||||||
title
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { gql } from '@urql/core'
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
query ReactionsForShoutsQuery($shouts: [String]!, $limit: Int!, $offset: Int!) {
|
query LoadReactionsByQuery($by: ReactionsBy, $limit: Int!, $offset: Int!) {
|
||||||
reactionsForShouts(shouts: $shouts, limit: $limit, offset: $offset) {
|
loadReactionsBy(by: $by, limit: $limit, offset: $offset) {
|
||||||
id
|
id
|
||||||
createdBy {
|
createdBy {
|
||||||
slug
|
slug
|
|
@ -1,42 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query SearchResultsQuery($q: String!, $limit: Int!, $offset: Int!) {
|
|
||||||
searchQuery(q: $q, limit: $limit, offset: $offset) {
|
|
||||||
_id: slug
|
|
||||||
title
|
|
||||||
subtitle
|
|
||||||
layout
|
|
||||||
slug
|
|
||||||
cover
|
|
||||||
# community
|
|
||||||
mainTopic
|
|
||||||
topics {
|
|
||||||
title
|
|
||||||
body
|
|
||||||
slug
|
|
||||||
stat {
|
|
||||||
_id: shouts
|
|
||||||
shouts
|
|
||||||
authors
|
|
||||||
followers
|
|
||||||
}
|
|
||||||
}
|
|
||||||
authors {
|
|
||||||
_id: slug
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
userpic
|
|
||||||
}
|
|
||||||
createdAt
|
|
||||||
publishedAt
|
|
||||||
stat {
|
|
||||||
_id: viewed
|
|
||||||
viewed
|
|
||||||
reacted
|
|
||||||
rating
|
|
||||||
commented
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -41,6 +41,17 @@ export type AuthorStat = {
|
||||||
rating?: Maybe<Scalars['Int']>
|
rating?: Maybe<Scalars['Int']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AuthorsBy = {
|
||||||
|
createdAt?: InputMaybe<Scalars['DateTime']>
|
||||||
|
days?: InputMaybe<Scalars['Int']>
|
||||||
|
lastSeen?: InputMaybe<Scalars['DateTime']>
|
||||||
|
name?: InputMaybe<Scalars['String']>
|
||||||
|
order?: InputMaybe<Scalars['String']>
|
||||||
|
slug?: InputMaybe<Scalars['String']>
|
||||||
|
stat?: InputMaybe<Scalars['String']>
|
||||||
|
topic?: InputMaybe<Scalars['String']>
|
||||||
|
}
|
||||||
|
|
||||||
export type Chat = {
|
export type Chat = {
|
||||||
admins?: Maybe<Array<Maybe<User>>>
|
admins?: Maybe<Array<Maybe<User>>>
|
||||||
createdAt: Scalars['Int']
|
createdAt: Scalars['Int']
|
||||||
|
@ -88,12 +99,6 @@ export type Collection = {
|
||||||
title: Scalars['String']
|
title: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CollectionInput = {
|
|
||||||
desc?: InputMaybe<Scalars['String']>
|
|
||||||
pic?: InputMaybe<Scalars['String']>
|
|
||||||
title: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Community = {
|
export type Community = {
|
||||||
createdAt: Scalars['DateTime']
|
createdAt: Scalars['DateTime']
|
||||||
createdBy: User
|
createdBy: User
|
||||||
|
@ -104,12 +109,6 @@ export type Community = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommunityInput = {
|
|
||||||
desc?: InputMaybe<Scalars['String']>
|
|
||||||
pic?: InputMaybe<Scalars['String']>
|
|
||||||
title: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum FollowingEntity {
|
export enum FollowingEntity {
|
||||||
Author = 'AUTHOR',
|
Author = 'AUTHOR',
|
||||||
Community = 'COMMUNITY',
|
Community = 'COMMUNITY',
|
||||||
|
@ -133,25 +132,28 @@ export enum MessageStatus {
|
||||||
Updated = 'UPDATED'
|
Updated = 'UPDATED'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MessagesBy = {
|
||||||
|
author?: InputMaybe<Scalars['String']>
|
||||||
|
body?: InputMaybe<Scalars['String']>
|
||||||
|
chat?: InputMaybe<Scalars['String']>
|
||||||
|
days?: InputMaybe<Scalars['Int']>
|
||||||
|
order?: InputMaybe<Scalars['String']>
|
||||||
|
stat?: InputMaybe<Scalars['String']>
|
||||||
|
}
|
||||||
|
|
||||||
export type Mutation = {
|
export type Mutation = {
|
||||||
confirmEmail: AuthResult
|
confirmEmail: AuthResult
|
||||||
createChat: Result
|
createChat: Result
|
||||||
createCollection: Result
|
|
||||||
createCommunity: Result
|
|
||||||
createMessage: Result
|
createMessage: Result
|
||||||
createReaction: Result
|
createReaction: Result
|
||||||
createShout: Result
|
createShout: Result
|
||||||
createTopic: Result
|
createTopic: Result
|
||||||
deleteChat: Result
|
deleteChat: Result
|
||||||
deleteCollection: Result
|
|
||||||
deleteCommunity: Result
|
|
||||||
deleteMessage: Result
|
deleteMessage: Result
|
||||||
deleteReaction: Result
|
deleteReaction: Result
|
||||||
deleteShout: Result
|
deleteShout: Result
|
||||||
destroyTopic: Result
|
destroyTopic: Result
|
||||||
enterChat: Result
|
|
||||||
follow: Result
|
follow: Result
|
||||||
incrementView: Result
|
|
||||||
inviteAuthor: Result
|
inviteAuthor: Result
|
||||||
inviteChat: Result
|
inviteChat: Result
|
||||||
markAsRead: Result
|
markAsRead: Result
|
||||||
|
@ -162,9 +164,8 @@ export type Mutation = {
|
||||||
sendLink: Result
|
sendLink: Result
|
||||||
unfollow: Result
|
unfollow: Result
|
||||||
updateChat: Result
|
updateChat: Result
|
||||||
updateCollection: Result
|
|
||||||
updateCommunity: Result
|
|
||||||
updateMessage: Result
|
updateMessage: Result
|
||||||
|
updateOnlineStatus: Result
|
||||||
updateProfile: Result
|
updateProfile: Result
|
||||||
updateReaction: Result
|
updateReaction: Result
|
||||||
updateShout: Result
|
updateShout: Result
|
||||||
|
@ -180,14 +181,6 @@ export type MutationCreateChatArgs = {
|
||||||
title?: InputMaybe<Scalars['String']>
|
title?: InputMaybe<Scalars['String']>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationCreateCollectionArgs = {
|
|
||||||
collection: CollectionInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationCreateCommunityArgs = {
|
|
||||||
community: CommunityInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationCreateMessageArgs = {
|
export type MutationCreateMessageArgs = {
|
||||||
body: Scalars['String']
|
body: Scalars['String']
|
||||||
chatId: Scalars['String']
|
chatId: Scalars['String']
|
||||||
|
@ -210,14 +203,6 @@ export type MutationDeleteChatArgs = {
|
||||||
chatId: Scalars['String']
|
chatId: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationDeleteCollectionArgs = {
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationDeleteCommunityArgs = {
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationDeleteMessageArgs = {
|
export type MutationDeleteMessageArgs = {
|
||||||
chatId: Scalars['String']
|
chatId: Scalars['String']
|
||||||
id: Scalars['Int']
|
id: Scalars['Int']
|
||||||
|
@ -235,19 +220,11 @@ export type MutationDestroyTopicArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationEnterChatArgs = {
|
|
||||||
chatId: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationFollowArgs = {
|
export type MutationFollowArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
what: FollowingEntity
|
what: FollowingEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationIncrementViewArgs = {
|
|
||||||
shout: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationInviteAuthorArgs = {
|
export type MutationInviteAuthorArgs = {
|
||||||
author: Scalars['String']
|
author: Scalars['String']
|
||||||
shout: Scalars['String']
|
shout: Scalars['String']
|
||||||
|
@ -293,14 +270,6 @@ export type MutationUpdateChatArgs = {
|
||||||
chat: ChatInput
|
chat: ChatInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MutationUpdateCollectionArgs = {
|
|
||||||
collection: CollectionInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationUpdateCommunityArgs = {
|
|
||||||
community: CommunityInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MutationUpdateMessageArgs = {
|
export type MutationUpdateMessageArgs = {
|
||||||
body: Scalars['String']
|
body: Scalars['String']
|
||||||
chatId: Scalars['String']
|
chatId: Scalars['String']
|
||||||
|
@ -348,98 +317,66 @@ export type ProfileInput = {
|
||||||
|
|
||||||
export type Query = {
|
export type Query = {
|
||||||
authorsAll: Array<Maybe<Author>>
|
authorsAll: Array<Maybe<Author>>
|
||||||
collectionsAll: Array<Maybe<Collection>>
|
|
||||||
getAuthor: User
|
getAuthor: User
|
||||||
getCollabs: Array<Maybe<Collab>>
|
getCollabs: Array<Maybe<Collab>>
|
||||||
getCommunities: Array<Maybe<Community>>
|
|
||||||
getCommunity: Community
|
|
||||||
getShoutBySlug: Shout
|
|
||||||
getTopic: Topic
|
getTopic: Topic
|
||||||
getUserCollections: Array<Maybe<Collection>>
|
|
||||||
getUserRoles: Array<Maybe<Role>>
|
|
||||||
getUsersBySlugs: Array<Maybe<Author>>
|
|
||||||
isEmailUsed: Scalars['Boolean']
|
isEmailUsed: Scalars['Boolean']
|
||||||
|
loadAuthorsBy: Array<Maybe<Author>>
|
||||||
loadChats: Result
|
loadChats: Result
|
||||||
loadMessages: Result
|
loadMessagesBy: Result
|
||||||
|
loadReactionsBy: Array<Maybe<Reaction>>
|
||||||
|
loadShoutsBy: Array<Maybe<Shout>>
|
||||||
markdownBody: Scalars['String']
|
markdownBody: Scalars['String']
|
||||||
reactionsByAuthor: Array<Maybe<Reaction>>
|
|
||||||
reactionsForShouts: Array<Maybe<Reaction>>
|
|
||||||
recentAll: Array<Maybe<Shout>>
|
|
||||||
recentCandidates: Array<Maybe<Shout>>
|
|
||||||
recentCommented: Array<Maybe<Shout>>
|
|
||||||
recentLayoutShouts: Array<Maybe<Shout>>
|
|
||||||
recentPublished: Array<Maybe<Shout>>
|
|
||||||
recentReacted: Array<Maybe<Shout>>
|
|
||||||
searchChats: Result
|
|
||||||
searchMessages: Result
|
|
||||||
searchQuery?: Maybe<Array<Maybe<Shout>>>
|
|
||||||
searchUsers: Result
|
searchUsers: Result
|
||||||
shoutsByAuthors: Array<Maybe<Shout>>
|
|
||||||
shoutsByCollection: Array<Maybe<Shout>>
|
|
||||||
shoutsByCommunities: Array<Maybe<Shout>>
|
|
||||||
shoutsByLayout: Array<Maybe<Shout>>
|
|
||||||
shoutsByTopics: Array<Maybe<Shout>>
|
|
||||||
shoutsForFeed: Array<Maybe<Shout>>
|
|
||||||
signIn: AuthResult
|
signIn: AuthResult
|
||||||
signOut: AuthResult
|
signOut: AuthResult
|
||||||
topAuthors: Array<Maybe<Author>>
|
|
||||||
topCommented: Array<Maybe<Shout>>
|
|
||||||
topLayoutShouts: Array<Maybe<Shout>>
|
|
||||||
topMonth: Array<Maybe<Shout>>
|
|
||||||
topMonthLayoutShouts: Array<Maybe<Shout>>
|
|
||||||
topOverall: Array<Maybe<Shout>>
|
|
||||||
topPublished: Array<Maybe<Shout>>
|
|
||||||
topicsAll: Array<Maybe<Topic>>
|
topicsAll: Array<Maybe<Topic>>
|
||||||
topicsByAuthor: Array<Maybe<Topic>>
|
topicsByAuthor: Array<Maybe<Topic>>
|
||||||
topicsByCommunity: Array<Maybe<Topic>>
|
topicsByCommunity: Array<Maybe<Topic>>
|
||||||
topicsRandom: Array<Maybe<Topic>>
|
topicsRandom: Array<Maybe<Topic>>
|
||||||
userFollowedAuthors: Array<Maybe<Author>>
|
userFollowedAuthors: Array<Maybe<Author>>
|
||||||
userFollowedCommunities: Array<Maybe<Community>>
|
|
||||||
userFollowedTopics: Array<Maybe<Topic>>
|
userFollowedTopics: Array<Maybe<Topic>>
|
||||||
userFollowers: Array<Maybe<Author>>
|
userFollowers: Array<Maybe<Author>>
|
||||||
userReactedShouts: Array<Maybe<Shout>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryGetAuthorArgs = {
|
export type QueryGetAuthorArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryGetCommunityArgs = {
|
|
||||||
slug?: InputMaybe<Scalars['String']>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryGetShoutBySlugArgs = {
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryGetTopicArgs = {
|
export type QueryGetTopicArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryGetUserCollectionsArgs = {
|
|
||||||
author: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryGetUserRolesArgs = {
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryGetUsersBySlugsArgs = {
|
|
||||||
slugs: Array<InputMaybe<Scalars['String']>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryIsEmailUsedArgs = {
|
export type QueryIsEmailUsedArgs = {
|
||||||
email: Scalars['String']
|
email: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryLoadChatsArgs = {
|
export type QueryLoadAuthorsByArgs = {
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
by?: InputMaybe<AuthorsBy>
|
||||||
|
limit?: InputMaybe<Scalars['Int']>
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
offset?: InputMaybe<Scalars['Int']>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryLoadMessagesArgs = {
|
export type QueryLoadChatsArgs = {
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
limit?: InputMaybe<Scalars['Int']>
|
||||||
chatId: Scalars['String']
|
offset?: InputMaybe<Scalars['Int']>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QueryLoadMessagesByArgs = {
|
||||||
|
by: MessagesBy
|
||||||
|
limit?: InputMaybe<Scalars['Int']>
|
||||||
|
offset?: InputMaybe<Scalars['Int']>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QueryLoadReactionsByArgs = {
|
||||||
|
by: ReactionBy
|
||||||
|
limit?: InputMaybe<Scalars['Int']>
|
||||||
|
offset?: InputMaybe<Scalars['Int']>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type QueryLoadShoutsByArgs = {
|
||||||
|
by?: InputMaybe<ShoutsBy>
|
||||||
|
limit?: InputMaybe<Scalars['Int']>
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
offset?: InputMaybe<Scalars['Int']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,106 +384,10 @@ export type QueryMarkdownBodyArgs = {
|
||||||
body: Scalars['String']
|
body: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryReactionsByAuthorArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryReactionsForShoutsArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
shouts: Array<InputMaybe<Scalars['String']>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryRecentAllArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryRecentCandidatesArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryRecentCommentedArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryRecentLayoutShoutsArgs = {
|
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
|
||||||
layout: Scalars['String']
|
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryRecentPublishedArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryRecentReactedArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuerySearchChatsArgs = {
|
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
|
||||||
q: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuerySearchMessagesArgs = {
|
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
|
||||||
q: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuerySearchQueryArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
q?: InputMaybe<Scalars['String']>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QuerySearchUsersArgs = {
|
export type QuerySearchUsersArgs = {
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
limit?: InputMaybe<Scalars['Int']>
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
offset?: InputMaybe<Scalars['Int']>
|
||||||
q: Scalars['String']
|
query: Scalars['String']
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryShoutsByAuthorsArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
slugs: Array<InputMaybe<Scalars['String']>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryShoutsByCollectionArgs = {
|
|
||||||
collection: Scalars['String']
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryShoutsByCommunitiesArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
slugs: Array<InputMaybe<Scalars['String']>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryShoutsByLayoutArgs = {
|
|
||||||
amount: Scalars['Int']
|
|
||||||
layout?: InputMaybe<Scalars['String']>
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryShoutsByTopicsArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
slugs: Array<InputMaybe<Scalars['String']>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryShoutsForFeedArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QuerySignInArgs = {
|
export type QuerySignInArgs = {
|
||||||
|
@ -555,44 +396,6 @@ export type QuerySignInArgs = {
|
||||||
password?: InputMaybe<Scalars['String']>
|
password?: InputMaybe<Scalars['String']>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryTopAuthorsArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopCommentedArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopLayoutShoutsArgs = {
|
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
|
||||||
layout: Scalars['String']
|
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopMonthArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopMonthLayoutShoutsArgs = {
|
|
||||||
amount?: InputMaybe<Scalars['Int']>
|
|
||||||
layout: Scalars['String']
|
|
||||||
offset?: InputMaybe<Scalars['Int']>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopOverallArgs = {
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopPublishedArgs = {
|
|
||||||
daysago: Scalars['Int']
|
|
||||||
limit: Scalars['Int']
|
|
||||||
offset: Scalars['Int']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryTopicsByAuthorArgs = {
|
export type QueryTopicsByAuthorArgs = {
|
||||||
author: Scalars['String']
|
author: Scalars['String']
|
||||||
}
|
}
|
||||||
|
@ -609,10 +412,6 @@ export type QueryUserFollowedAuthorsArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryUserFollowedCommunitiesArgs = {
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type QueryUserFollowedTopicsArgs = {
|
export type QueryUserFollowedTopicsArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
@ -621,10 +420,6 @@ export type QueryUserFollowersArgs = {
|
||||||
slug: Scalars['String']
|
slug: Scalars['String']
|
||||||
}
|
}
|
||||||
|
|
||||||
export type QueryUserReactedShoutsArgs = {
|
|
||||||
slug: Scalars['String']
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Rating = {
|
export type Rating = {
|
||||||
rater: Scalars['String']
|
rater: Scalars['String']
|
||||||
value: Scalars['Int']
|
value: Scalars['Int']
|
||||||
|
@ -647,6 +442,17 @@ export type Reaction = {
|
||||||
updatedAt?: Maybe<Scalars['DateTime']>
|
updatedAt?: Maybe<Scalars['DateTime']>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ReactionBy = {
|
||||||
|
author?: InputMaybe<Scalars['String']>
|
||||||
|
body?: InputMaybe<Scalars['String']>
|
||||||
|
days?: InputMaybe<Scalars['Int']>
|
||||||
|
order?: InputMaybe<Scalars['String']>
|
||||||
|
shout?: InputMaybe<Scalars['String']>
|
||||||
|
shouts?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
|
||||||
|
stat?: InputMaybe<Scalars['String']>
|
||||||
|
topic?: InputMaybe<Scalars['String']>
|
||||||
|
}
|
||||||
|
|
||||||
export type ReactionInput = {
|
export type ReactionInput = {
|
||||||
body?: InputMaybe<Scalars['String']>
|
body?: InputMaybe<Scalars['String']>
|
||||||
kind: Scalars['Int']
|
kind: Scalars['Int']
|
||||||
|
@ -757,6 +563,20 @@ export type ShoutInput = {
|
||||||
visibleForUsers?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
|
visibleForUsers?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ShoutsBy = {
|
||||||
|
author?: InputMaybe<Scalars['String']>
|
||||||
|
body?: InputMaybe<Scalars['String']>
|
||||||
|
days?: InputMaybe<Scalars['Int']>
|
||||||
|
layout?: InputMaybe<Scalars['String']>
|
||||||
|
order?: InputMaybe<Scalars['String']>
|
||||||
|
slug?: InputMaybe<Scalars['String']>
|
||||||
|
stat?: InputMaybe<Scalars['String']>
|
||||||
|
title?: InputMaybe<Scalars['String']>
|
||||||
|
topic?: InputMaybe<Scalars['String']>
|
||||||
|
topics?: InputMaybe<Array<InputMaybe<Scalars['String']>>>
|
||||||
|
visibility?: InputMaybe<Scalars['String']>
|
||||||
|
}
|
||||||
|
|
||||||
export type Stat = {
|
export type Stat = {
|
||||||
commented?: Maybe<Scalars['Int']>
|
commented?: Maybe<Scalars['Int']>
|
||||||
ranking?: Maybe<Scalars['Int']>
|
ranking?: Maybe<Scalars['Int']>
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
---
|
|
||||||
import { setLocale } from '../stores/ui';
|
|
||||||
import '../styles/app.scss'
|
|
||||||
import { t } from '../utils/intl'
|
|
||||||
|
|
||||||
const lang = Astro.url.searchParams.get('lang') || 'ru'
|
|
||||||
console.log('[layout] server locale is', lang)
|
|
||||||
setLocale(lang)
|
|
||||||
|
|
||||||
---
|
|
||||||
<html lang={lang || 'ru'}>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
||||||
<title>{t('Discours')}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<slot />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,10 @@
|
||||||
"Link sent, check your email": "Ссылка отправлена, проверьте почту",
|
"Link sent, check your email": "Ссылка отправлена, проверьте почту",
|
||||||
"Create post": "Создать публикацию",
|
"Create post": "Создать публикацию",
|
||||||
"Just start typing...": "Просто начните печатать...",
|
"Just start typing...": "Просто начните печатать...",
|
||||||
|
"Artworks": "Артворки",
|
||||||
|
"Audio": "Аудио",
|
||||||
|
"Video": "Видео",
|
||||||
|
"Literature": "Литература",
|
||||||
"We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или",
|
"We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или",
|
||||||
"register": "зарегистрируйтесь"
|
"register": "зарегистрируйтесь"
|
||||||
}
|
}
|
||||||
|
|
30
src/main.astro
Normal file
30
src/main.astro
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
import { setLocale } from './stores/ui'
|
||||||
|
import './styles/app.scss'
|
||||||
|
import { t } from './utils/intl'
|
||||||
|
|
||||||
|
// Setting locale for prerendered content here
|
||||||
|
|
||||||
|
const lang = Astro.url.searchParams.get('lang') || 'ru'
|
||||||
|
console.log('[main.astro] astro runtime locale is', lang)
|
||||||
|
setLocale(lang)
|
||||||
|
|
||||||
|
---
|
||||||
|
<html lang={lang || 'ru'}>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
||||||
|
<title>{t('Discours')}</title>
|
||||||
|
<script async
|
||||||
|
src="https://ackee.discours.io/increment.js"
|
||||||
|
data-ackee-server="https://ackee.discours.io"
|
||||||
|
data-ackee-domain-id="1004abeb-89b2-4e85-ad97-74f8d2c8ed2d"
|
||||||
|
></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
import Zine from '../layouts/zine.astro'
|
import Prerendered from '../main.astro'
|
||||||
import { Root } from '../components/Root'
|
import { Root } from '../components/Root'
|
||||||
import { initRouter } from '../stores/router'
|
import { initRouter } from '../stores/router'
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ initRouter(pathname, search)
|
||||||
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
|
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Prerendered>
|
||||||
<Root client:load />
|
<Root client:load />
|
||||||
</Zine>
|
</Prerendered>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
import { Root } from '../components/Root'
|
import { Root } from '../components/Root'
|
||||||
import Zine from '../layouts/zine.astro'
|
import Prerendered from '../main.astro'
|
||||||
import { apiClient } from '../utils/apiClient'
|
import { apiClient } from '../utils/apiClient'
|
||||||
import { initRouter } from '../stores/router'
|
import { initRouter } from '../stores/router'
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ if (slug.endsWith('.map')) {
|
||||||
return Astro.redirect('/404')
|
return Astro.redirect('/404')
|
||||||
}
|
}
|
||||||
|
|
||||||
const article = await apiClient.getArticle({ slug })
|
const article = await apiClient.loadShoutsBy({ by: { slug }, limit: 1})
|
||||||
if (!article) {
|
if (!article) {
|
||||||
return Astro.redirect('/404')
|
return Astro.redirect('/404')
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,6 @@ initRouter(pathname, search)
|
||||||
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
|
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
|
||||||
---
|
---
|
||||||
|
|
||||||
<Zine>
|
<Prerendered>
|
||||||
<Root article={article} client:load />
|
<Root article={article.at(0)} client:load />
|
||||||
</Zine>
|
</Prerendered>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user