Merge remote-tracking branch 'origin/layouts' into prepare-inbox

This commit is contained in:
ilya-bkv 2022-11-16 15:26:44 +03:00
commit f3eccb1ff2
131 changed files with 12116 additions and 11636 deletions

View File

@ -3,5 +3,6 @@ public
*.cjs
src/graphql/*.gen.ts
src/legacy_*
src/components/EditorExample
dist/
.vercel/

View File

@ -74,6 +74,8 @@ module.exports = {
'unicorn/numeric-separators-style': 'off',
'unicorn/prefer-node-protocol': 'off',
'promise/always-return': 'off',
eqeqeq: 'error',
'no-param-reassign': 'error',
'no-nested-ternary': 'error',

View File

@ -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]
[+] 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]
[+] nanostores-base global store

View File

@ -1,7 +1,7 @@
import { defineConfig, AstroUserConfig } from 'astro/config'
import vercel from '@astrojs/vercel/serverless'
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 { isDev } from './src/utils/config'
import { visualizer } from 'rollup-plugin-visualizer'
@ -37,6 +37,7 @@ const astroConfig: AstroUserConfig = {
adapter: vercel(),
vite: {
build: {
chunkSizeWarningLimit: 777,
rollupOptions: {
plugins: [visualizer()],
output: {

View File

@ -1,5 +1,5 @@
overwrite: true
schema: 'https://newapi.discours.io/graphql'
schema: 'https://testapi.discours.io/graphql'
generates:
src/graphql/introspec.gen.ts:
plugins:

View File

@ -1,6 +1,6 @@
{
"name": "discoursio-webapp",
"version": "0.5.1",
"version": "0.6.1",
"private": true,
"license": "MIT",
"scripts": {
@ -30,113 +30,113 @@
"vercel-build": "astro build"
},
"dependencies": {
"mailgun.js": "^8.0.1"
"mailgun.js": "^8.0.2"
},
"devDependencies": {
"@astrojs/solid-js": "^1.1.0",
"@astrojs/vercel": "^2.1.0",
"@babel/core": "^7.18.13",
"@graphql-codegen/cli": "^2.12.1",
"@graphql-codegen/typescript": "^2.7.3",
"@graphql-codegen/typescript-operations": "^2.5.3",
"@graphql-codegen/typescript-urql": "^3.7.0",
"@astrojs/solid-js": "^1.2.3",
"@astrojs/vercel": "^2.3.3",
"@babel/core": "^7.20.2",
"@graphql-codegen/cli": "^2.13.12",
"@graphql-codegen/typescript": "^2.8.2",
"@graphql-codegen/typescript-operations": "^2.5.7",
"@graphql-codegen/typescript-urql": "^3.7.3",
"@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",
"@nanostores/persistent": "^0.7.0",
"@nanostores/router": "^0.7.0",
"@nanostores/solid": "^0.3.0",
"@popperjs/core": "^2.11.6",
"@solid-devtools/debugger": "^0.13.1",
"@solid-devtools/logger": "^0.4.9",
"@solid-primitives/memo": "^1.0.2",
"@solid-devtools/debugger": "^0.14.0",
"@solid-devtools/logger": "^0.5.0",
"@solid-primitives/memo": "^1.1.2",
"@solid-primitives/storage": "^1.3.3",
"@types/express": "^4.17.14",
"@types/node": "^18.7.19",
"@types/node": "^18.11.9",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.29.0",
"@typescript-eslint/parser": "^5.35.1",
"@urql/core": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"@urql/core": "^3.0.5",
"@urql/devtools": "^2.0.3",
"@urql/exchange-auth": "^1.0.0",
"@urql/exchange-graphcache": "^5.0.0",
"astro": "^1.1.1",
"@urql/exchange-graphcache": "^5.0.5",
"astro": "^1.6.8",
"astro-eslint-parser": "^0.9.0",
"bcryptjs": "^2.4.3",
"bootstrap": "5.1.3",
"clsx": "^1.2.1",
"cookie": "^0.5.0",
"cookie-signature": "^1.2.0",
"eslint": "^8.26.0",
"eslint": "^8.27.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-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-promise": "^6.0.1",
"eslint-plugin-solid": "^0.7.3",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-solid": "^0.8.0",
"eslint-plugin-sonarjs": "^0.16.0",
"eslint-plugin-unicorn": "^44.0.2",
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"graphql-ws": "^5.11.2",
"hast-util-select": "^5.0.2",
"husky": "^8.0.1",
"idb": "^7.1.0",
"jest": "^29.2.1",
"husky": "^8.0.2",
"idb": "^7.1.1",
"jest": "^29.3.1",
"lint-staged": "^13.0.3",
"loglevel": "^1.8.0",
"loglevel": "^1.8.1",
"loglevel-plugin-prefix": "^0.8.4",
"markdown-it": "^13.0.1",
"markdown-it-container": "^3.0.0",
"markdown-it-implicit-figures": "^0.10.0",
"markdown-it-mark": "^3.0.1",
"markdown-it-replace-link": "^1.1.0",
"nanostores": "^0.7.0",
"nanostores": "^0.7.1",
"orderedmap": "^2.1.0",
"postcss": "^8.4.16",
"postcss-modules": "^5.0.0",
"postcss": "^8.4.19",
"postcss-modules": "5.0.0",
"prettier": "^2.7.1",
"prettier-eslint": "^15.0.1",
"prosemirror-commands": "^1.3.1",
"prosemirror-dropcursor": "^1.6.0",
"prosemirror-dropcursor": "^1.6.1",
"prosemirror-example-setup": "^1.2.1",
"prosemirror-gapcursor": "^1.3.1",
"prosemirror-history": "^1.3.0",
"prosemirror-inputrules": "^1.2.0",
"prosemirror-keymap": "^1.2.0",
"prosemirror-markdown": "^1.9.4",
"prosemirror-markdown": "^1.10.1",
"prosemirror-menu": "^1.2.1",
"prosemirror-model": "^1.16.0",
"prosemirror-model": "^1.18.2",
"prosemirror-schema-list": "^1.2.2",
"prosemirror-state": "^1.4.1",
"prosemirror-view": "^1.28.1",
"rollup": "~2.79.1",
"rollup-plugin-visualizer": "^5.8.2",
"sass": "^1.55.0",
"solid-devtools": "^0.20.1",
"solid-js": "^1.6.0",
"prosemirror-state": "^1.4.2",
"prosemirror-view": "^1.29.1",
"rollup": "^2.79.1",
"rollup-plugin-visualizer": "^5.8.3",
"sass": "^1.56.1",
"solid-devtools": "^0.22.0",
"solid-js": "^1.6.2",
"solid-js-form": "^0.1.5",
"solid-jsx": "^0.9.1",
"solid-social": "^0.9.0",
"solid-utils": "^0.8.1",
"sort-package-json": "^2.0.0",
"stylelint": "^14.12.1",
"sort-package-json": "^2.1.0",
"stylelint": "^14.15.0",
"stylelint-config-css-modules": "^4.1.0",
"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-scss": "^4.3.0",
"swiper": "^8.4.2",
"swiper": "^8.4.4",
"ts-node": "^10.9.1",
"typescript": "^4.8.3",
"undici": "^5.10.0",
"typescript": "^4.8.4",
"undici": "^5.12.0",
"unique-names-generator": "^4.7.1",
"uuid": "^9.0.0",
"vite": "^3.1.3",
"ws": "^8.9.0",
"vite": "^3.2.4",
"ws": "^8.11.0",
"y-prosemirror": "^1.2.0",
"y-protocols": "^1.0.5",
"y-webrtc": "^10.2.3",
"yjs": "^13.5.41"
"yjs": "^13.5.42"
}
}

10965
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

4
public/icons/audio.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M11.1853 7.68061L11.1746 2.64865L5.05266 3.68025C5.04861 8.28981 5.04659 10.9099 5.04659 11.5405L5.04077 11.5326C4.95203 12.9109 3.85883 14 2.52329 14C1.12972 14 0 12.8142 0 11.3514C0 9.88854 1.12972 8.7027 2.52329 8.7027C2.77381 8.7027 3.0158 8.74102 3.24423 8.81239V1.7027L12.9769 0C13.0077 6.85087 13.0077 10.3193 12.9769 10.4054L12.9711 10.398C12.8821 11.776 11.789 12.8649 10.4536 12.8649C9.06007 12.8649 7.93035 11.679 7.93035 10.2162C7.93035 8.75341 9.06007 7.56757 10.4536 7.56757C10.7081 7.56757 10.9537 7.60709 11.1853 7.68061Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 675 B

View File

@ -1,3 +1,3 @@
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.71176 2.49041L8.40551 2.26722V1.55186C8.4056 1.55036 8.40572 1.54846 8.40589 1.54616C8.40697 1.53163 8.40964 1.50407 8.41583 1.46825C8.42899 1.39201 8.4535 1.30486 8.49268 1.22923C8.52946 1.15823 8.57081 1.11241 8.61909 1.08135C8.6628 1.05322 8.7689 1 8.99954 1C9.23018 1 9.33628 1.05322 9.38 1.08135C9.42827 1.11241 9.46962 1.15823 9.5064 1.22923C9.54558 1.30486 9.57009 1.39201 9.58325 1.46825C9.58944 1.50407 9.59211 1.53163 9.59319 1.54616C9.59336 1.54846 9.59348 1.55036 9.59358 1.55186V2.26722L10.2873 2.49041C12.7482 3.28211 14.3757 5.49002 14.3757 7.94197V12.8672C14.3757 13.986 14.82 14.759 15.3994 15.377C15.6695 15.665 15.9739 15.925 16.2499 16.1586C16.2731 16.1783 16.2962 16.1978 16.3192 16.2172C16.5518 16.4139 16.7728 16.6007 16.991 16.8057C16.9922 16.8139 16.9934 16.8231 16.9946 16.8335C17.0069 16.9472 16.999 17.1091 16.962 17.2772C16.9459 17.3502 16.9271 17.4118 16.9087 17.4615H10.5936H9.59358V18.4481C9.59348 18.4496 9.59336 18.4515 9.59319 18.4538C9.59211 18.4684 9.58944 18.4959 9.58325 18.5318C9.57009 18.608 9.54558 18.6951 9.5064 18.7708C9.46962 18.8418 9.42827 18.8876 9.38 18.9187C9.33628 18.9468 9.23018 19 8.99954 19C8.7689 19 8.6628 18.9468 8.61909 18.9187C8.57081 18.8876 8.52946 18.8418 8.49268 18.7708C8.4535 18.6951 8.42899 18.608 8.41583 18.5318C8.40964 18.4959 8.40697 18.4684 8.40589 18.4538C8.40572 18.4515 8.4056 18.4496 8.40551 18.4481V17.4615H7.40551H1.09134C1.07295 17.4118 1.05414 17.3502 1.03802 17.2772C1.00095 17.1091 0.993067 16.9472 1.00542 16.8335C1.00655 16.8231 1.00778 16.8139 1.00901 16.8057C1.22708 16.6008 1.44781 16.4142 1.6802 16.2176C1.70337 16.198 1.72666 16.1783 1.75007 16.1585C2.02607 15.9249 2.33033 15.6649 2.60028 15.3768C3.17948 14.7587 3.62341 13.9857 3.62341 12.8672V7.94197C3.62341 5.49001 5.25088 3.28211 7.71176 2.49041ZM16.8354 17.6091C16.8354 17.609 16.8361 17.608 16.8377 17.6061C16.8362 17.6082 16.8354 17.6091 16.8354 17.6091ZM1.16462 17.6091C1.16462 17.6091 1.16384 17.6082 1.16228 17.6061C1.16385 17.608 1.16463 17.609 1.16462 17.6091Z" stroke="black" stroke-width="2"/>
<svg width="17" height="18" viewBox="0 0 17 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.97121 2.33657L7.66496 2.11338V1.39995C7.66498 1.39963 7.665 1.39929 7.66503 1.39893C7.66579 1.3886 7.66785 1.36706 7.67279 1.33844C7.68346 1.27662 7.70299 1.20871 7.73221 1.1523C7.75902 1.10054 7.78543 1.07394 7.81129 1.05731C7.83259 1.0436 7.9088 1 8.09959 1C8.29037 1 8.36659 1.0436 8.38789 1.05731C8.41374 1.07394 8.44015 1.10054 8.46697 1.1523C8.49619 1.20871 8.51571 1.27662 8.52639 1.33844C8.53133 1.36706 8.53338 1.3886 8.53414 1.39893C8.53417 1.39929 8.53419 1.39963 8.53422 1.39995V2.11338L9.22796 2.33657C11.4033 3.03639 12.8381 4.98635 12.8381 7.14777V11.5805C12.8381 12.6186 13.2522 13.3378 13.7865 13.9077C14.0341 14.1717 14.3122 14.4092 14.5603 14.6191C14.5811 14.6367 14.6017 14.6541 14.6223 14.6715C14.8226 14.8408 15.0112 15.0003 15.1969 15.1732C15.2049 15.2629 15.1981 15.3919 15.1681 15.5279C15.1613 15.5589 15.1537 15.5882 15.1456 15.6154H9.53422H8.53422V16.6C8.53419 16.6004 8.53417 16.6007 8.53414 16.6011C8.53338 16.6114 8.53133 16.6329 8.52639 16.6616C8.51571 16.7234 8.49619 16.7913 8.46697 16.8477C8.44015 16.8995 8.41374 16.9261 8.38789 16.9427C8.36659 16.9564 8.29037 17 8.09959 17C7.9088 17 7.83259 16.9564 7.81129 16.9427C7.78543 16.9261 7.75902 16.8995 7.73221 16.8477C7.70299 16.7913 7.68346 16.7234 7.67279 16.6616C7.66785 16.6329 7.66579 16.6114 7.66503 16.6011C7.665 16.6007 7.66498 16.6004 7.66496 16.6V15.6154H6.66496H1.05437C1.04632 15.5882 1.03872 15.5589 1.03187 15.5279C1.00187 15.3919 0.995103 15.2629 1.00309 15.1732C1.18866 15.0004 1.37707 14.8411 1.57711 14.6719C1.59784 14.6544 1.61869 14.6368 1.63967 14.619C1.88772 14.409 2.16578 14.1715 2.41322 13.9075C2.9474 13.3375 3.36106 12.6184 3.36106 11.5805V7.14777C3.36106 4.98635 4.79591 3.03639 6.97121 2.33657ZM1.12345 15.7819C1.12341 15.7818 1.12338 15.7818 1.12334 15.7817L1.12345 15.7819Z" stroke="black" stroke-width="2"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<svg width="12" height="18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3.969 1.688l6.181 2.18v12.88H12V2.722L4.2 0 0 1.47v13.668L7.436 18H8.55V4.532L2.147 2.265l1.822-.577z" fill="currentColor"/></svg>

After

Width:  |  Height:  |  Size: 216 B

View 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>
)
}

View File

@ -1,5 +1,5 @@
import './Comment.scss'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { AuthorCard } from '../Author/Card'
import { Show, createMemo } from 'solid-js'
import { clsx } from 'clsx'

View File

@ -1,13 +1,12 @@
import { capitalize } from '../../utils'
import './Full.scss'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import ArticleComment from './Comment'
import { AuthorCard } from '../Author/Card'
import { createMemo, For, onMount, Show } from 'solid-js'
import type { Author, Reaction, Shout } from '../../graphql/types.gen'
import { t } from '../../utils/intl'
import { showModal } from '../../stores/ui'
import { incrementView } from '../../stores/zine/articles'
import MD from './MD'
import { SharePopup } from './SharePopup'
import { useSession } from '../../context/session'
@ -39,11 +38,6 @@ const formatDate = (date: Date) => {
export const FullArticle = (props: ArticleProps) => {
const { session } = useSession()
onMount(() => {
incrementView({ articleSlug: props.article.slug })
})
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
const mainTopic = () =>

View File

@ -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>
)
}

View File

@ -1,4 +1,4 @@
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { t } from '../../utils/intl'
import styles from '../_shared/Popup.module.scss'

View 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} />
}

View File

@ -1,6 +1,6 @@
import type { Author } from '../../graphql/types.gen'
import Userpic from './Userpic'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import styles from './Card.module.scss'
import { createMemo, For, Show } from 'solid-js'
import { translit } from '../../utils/ru2en'

View File

@ -20,7 +20,7 @@ export const Donate = () => {
const [period, setPeriod] = createSignal(monthly)
const [amount, setAmount] = createSignal(0)
onMount(() => {
const initiated = () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const {
cp: { CloudPayments }
@ -51,6 +51,16 @@ export const Donate = () => {
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 = () => {
@ -103,70 +113,68 @@ export const Donate = () => {
}
return (
<>
<form class="discours-form donate-form" action="" method="post">
<input type="hidden" name="shopId" value="156465" />
<input value="148805" name="scid" type="hidden" />
<input value="0" name="customerNumber" type="hidden" />
<form class="discours-form donate-form" action="" method="post">
<input type="hidden" name="shopId" value="156465" />
<input value="148805" name="scid" type="hidden" />
<input value="0" name="customerNumber" type="hidden" />
<div class="form-group">
<div class="donate-buttons-container" ref={amountSwitchElement}>
<input type="radio" name="amount" id="fix250" value="250" />
<label for="fix250" class="btn donate-value-radio">
250&thinsp;
</label>
<input type="radio" name="amount" id="fix500" value="500" checked />
<label for="fix500" class="btn donate-value-radio">
500&thinsp;
</label>
<input type="radio" name="amount" id="fix1000" value="1000" />
<label for="fix1000" class="btn donate-value-radio">
1000&thinsp;
</label>
<input
class="form-control donate-input"
required
ref={customAmountElement}
type="number"
name="sum"
placeholder={t('Another amount')}
/>
</div>
<div class="form-group">
<div class="donate-buttons-container" ref={amountSwitchElement}>
<input type="radio" name="amount" id="fix250" value="250" />
<label for="fix250" class="btn donate-value-radio">
250&thinsp;
</label>
<input type="radio" name="amount" id="fix500" value="500" checked />
<label for="fix500" class="btn donate-value-radio">
500&thinsp;
</label>
<input type="radio" name="amount" id="fix1000" value="1000" />
<label for="fix1000" class="btn donate-value-radio">
1000&thinsp;
</label>
<input
class="form-control donate-input"
required
ref={customAmountElement}
type="number"
name="sum"
placeholder={t('Another amount')}
/>
</div>
</div>
<div class="form-group" id="payment-type" classList={{ showing: showingPayment() }}>
<div class="btn-group payment-choose" data-toggle="buttons">
<input
type="radio"
autocomplete="off"
id="once"
name="once"
onClick={() => setPeriod(once)}
checked={period() === once}
/>
<label for="once" class="btn payment-type" classList={{ active: period() === once }}>
{t('One time')}
</label>
<input
type="radio"
autocomplete="off"
id="monthly"
name="monthly"
onClick={() => setPeriod(monthly)}
checked={period() === monthly}
/>
<label for="monthly" class="btn payment-type" classList={{ active: period() === monthly }}>
{t('Every month')}
</label>
</div>
<div class="form-group" id="payment-type" classList={{ showing: showingPayment() }}>
<div class="btn-group payment-choose" data-toggle="buttons">
<input
type="radio"
autocomplete="off"
id="once"
name="once"
onClick={() => setPeriod(once)}
checked={period() === once}
/>
<label for="once" class="btn payment-type" classList={{ active: period() === once }}>
{t('One time')}
</label>
<input
type="radio"
autocomplete="off"
id="monthly"
name="monthly"
onClick={() => setPeriod(monthly)}
checked={period() === monthly}
/>
<label for="monthly" class="btn payment-type" classList={{ active: period() === monthly }}>
{t('Every month')}
</label>
</div>
</div>
<div class="form-group">
<a href={''} class="btn send-btn donate" onClick={show}>
{t('Help discours to grow')}
</a>
</div>
</form>
</>
<div class="form-group">
<a href={''} class="btn send-btn donate" onClick={show}>
{t('Help discours to grow')}
</a>
</div>
</form>
)
}

View File

@ -1,6 +1,6 @@
import { createMemo, For } from 'solid-js'
import styles from './Footer.module.scss'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import Subscribe from './Subscribe'
import { t } from '../../utils/intl'
import { locale } from '../../stores/ui'

View File

@ -6,7 +6,7 @@ import { AuthorCard } from '../Author/Card'
import { TopicCard } from '../Topic/Card'
import style from './Beside.module.scss'
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'
interface BesideProps {

View File

@ -3,7 +3,7 @@ import { createMemo, For, Show } from 'solid-js'
import type { Shout } from '../../graphql/types.gen'
import { capitalize } from '../../utils'
import { translit } from '../../utils/ru2en'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import styles from './Card.module.scss'
import { locale } from '../../stores/ui'
import { handleClientRouteLinkClick } from '../../stores/router'
@ -96,7 +96,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
<div class={styles.shoutCardContent}>
<Show when={layout && layout !== 'article' && !(props.settings?.noicon || props.settings?.noimage)}>
<div class={styles.shoutCardType}>
<a href={`/topic/${mainTopic.slug}`}>
<a href={`/expo/${layout}`}>
<Icon name={layout} class={styles.icon} />
</a>
</div>

View File

@ -39,10 +39,12 @@ export default (props: ArticleListProps) => {
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 (
<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))} />}
</For>
<a href={''} onClick={handleMore} classList={{ disabled: loadingMore() }}>

View File

@ -2,7 +2,7 @@ import { For } from 'solid-js'
import type { Author } from '../../graphql/types.gen'
import { useAuthorsStore } from '../../stores/zine/authors'
import { t } from '../../utils/intl'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { useTopicsStore } from '../../stores/zine/topics'
import { useArticlesStore } from '../../stores/zine/articles'
import { useSeenStore } from '../../stores/zine/seen'
@ -13,7 +13,7 @@ type FeedSidebarProps = {
}
export const FeedSidebar = (props: FeedSidebarProps) => {
const { getSeen: seen } = useSeenStore()
const { seen } = useSeenStore()
const { session } = useSession()
const { authorEntities } = useAuthorsStore({ authors: props.authors })
const { articlesByTopic } = useArticlesStore()

View File

@ -7,7 +7,7 @@ import 'swiper/scss/pagination'
import './Slider.scss'
import type { Shout } from '../../graphql/types.gen'
import { createEffect, createMemo, createSignal, Show, For } from 'solid-js'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
interface SliderProps {
title?: string

View File

@ -28,10 +28,12 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.name {
color: #141414;
font-weight: 500;
}
.message {
color: #9fa1a7;
}
@ -40,6 +42,7 @@
.activity {
font-size: 12px;
margin-left: 12px;
.time {
text-align: right;
color: #ccc;

View File

@ -1,24 +1,32 @@
import './DialogCard.module.scss'
import DialogAvatar from './DialogAvatar'
import type { Author } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient'
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 Props = {
type DialogProps = {
online?: boolean
message?: string
counter?: number
ownerSlug: Author['slug']
} & Author
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 DialogCard = (props: Props) => {
const handleOpenChat = async () => {
try {
const initChat = await apiClient.createChat({
title: 'test chat',
members: [props.slug, props.ownerSlug]
members: [props.author.slug, currentSession().user.slug]
})
console.log('!!! test:', initChat.data)
// console.log('!!! test:', test)
} catch (error) {
console.log('!!! errr:', error)
}
@ -27,10 +35,10 @@ const DialogCard = (props: Props) => {
return (
<div class={styles.DialogCard} onClick={handleOpenChat}>
<div class={styles.avatar}>
<DialogAvatar name={props.name} url={props.userpic} online={props.online} />
<DialogAvatar name={props.author.name} url={props.author.userpic} online={props.online} />
</div>
<div class={styles.row}>
<div class={styles.name}>{props.name}</div>
<div class={styles.name}>{props.author.name}</div>
<div class={styles.message}>
Указать предпочтительные языки для результатов поиска можно в разделе
</div>

View File

@ -1,7 +1,7 @@
.Search {
.field {
position: relative;
background: #ffffff;
background: #fff;
border: 2px solid #e8e8e8;
border-radius: 2px;
overflow: hidden;
@ -22,8 +22,10 @@
color: #858585;
font-family: inherit;
}
&:focus {
outline: none;
& + .icon {
opacity: 0;
right: -30px;

View File

@ -1,6 +1,6 @@
import styles from './Search.module.scss'
import { createSignal } from 'solid-js'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
type Props = {
placeholder: string

View File

@ -1,5 +1,5 @@
import { t } from '../../../utils/intl'
import { Icon } from '../Icon'
import { Icon } from '../../_shared/Icon'
import { hideModal } from '../../../stores/ui'
import styles from './SocialProviders.module.scss'

View File

@ -1,5 +1,5 @@
import { For, Show, createSignal, createEffect, onMount, onCleanup } from 'solid-js'
import { Icon } from './Icon'
import { Icon } from '../_shared/Icon'
import { Modal } from './Modal'
import { AuthModal } from './AuthModal'
import { t } from '../../utils/intl'

View File

@ -2,7 +2,7 @@ import styles from './Header.module.scss'
import { clsx } from 'clsx'
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
import { t } from '../../utils/intl'
import { Icon } from './Icon'
import { Icon } from '../_shared/Icon'
import { createSignal, Show } from 'solid-js'
import Notifications from './Notifications'
import { ProfilePopup } from './ProfilePopup'

View File

@ -1,6 +1,6 @@
import { For, Show } from 'solid-js'
import type { Topic } from '../../graphql/types.gen'
import { Icon } from './Icon'
import { Icon } from '../_shared/Icon'
import './Topics.scss'
import { t } from '../../utils/intl'
import { locale } from '../../stores/ui'

View File

@ -1,4 +1,4 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { AllAuthorsView } from '../Views/AllAuthors'
import type { PageProps } from '../types'
import { createSignal, onMount, Show } from 'solid-js'
@ -18,11 +18,11 @@ export const AllAuthorsPage = (props: PageProps) => {
})
return (
<MainLayout>
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<AllAuthorsView authors={props.allAuthors} />
</Show>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,4 +1,4 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { AllTopicsView } from '../Views/AllTopics'
import type { PageProps } from '../types'
import { createSignal, onMount, Show } from 'solid-js'
@ -18,11 +18,11 @@ export const AllTopicsPage = (props: PageProps) => {
})
return (
<MainLayout>
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<AllTopicsView topics={props.allTopics} />
</Show>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,14 +1,14 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { ArticleView } from '../Views/Article'
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 type { Shout } from '../../graphql/types.gen'
import { useRouter } from '../../stores/router'
import { Loading } from '../Loading'
export const ArticlePage = (props: PageProps) => {
const sortedArticles = props.article ? [props.article] : []
const shouts = props.article ? [props.article] : []
const slug = createMemo(() => {
const { page: getPage } = useRouter()
@ -23,25 +23,25 @@ export const ArticlePage = (props: PageProps) => {
})
const { articleEntities } = useArticlesStore({
sortedArticles
shouts
})
const article = createMemo<Shout>(() => articleEntities()[slug()])
onMount(() => {
onMount(async () => {
const articleValue = articleEntities()[slug()]
if (!articleValue || !articleValue.body) {
loadArticle({ slug: slug() })
await loadShoutsBy({ by: { slug: slug() }, limit: 1, offset: 0 })
}
})
return (
<MainLayout headerTitle={article()?.title || ''}>
<PageWrap headerTitle={article()?.title || ''}>
<Show when={Boolean(article())} fallback={<Loading />}>
<ArticleView article={article()} />
</Show>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,14 +1,14 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../Views/Author'
import type { PageProps } from '../types'
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 { loadAuthor } from '../../stores/zine/authors'
import { Loading } from '../Loading'
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 { page: getPage } = useRouter()
@ -27,7 +27,7 @@ export const AuthorPage = (props: PageProps) => {
return
}
await loadAuthorArticles({ authorSlug: slug(), limit: PRERENDERED_ARTICLES_COUNT })
await loadShoutsBy({ by: { author: slug() }, limit: PRERENDERED_ARTICLES_COUNT })
await loadAuthor({ slug: slug() })
setIsLoaded(true)
@ -36,11 +36,11 @@ export const AuthorPage = (props: PageProps) => {
onCleanup(() => resetSortedArticles())
return (
<MainLayout>
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<AuthorView author={props.author} authorArticles={props.authorArticles} authorSlug={slug()} />
<AuthorView author={props.author} shouts={props.shouts} authorSlug={slug()} />
</Show>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,8 +1,8 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
export const ConnectPage = () => {
return (
<MainLayout>
<PageWrap>
<article class="container container--static-page">
<div class="row">
<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>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,16 +1,16 @@
import { lazy, Suspense } from 'solid-js'
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { Loading } from '../Loading'
const CreateView = lazy(() => import('../Views/Create'))
export const CreatePage = () => {
return (
<MainLayout>
<PageWrap>
<Suspense fallback={<Loading />}>
<CreateView />
</Suspense>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,4 +1,4 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { FeedView } from '../Views/Feed'
import { onCleanup } from 'solid-js'
import { resetSortedArticles } from '../../stores/zine/articles'
@ -7,9 +7,9 @@ export const FeedPage = () => {
onCleanup(() => resetSortedArticles())
return (
<MainLayout>
<PageWrap>
<FeedView />
</MainLayout>
</PageWrap>
)
}

View File

@ -1,11 +1,11 @@
import { FourOuFourView } from '../Views/FourOuFour'
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
export const FourOuFourPage = () => {
return (
<MainLayout isHeaderFixed={false} hideFooter={true}>
<PageWrap isHeaderFixed={false} hideFooter={true}>
<FourOuFourView />
</MainLayout>
</PageWrap>
)
}

View File

@ -1,20 +1,20 @@
import { HomeView, PRERENDERED_ARTICLES_COUNT } from '../Views/Home'
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import type { PageProps } from '../types'
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 { Loading } from '../Loading'
import { InboxView } from '../Views/Inbox'
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 () => {
if (isLoaded()) {
return
}
await loadPublishedArticles({ limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadShoutsBy({ by: { visibility: 'public' }, limit: PRERENDERED_ARTICLES_COUNT, offset: 0 })
await loadRandomTopics()
setIsLoaded(true)
@ -23,12 +23,11 @@ export const HomePage = (props: PageProps) => {
onCleanup(() => resetSortedArticles())
return (
<MainLayout>
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<InboxView />
<HomeView randomTopics={props.randomTopics} recentPublishedArticles={props.homeArticles || []} />
<HomeView randomTopics={props.randomTopics} shouts={props.shouts || []} />
</Show>
</MainLayout>
</PageWrap>
)
}

View 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

View File

@ -1,8 +1,8 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { SearchView } from '../Views/Search'
import type { PageProps } from '../types'
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 { Loading } from '../Loading'
@ -26,18 +26,18 @@ export const SearchPage = (props: PageProps) => {
return
}
await loadSearchResults({ query: q(), limit: 50, offset: 0 })
await loadShoutsBy({ by: { title: q(), body: q() }, limit: 50, offset: 0 })
setIsLoaded(true)
})
onCleanup(() => resetSortedArticles())
return (
<MainLayout>
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<SearchView results={props.searchResults || []} query={props.searchQuery} />
</Show>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,14 +1,14 @@
import { MainLayout } from '../Layouts/MainLayout'
import { PageWrap } from '../_shared/PageWrap'
import { PRERENDERED_ARTICLES_COUNT, TopicView } from '../Views/Topic'
import type { PageProps } from '../types'
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 { loadTopic } from '../../stores/zine/topics'
import { Loading } from '../Loading'
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 { page: getPage } = useRouter()
@ -27,7 +27,7 @@ export const TopicPage = (props: PageProps) => {
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() })
setIsLoaded(true)
@ -36,11 +36,11 @@ export const TopicPage = (props: PageProps) => {
onCleanup(() => resetSortedArticles())
return (
<MainLayout>
<PageWrap>
<Show when={isLoaded()} fallback={<Loading />}>
<TopicView topic={props.topic} topicArticles={props.topicArticles} topicSlug={slug()} />
<TopicView topic={props.topic} shouts={props.shouts} topicSlug={slug()} />
</Show>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,10 +1,10 @@
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { t } from '../../../utils/intl'
export const DiscussionRulesPage = () => {
const title = t('Discussion rules')
return (
<MainLayout>
<PageWrap>
<article class="container container--static-page">
<div class="row">
<div class="col-md-6 col-xl-7 shift-content order-md-first">
@ -114,7 +114,7 @@ export const DiscussionRulesPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,10 +1,10 @@
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
// const title = t('Dogma')
export const DogmaPage = () => {
return (
<MainLayout>
<PageWrap>
<article class="container container--static-page">
<div class="row">
<div class="col-md-6 col-xl-7 shift-content order-md-first">
@ -53,7 +53,7 @@ export const DogmaPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,7 +1,7 @@
import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { t } from '../../../utils/intl'
import { Icon } from '../../Nav/Icon'
import { Icon } from '../../_shared/Icon'
export const GuidePage = () => {
const title = t('How it works')
@ -11,7 +11,7 @@ export const GuidePage = () => {
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
return (
<MainLayout>
<PageWrap>
{/*<Meta name="description" content={title} />*/}
{/*<Meta name="keywords" content={t('Discours') + ',' + title} />*/}
{/*<Meta property="og:title" content={title} />*/}
@ -283,7 +283,7 @@ export const GuidePage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,7 +1,7 @@
import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { Donate } from '../../Discours/Donate'
import { Icon } from '../../Nav/Icon'
import { Icon } from '../../_shared/Icon'
// const title = t('Support us')
@ -11,7 +11,7 @@ export const HelpPage = () => {
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
return (
<MainLayout>
<PageWrap>
{/*<Meta name="description">Здесь можно поддержать Дискурс материально.</Meta>*/}
{/*<Meta name="keywords">Discours.io, помощь, благотворительность</Meta>*/}
@ -161,7 +161,7 @@ export const HelpPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,10 +1,10 @@
import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { Modal } from '../../Nav/Modal'
import { Feedback } from '../../Discours/Feedback'
import Subscribe from '../../Discours/Subscribe'
import Opener from '../../Nav/Opener'
import { Icon } from '../../Nav/Icon'
import { Icon } from '../../_shared/Icon'
// title={t('Manifest')}
@ -14,7 +14,7 @@ export const ManifestPage = () => {
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
return (
<MainLayout>
<PageWrap>
<Modal name="feedback">
<Feedback />
</Modal>
@ -191,7 +191,7 @@ export const ManifestPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,11 +1,11 @@
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { t } from '../../../utils/intl'
// const title = t('Partners')
export const PartnersPage = () => {
return (
<MainLayout>
<PageWrap>
<article class="container container--static-page">
<div class="row">
<div class="col-md-6 col-xl-7 shift-content order-md-first">
@ -13,7 +13,7 @@ export const PartnersPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,10 +1,10 @@
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { t } from '../../../utils/intl'
export const PrinciplesPage = () => {
const title = t('Principles')
return (
<MainLayout>
<PageWrap>
<article class="container container--static-page">
<div class="row">
<div class="col-md-6 col-xl-7 shift-content order-md-first">
@ -172,7 +172,7 @@ export const PrinciplesPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,11 +1,11 @@
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { t } from '../../../utils/intl'
// title={t('Projects')}>
export const ProjectsPage = () => {
return (
<MainLayout>
<PageWrap>
<article class="container container--static-page">
<div class="row">
<div class="col-md-6 col-xl-7 shift-content order-md-first">
@ -13,7 +13,7 @@ export const ProjectsPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,6 +1,6 @@
import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { Icon } from '../../Nav/Icon'
import { PageWrap } from '../../_shared/PageWrap'
import { Icon } from '../../_shared/Icon'
// const title = t('Terms of use')
@ -10,7 +10,7 @@ export const TermsOfUsePage = () => {
const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
return (
<MainLayout>
<PageWrap>
{/*<Meta name="description" content={title} />*/}
{/*<Meta name="keywords" content={`Discours.io, ${t('Terms of use')}, ${t('Terms of use', 'en')}`} />*/}
{/*<Meta property="og:title" content={title} />*/}
@ -274,7 +274,7 @@ export const TermsOfUsePage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -1,10 +1,10 @@
import { MainLayout } from '../../Layouts/MainLayout'
import { PageWrap } from '../../_shared/PageWrap'
import { t } from '../../../utils/intl'
export const ThanksPage = () => {
const title = t('Thank you')
return (
<MainLayout>
<PageWrap>
{/*<Meta name="description" content={title} />*/}
{/*<Meta name="keywords" content={`Discours.io, ${title}, ${t('Thank you', 'en')}`} />*/}
{/*<Meta property="og:title" content={title} />*/}
@ -85,7 +85,7 @@ export const ThanksPage = () => {
</div>
</div>
</article>
</MainLayout>
</PageWrap>
)
}

View File

@ -29,29 +29,14 @@ import { TermsOfUsePage } from './Pages/about/TermsOfUsePage'
import { ThanksPage } from './Pages/about/ThanksPage'
import { CreatePage } from './Pages/CreatePage'
import { ConnectPage } from './Pages/ConnectPage'
import { LayoutShoutsPage } from './Pages/LayoutShoutsPage'
import { SessionProvider } from '../context/session'
// TODO: lazy load
// const HomePage = lazy(() => import('./Pages/HomePage'))
// 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 SomePage = lazy(() => import('./Pages/SomePage'))
const pagesMap: Record<keyof Routes, Component<PageProps>> = {
expo: LayoutShoutsPage,
connect: ConnectPage,
create: CreatePage,
home: HomePage,

View File

@ -1,5 +1,5 @@
import type { Topic } from '../../graphql/types.gen'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import './FloorHeader.scss'
import { t } from '../../utils/intl'

View File

@ -1,7 +1,7 @@
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
import type { Author } from '../../graphql/types.gen'
import { AuthorCard } from '../Author/Card'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { t } from '../../utils/intl'
import { useAuthorsStore, setAuthorsSort } from '../../stores/zine/authors'
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'

View File

@ -1,6 +1,6 @@
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
import type { Topic } from '../../graphql/types.gen'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { t } from '../../utils/intl'
import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics'
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'

View File

@ -1,13 +1,14 @@
import { createEffect, createSignal, Show, Suspense } from 'solid-js'
import { FullArticle } from '../Article/FullArticle'
import { t } from '../../utils/intl'
import type { Shout } from '../../graphql/types.gen'
import { loadArticleReactions, useReactionsStore } from '../../stores/zine/reactions'
import type { Shout, Reaction } from '../../graphql/types.gen'
import { useReactionsStore } from '../../stores/zine/reactions'
import '../../styles/Article.scss'
interface ArticlePageProps {
article: Shout
reactions?: Reaction[]
}
const ARTICLE_COMMENTS_PAGE_SIZE = 50
@ -15,13 +16,20 @@ const ARTICLE_COMMENTS_PAGE_SIZE = 50
export const ArticleView = (props: ArticlePageProps) => {
const [getCommentsPage] = createSignal(1)
const [getIsCommentsLoading, setIsCommentsLoading] = createSignal(false)
const reactionslist = useReactionsStore()
const {
reactionsByShout,
sortedReactions,
createReaction,
updateReaction,
deleteReaction,
loadReactionsBy
} = useReactionsStore({ reactions: props.reactions })
createEffect(async () => {
try {
setIsCommentsLoading(true)
await loadArticleReactions({
articleSlug: props.article.slug,
await loadReactionsBy({
by: { shout: props.article.slug },
limit: ARTICLE_COMMENTS_PAGE_SIZE,
offset: getCommentsPage() * ARTICLE_COMMENTS_PAGE_SIZE
})
@ -36,7 +44,7 @@ export const ArticleView = (props: ArticlePageProps) => {
<Suspense>
<FullArticle
article={props.article}
reactions={reactionslist().filter((r) => r.shout.slug === props.article.slug)}
reactions={reactionsByShout()[props.article.slug]}
isCommentsLoading={getIsCommentsLoading()}
/>
</Suspense>

View File

@ -5,7 +5,7 @@ import { Row3 } from '../Feed/Row3'
import { AuthorFull } from '../Author/Full'
import { t } from '../../utils/intl'
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 { useRouter } from '../../stores/router'
@ -15,7 +15,7 @@ import { splitToPages } from '../../utils/splitToPages'
// TODO: load reactions on client
type AuthorProps = {
authorArticles: Shout[]
shouts: Shout[]
author: Author
authorSlug: string
// FIXME author topics fro server
@ -23,7 +23,7 @@ type AuthorProps = {
}
type AuthorPageSearchParams = {
by: '' | 'viewed' | 'rating' | 'commented' | 'recent'
by: '' | 'viewed' | 'rating' | 'commented' | 'recent' | 'followed'
}
export const PRERENDERED_ARTICLES_COUNT = 12
@ -31,7 +31,7 @@ const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
export const AuthorView = (props: AuthorProps) => {
const { sortedArticles } = useArticlesStore({
sortedArticles: props.authorArticles
shouts: props.shouts
})
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
const { topicsByAuthor } = useTopicsStore()
@ -42,8 +42,8 @@ export const AuthorView = (props: AuthorProps) => {
const loadMore = async () => {
saveScrollPosition()
const { hasMore } = await loadAuthorArticles({
authorSlug: author().slug,
const { hasMore } = await loadShoutsBy({
by: { author: author().slug },
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})
@ -76,27 +76,21 @@ export const AuthorView = (props: AuthorProps) => {
<div class="row group__controls">
<div class="col-md-8">
<ul class="view-switcher">
<li classList={{ selected: !searchParams().by || searchParams().by === 'recent' }}>
<button type="button" onClick={() => changeSearchParam('by', 'recent')}>
{t('Recent')}
<li classList={{ selected: searchParams().by === 'rating' }}>
<button type="button" onClick={() => changeSearchParam('by', 'rating')}>
{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>
</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>
</div>
<div class="col-md-4">

View File

@ -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 stylesBeside from '../../components/Feed/Beside.module.scss'
import { Icon } from '../Nav/Icon'
import { byCreated, sortBy } from '../../utils/sortby'
import { Icon } from '../_shared/Icon'
import { TopicCard } from '../Topic/Card'
import { ArticleCard } from '../Feed/Card'
import { AuthorCard } from '../Author/Card'
import { t } from '../../utils/intl'
import { FeedSidebar } from '../Feed/Sidebar'
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 { useAuthorsStore } from '../../stores/zine/authors'
import { useTopicsStore } from '../../stores/zine/topics'
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
import { useSession } from '../../context/session'
import { Collab, ReactionKind, Shout } from '../../graphql/types.gen'
import { collabs, setCollabs } from '../../stores/editor'
// const AUTHORSHIP_REACTIONS = [
// ReactionKind.Accept,
// ReactionKind.Reject,
// ReactionKind.Propose,
// ReactionKind.Ask
// ]
const AUTHORSHIP_REACTIONS = [
ReactionKind.Accept,
ReactionKind.Reject,
ReactionKind.Propose,
ReactionKind.Ask
]
export const FEED_PAGE_SIZE = 20
export const FeedView = () => {
// state
const { sortedArticles } = useArticlesStore()
const reactions = useReactionsStore()
const { sortedArticles, loadShoutsBy } = useArticlesStore()
const { sortedReactions: topComments, loadReactionsBy } = useReactionsStore({})
const { sortedAuthors } = useAuthorsStore()
const { topTopics } = useTopicsStore()
const { topAuthors } = useTopAuthorsStore()
const { session } = useSession()
const topReactions = createMemo(() => sortBy(reactions(), byCreated))
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 { 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)
}
onMount(() => {
loadMore()
onMount(async () => {
// 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 (
@ -118,7 +126,7 @@ export const FeedView = () => {
<aside class="col-md-3">
<section class="feed-comments">
<h4>{t('Comments')}</h4>
<For each={topReactions()}>
<For each={topComments()}>
{(comment) => <CommentCard comment={comment} compact={true} />}
</For>
</section>

View File

@ -6,7 +6,7 @@ import { handleClientRouteLinkClick } from '../../stores/router'
// by: '' | 'topics' | 'authors' | 'reacted'
// }
export const FeedSettingsView = () => {
export const FeedSettingsView = (_props) => {
return (
<div class="container">
<h1>{t('Feed settings')}</h1>

View File

@ -1,5 +1,5 @@
import { t } from '../../utils/intl'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import styles from '../../styles/FourOuFour.module.scss'
import { clsx } from 'clsx'

View File

@ -11,15 +11,10 @@ import RowShort from '../Feed/RowShort'
import Slider from '../Feed/Slider'
import Group from '../Feed/Group'
import type { Shout, Topic } from '../../graphql/types.gen'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { t } from '../../utils/intl'
import { useTopicsStore } from '../../stores/zine/topics'
import {
loadPublishedArticles,
loadTopArticles,
loadTopMonthArticles,
useArticlesStore
} from '../../stores/zine/articles'
import { loadShoutsBy, useArticlesStore } from '../../stores/zine/articles'
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
import { locale } from '../../stores/ui'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
@ -27,7 +22,7 @@ import { splitToPages } from '../../utils/splitToPages'
type HomeProps = {
randomTopics: Topic[]
recentPublishedArticles: Shout[]
shouts: Shout[]
}
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) => {
const {
sortedArticles,
articlesByLayout,
topArticles,
topMonthArticles,
topViewedArticles,
topCommentedArticles,
articlesByLayout
topMonthArticles,
topViewedArticles
} = useArticlesStore({
sortedArticles: props.recentPublishedArticles
shouts: props.shouts
})
const { randomTopics, topTopics } = useTopicsStore({
randomTopics: props.randomTopics
})
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { topAuthors } = useTopAuthorsStore()
onMount(async () => {
loadTopArticles()
loadTopMonthArticles()
if (sortedArticles().length < PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT) {
const { hasMore } = await loadPublishedArticles({
const { hasMore } = await loadShoutsBy({
by: {},
limit: CLIENT_LOAD_ARTICLES_COUNT,
offset: sortedArticles().length
})
@ -91,7 +84,8 @@ export const HomeView = (props: HomeProps) => {
const loadMore = async () => {
saveScrollPosition()
const { hasMore } = await loadPublishedArticles({
const { hasMore } = await loadShoutsBy({
by: { visibility: 'public' },
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})

View File

@ -1,7 +1,7 @@
import { For, createSignal, Show, onMount, createEffect } from 'solid-js'
import type { Author } from '../../graphql/types.gen'
import { AuthorCard } from '../Author/Card'
import { Icon } from '../Nav/Icon'
import { Icon } from '../_shared/Icon'
import { Loading } from '../Loading'
import DialogCard from '../Inbox/DialogCard'
import Search from '../Inbox/Search'
@ -12,6 +12,8 @@ import MarkdownIt from 'markdown-it'
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({
@ -150,17 +152,7 @@ export const InboxView = () => {
</div>
<div class="holder">
<div class="dialogs">
<For each={authors()}>
{(author) => (
<DialogCard
ownerSlug={currentSlug()}
id={author.id}
name={author.name}
slug={author.slug}
online={true}
/>
)}
</For>
<For each={authors()}>{(author) => <DialogCard author={author} online={true} />}</For>
</div>
</div>
</div>

View File

@ -3,7 +3,7 @@ import '../../styles/Search.scss'
import type { Shout } from '../../graphql/types.gen'
import { ArticleCard } from '../Feed/Card'
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'
type SearchPageSearchParams = {
@ -16,7 +16,7 @@ type 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 { searchParams } = useRouter<SearchPageSearchParams>()
@ -28,7 +28,7 @@ export const SearchView = (props: Props) => {
const handleSubmit = (_ev) => {
// TODO page
// TODO sort
loadSearchResults({ query: getQuery() })
loadShoutsBy({ by: { title: getQuery(), body: getQuery() }, limit: 50 })
}
return (

View File

@ -8,7 +8,7 @@ import { FullTopic } from '../Topic/Full'
import { t } from '../../utils/intl'
import { useRouter } from '../../stores/router'
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 { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages'
@ -22,7 +22,7 @@ type TopicsPageSearchParams = {
interface TopicProps {
topic: Topic
topicArticles: Shout[]
shouts: Shout[]
topicSlug: string
}
@ -34,7 +34,7 @@ export const TopicView = (props: TopicProps) => {
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const { sortedArticles } = useArticlesStore({ sortedArticles: props.topicArticles })
const { sortedArticles } = useArticlesStore({ shouts: props.shouts })
const { topicEntities } = useTopicsStore({ topics: [props.topic] })
const { authorsByTopic } = useAuthorsStore()
@ -44,8 +44,8 @@ export const TopicView = (props: TopicProps) => {
const loadMore = async () => {
saveScrollPosition()
const { hasMore } = await loadTopicArticles({
topicSlug: topic().slug,
const { hasMore } = await loadShoutsBy({
by: { topic: topic().slug },
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})

View File

@ -5,14 +5,14 @@ import { Footer } from '../Discours/Footer'
import '../../styles/app.scss'
import { Show } from 'solid-js'
type MainLayoutProps = {
type PageWrapProps = {
headerTitle?: string
children: JSX.Element
isHeaderFixed?: boolean
hideFooter?: boolean
}
export const MainLayout = (props: MainLayoutProps) => {
export const PageWrap = (props: PageWrapProps) => {
const isHeaderFixed = props.isHeaderFixed !== undefined ? props.isHeaderFixed : true
return (

View File

@ -1,18 +1,18 @@
// in a separate file to avoid circular dependencies
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
export type PageProps = {
randomTopics?: Topic[]
article?: Shout
authorArticles?: Shout[]
topicArticles?: Shout[]
homeArticles?: Shout[]
shouts?: Shout[]
author?: Author
allAuthors?: Author[]
topic?: Topic
allTopics?: Topic[]
searchQuery?: string
layout?: LayoutType
// other types?
searchResults?: Shout[]
chats?: Chat[]

View File

@ -1,9 +0,0 @@
import { gql } from '@urql/core'
export default gql`
mutation IncrementViewMutation($shout: String!) {
incrementView(shout: $shout) {
error
}
}
`

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -1,14 +1,16 @@
import { gql } from '@urql/core'
export default gql`
query RecentPublishedQuery($limit: Int!, $offset: Int!) {
recentPublished(limit: $limit, offset: $offset) {
query LoadShoutsByQuery($by: ShoutsBy, $limit: Int!, $offset: Int!) {
loadShoutsBy(by: $by, limit: $limit, offset: $offset) {
_id: slug
title
subtitle
slug
layout
cover
# community
mainTopic
topics {
title
body
@ -26,10 +28,8 @@ export default gql`
slug
userpic
}
# community
mainTopic
publishedAt
createdAt
publishedAt
stat {
_id: viewed
viewed

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -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
}
}
}
`

View File

@ -1,8 +1,8 @@
import { gql } from '@urql/core'
export default gql`
query GetAuthorsBySlugsQuery($slugs: [String]!) {
getUsersBySlugs(slugs: $slugs) {
query AuthorLoadByQuery($by: AuthorsBy, $limit: Int, $offset: Int) {
loadAuthorsBy(by: $by, limit: $limit, offset: $offset) {
_id: slug
slug
name

View File

@ -1,8 +1,8 @@
import { gql } from '@urql/core'
export default gql`
query LoadMessagesQuery($chatId: String!, $offset: Int, $amount: Int) {
loadChat(chatId: $chatId, offset: $offset, amount: $amount) {
query LoadMessagesQuery($by: MessagesBy!, $limit: Int, $offset: Int) {
loadMessagesBy(by: $by, limit: $limit, offset: $offset) {
error
messages {
author

View File

@ -1,8 +1,8 @@
import { gql } from '@urql/core'
export default gql`
query GetChatsQuery {
myChats {
query GetChatsQuery($limit: Int, $offset: Int) {
loadChats(limit: $limit, offset: $offset) {
error
chats {
title

View File

@ -1,13 +0,0 @@
import { gql } from '@urql/core'
export default gql`
query {
getMyCollections {
id
title
desc
slug
amount
}
}
`

View File

@ -1,13 +0,0 @@
import { gql } from '@urql/core'
export default gql`
query CollectionsUserQuery($slug: String!) {
getUserCollections(user: $slug) {
id
title
desc
slug
amount
}
}
`

View File

@ -1,8 +1,8 @@
import { gql } from '@urql/core'
export default gql`
query ReactionsForShoutsQuery($shouts: [String]!, $limit: Int!, $offset: Int!) {
reactionsForShouts(shouts: $shouts, limit: $limit, offset: $offset) {
query LoadReactionsByQuery($by: ReactionsBy, $limit: Int!, $offset: Int!) {
loadReactionsBy(by: $by, limit: $limit, offset: $offset) {
id
createdBy {
slug

View File

@ -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
}
}
}
`

View File

@ -35,11 +35,23 @@ export type Author = {
}
export type AuthorStat = {
commented?: Maybe<Scalars['Int']>
followers?: Maybe<Scalars['Int']>
followings?: 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 = {
admins?: Maybe<Array<Maybe<User>>>
createdAt: Scalars['Int']
@ -87,12 +99,6 @@ export type Collection = {
title: Scalars['String']
}
export type CollectionInput = {
desc?: InputMaybe<Scalars['String']>
pic?: InputMaybe<Scalars['String']>
title: Scalars['String']
}
export type Community = {
createdAt: Scalars['DateTime']
createdBy: User
@ -103,12 +109,6 @@ export type Community = {
slug: Scalars['String']
}
export type CommunityInput = {
desc?: InputMaybe<Scalars['String']>
pic?: InputMaybe<Scalars['String']>
title: Scalars['String']
}
export enum FollowingEntity {
Author = 'AUTHOR',
Community = 'COMMUNITY',
@ -132,25 +132,28 @@ export enum MessageStatus {
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 = {
confirmEmail: AuthResult
createChat: Result
createCollection: Result
createCommunity: Result
createMessage: Result
createReaction: Result
createShout: Result
createTopic: Result
deleteChat: Result
deleteCollection: Result
deleteCommunity: Result
deleteMessage: Result
deleteReaction: Result
deleteShout: Result
destroyTopic: Result
enterChat: Result
follow: Result
incrementView: Result
inviteAuthor: Result
inviteChat: Result
markAsRead: Result
@ -161,9 +164,8 @@ export type Mutation = {
sendLink: Result
unfollow: Result
updateChat: Result
updateCollection: Result
updateCommunity: Result
updateMessage: Result
updateOnlineStatus: Result
updateProfile: Result
updateReaction: Result
updateShout: Result
@ -179,14 +181,6 @@ export type MutationCreateChatArgs = {
title?: InputMaybe<Scalars['String']>
}
export type MutationCreateCollectionArgs = {
collection: CollectionInput
}
export type MutationCreateCommunityArgs = {
community: CommunityInput
}
export type MutationCreateMessageArgs = {
body: Scalars['String']
chatId: Scalars['String']
@ -209,14 +203,6 @@ export type MutationDeleteChatArgs = {
chatId: Scalars['String']
}
export type MutationDeleteCollectionArgs = {
slug: Scalars['String']
}
export type MutationDeleteCommunityArgs = {
slug: Scalars['String']
}
export type MutationDeleteMessageArgs = {
chatId: Scalars['String']
id: Scalars['Int']
@ -234,19 +220,11 @@ export type MutationDestroyTopicArgs = {
slug: Scalars['String']
}
export type MutationEnterChatArgs = {
chatId: Scalars['String']
}
export type MutationFollowArgs = {
slug: Scalars['String']
what: FollowingEntity
}
export type MutationIncrementViewArgs = {
shout: Scalars['String']
}
export type MutationInviteAuthorArgs = {
author: Scalars['String']
shout: Scalars['String']
@ -292,14 +270,6 @@ export type MutationUpdateChatArgs = {
chat: ChatInput
}
export type MutationUpdateCollectionArgs = {
collection: CollectionInput
}
export type MutationUpdateCommunityArgs = {
community: CommunityInput
}
export type MutationUpdateMessageArgs = {
body: Scalars['String']
chatId: Scalars['String']
@ -347,95 +317,66 @@ export type ProfileInput = {
export type Query = {
authorsAll: Array<Maybe<Author>>
collectionsAll: Array<Maybe<Collection>>
getAuthor: User
getCollabs: Array<Maybe<Collab>>
getCommunities: Array<Maybe<Community>>
getCommunity: Community
getShoutBySlug: Shout
getTopic: Topic
getUserCollections: Array<Maybe<Collection>>
getUserRoles: Array<Maybe<Role>>
getUsersBySlugs: Array<Maybe<Author>>
isEmailUsed: Scalars['Boolean']
loadAuthorsBy: Array<Maybe<Author>>
loadChats: Result
loadMessages: Result
loadMessagesBy: Result
loadReactionsBy: Array<Maybe<Reaction>>
loadShoutsBy: Array<Maybe<Shout>>
markdownBody: Scalars['String']
reactionsByAuthor: Array<Maybe<Reaction>>
reactionsForShouts: Array<Maybe<Reaction>>
recentAll: Array<Maybe<Shout>>
recentCandidates: Array<Maybe<Shout>>
recentCommented: Array<Maybe<Shout>>
recentPublished: Array<Maybe<Shout>>
recentReacted: Array<Maybe<Shout>>
searchChats: Result
searchMessages: Result
searchQuery?: Maybe<Array<Maybe<Shout>>>
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
signOut: AuthResult
topAuthors: Array<Maybe<Author>>
topCommented: Array<Maybe<Shout>>
topMonth: Array<Maybe<Shout>>
topOverall: Array<Maybe<Shout>>
topPublished: Array<Maybe<Shout>>
topicsAll: Array<Maybe<Topic>>
topicsByAuthor: Array<Maybe<Topic>>
topicsByCommunity: Array<Maybe<Topic>>
topicsRandom: Array<Maybe<Topic>>
userFollowedAuthors: Array<Maybe<Author>>
userFollowedCommunities: Array<Maybe<Community>>
userFollowedTopics: Array<Maybe<Topic>>
userFollowers: Array<Maybe<Author>>
userReactedShouts: Array<Maybe<Shout>>
}
export type QueryGetAuthorArgs = {
slug: Scalars['String']
}
export type QueryGetCommunityArgs = {
slug?: InputMaybe<Scalars['String']>
}
export type QueryGetShoutBySlugArgs = {
slug: Scalars['String']
}
export type QueryGetTopicArgs = {
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 = {
email: Scalars['String']
}
export type QueryLoadChatsArgs = {
amount?: InputMaybe<Scalars['Int']>
export type QueryLoadAuthorsByArgs = {
limit?: InputMaybe<Scalars['Int']>
by?: InputMaybe<AuthorsBy>
offset?: InputMaybe<Scalars['Int']>
}
export type QueryLoadMessagesArgs = {
amount?: InputMaybe<Scalars['Int']>
chatId: Scalars['String']
export type QueryLoadChatsArgs = {
limit?: InputMaybe<Scalars['Int']>
offset?: InputMaybe<Scalars['Int']>
}
export type QueryLoadMessagesByArgs = {
limit?: InputMaybe<Scalars['Int']>
by: MessagesBy
offset?: InputMaybe<Scalars['Int']>
}
export type QueryLoadReactionsByArgs = {
offset?: InputMaybe<Scalars['Int']>
by: ReactionBy
limit?: InputMaybe<Scalars['Int']>
}
export type QueryLoadShoutsByArgs = {
limit?: InputMaybe<Scalars['Int']>
by?: InputMaybe<ShoutsBy>
offset?: InputMaybe<Scalars['Int']>
}
@ -443,100 +384,10 @@ export type QueryMarkdownBodyArgs = {
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 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 = {
amount?: InputMaybe<Scalars['Int']>
limit?: InputMaybe<Scalars['Int']>
offset?: InputMaybe<Scalars['Int']>
q: 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']
query: Scalars['String']
}
export type QuerySignInArgs = {
@ -545,32 +396,6 @@ export type QuerySignInArgs = {
password?: InputMaybe<Scalars['String']>
}
export type QueryTopAuthorsArgs = {
limit: Scalars['Int']
offset: Scalars['Int']
}
export type QueryTopCommentedArgs = {
limit: Scalars['Int']
offset: Scalars['Int']
}
export type QueryTopMonthArgs = {
limit: Scalars['Int']
offset: 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 = {
author: Scalars['String']
}
@ -587,10 +412,6 @@ export type QueryUserFollowedAuthorsArgs = {
slug: Scalars['String']
}
export type QueryUserFollowedCommunitiesArgs = {
slug: Scalars['String']
}
export type QueryUserFollowedTopicsArgs = {
slug: Scalars['String']
}
@ -599,10 +420,6 @@ export type QueryUserFollowersArgs = {
slug: Scalars['String']
}
export type QueryUserReactedShoutsArgs = {
slug: Scalars['String']
}
export type Rating = {
rater: Scalars['String']
value: Scalars['Int']
@ -625,6 +442,17 @@ export type Reaction = {
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 = {
body?: InputMaybe<Scalars['String']>
kind: Scalars['Int']
@ -708,6 +536,7 @@ export type Shout = {
lang?: Maybe<Scalars['String']>
layout?: Maybe<Scalars['String']>
mainTopic?: Maybe<Scalars['String']>
media?: Maybe<Scalars['String']>
publishedAt?: Maybe<Scalars['DateTime']>
publishedBy?: Maybe<User>
slug: Scalars['String']
@ -734,6 +563,20 @@ export type ShoutInput = {
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 = {
commented?: Maybe<Scalars['Int']>
ranking?: Maybe<Scalars['Int']>

View File

@ -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>

View File

@ -170,6 +170,10 @@
"Link sent, check your email": "Ссылка отправлена, проверьте почту",
"Create post": "Создать публикацию",
"Just start typing...": "Просто начните печатать...",
"Artworks": "Артворки",
"Audio": "Аудио",
"Video": "Видео",
"Literature": "Литература",
"We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или",
"register": "зарегистрируйтесь"
}

30
src/main.astro Normal file
View 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>

View File

@ -1,5 +1,5 @@
---
import Zine from '../layouts/zine.astro'
import Prerendered from '../main.astro'
import { Root } from '../components/Root'
import { initRouter } from '../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Prerendered>
<Root client:load />
</Zine>
</Prerendered>

View File

@ -1,6 +1,6 @@
---
import { Root } from '../components/Root'
import Zine from '../layouts/zine.astro'
import Prerendered from '../main.astro'
import { apiClient } from '../utils/apiClient'
import { initRouter } from '../stores/router'
@ -9,7 +9,7 @@ if (slug.endsWith('.map')) {
return Astro.redirect('/404')
}
const article = await apiClient.getArticle({ slug })
const article = await apiClient.loadShoutsBy({ by: { slug }, limit: 1})
if (!article) {
return Astro.redirect('/404')
}
@ -20,6 +20,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Root article={article} client:load />
</Zine>
<Prerendered>
<Root article={article.at(0)} client:load />
</Prerendered>

View File

@ -1,5 +1,5 @@
---
import Zine from '../../layouts/zine.astro'
import Prerendered from '../../main.astro'
import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Prerendered>
<Root client:load />
</Zine>
</Prerendered>

View File

@ -1,5 +1,5 @@
---
import Zine from '../../layouts/zine.astro'
import Prerendered from '../../main.astro'
import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Prerendered>
<Root client:load />
</Zine>
</Prerendered>

View File

@ -1,5 +1,5 @@
---
import Zine from '../../layouts/zine.astro'
import Prerendered from '../../main.astro'
import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router'
@ -9,6 +9,6 @@ initRouter(pathname, search)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Prerendered>
<Root client:load />
</Zine>
</Prerendered>

View File

@ -1,14 +1,18 @@
---
import Zine from '../../layouts/zine.astro'
import Prerendered from '../../main.astro'
import { Root } from '../../components/Root'
import { initRouter } from '../../stores/router'
const { pathname, search } = Astro.url
initRouter(pathname, search)
Astro.response.headers.set(
"Content-Security-Policy",
"frame-src 'self' https://*.discours.io https://widget.cloudpayments.ru; frame-ancestors 'self' https://*.discours.io https://widget.cloudpayments.ru;"
)
Astro.response.headers.set('Cache-Control', 's-maxage=1, stale-while-revalidate')
---
<Zine>
<Prerendered>
<Root client:load />
</Zine>
</Prerendered>

Some files were not shown because too many files have changed in this diff Show More