reset1
This commit is contained in:
parent
219e3e2325
commit
1e4138e40e
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,3 +28,4 @@ target
|
||||||
.output
|
.output
|
||||||
.vinxi
|
.vinxi
|
||||||
*.pem
|
*.pem
|
||||||
|
edge.*
|
||||||
|
|
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Launch browser against localhost",
|
|
||||||
"type": "chrome",
|
|
||||||
"request": "launch",
|
|
||||||
"url": "https://localhost:3000",
|
|
||||||
"webRoot": "${workspaceFolder}/src",
|
|
||||||
"sourceMaps": true,
|
|
||||||
"trace": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -30,12 +30,11 @@ export default defineConfig({
|
||||||
https: true
|
https: true
|
||||||
},
|
},
|
||||||
devOverlay: true,
|
devOverlay: true,
|
||||||
vite: {
|
|
||||||
build: {
|
build: {
|
||||||
sourcemap: true,
|
|
||||||
chunkSizeWarningLimit: 1024,
|
chunkSizeWarningLimit: 1024,
|
||||||
target: 'esnext'
|
target: 'esnext'
|
||||||
},
|
},
|
||||||
|
vite: {
|
||||||
envPrefix: 'PUBLIC_',
|
envPrefix: 'PUBLIC_',
|
||||||
plugins: [!isVercel && mkcert(), nodePolyfills(polyfillOptions), sassDts()],
|
plugins: [!isVercel && mkcert(), nodePolyfills(polyfillOptions), sassDts()],
|
||||||
css: {
|
css: {
|
||||||
|
|
|
@ -11,7 +11,7 @@ generates:
|
||||||
skipTypename: true
|
skipTypename: true
|
||||||
useTypeImports: true
|
useTypeImports: true
|
||||||
outputPath: './src/graphql/types/chat.gen.ts'
|
outputPath: './src/graphql/types/chat.gen.ts'
|
||||||
# namingConvention: lodash#pascalCase
|
# namingConvention: change-case#CamelCase # for generated types
|
||||||
|
|
||||||
# Generate types for core
|
# Generate types for core
|
||||||
src/graphql/schema/core.gen.ts:
|
src/graphql/schema/core.gen.ts:
|
||||||
|
@ -24,7 +24,7 @@ generates:
|
||||||
skipTypename: true
|
skipTypename: true
|
||||||
useTypeImports: true
|
useTypeImports: true
|
||||||
outputPath: './src/graphql/types/core.gen.ts'
|
outputPath: './src/graphql/types/core.gen.ts'
|
||||||
# namingConvention: lodash#pascalCase
|
# namingConvention: change-case#CamelCase # for generated types
|
||||||
hooks:
|
hooks:
|
||||||
afterAllFileWrite:
|
afterAllFileWrite:
|
||||||
- prettier --ignore-path .gitignore --write --plugin-search-dir=. src/graphql/schema/*.gen.ts
|
- prettier --ignore-path .gitignore --write --plugin-search-dir=. src/graphql/schema/*.gen.ts
|
||||||
|
|
380
package-lock.json
generated
380
package-lock.json
generated
|
@ -21,7 +21,7 @@
|
||||||
"@graphql-codegen/typescript-operations": "^4.2.3",
|
"@graphql-codegen/typescript-operations": "^4.2.3",
|
||||||
"@graphql-codegen/typescript-urql": "^4.0.0",
|
"@graphql-codegen/typescript-urql": "^4.0.0",
|
||||||
"@hocuspocus/provider": "^2.13.5",
|
"@hocuspocus/provider": "^2.13.5",
|
||||||
"@playwright/test": "^1.45.2",
|
"@playwright/test": "^1.45.3",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@solid-primitives/media": "^2.2.9",
|
"@solid-primitives/media": "^2.2.9",
|
||||||
"@solid-primitives/memo": "^1.3.9",
|
"@solid-primitives/memo": "^1.3.9",
|
||||||
|
@ -33,35 +33,35 @@
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.14.1",
|
"@solidjs/router": "^0.14.1",
|
||||||
"@solidjs/start": "^1.0.6",
|
"@solidjs/start": "^1.0.6",
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/extension-blockquote": "^2.5.4",
|
"@tiptap/extension-blockquote": "^2.5.5",
|
||||||
"@tiptap/extension-bold": "^2.5.4",
|
"@tiptap/extension-bold": "^2.5.5",
|
||||||
"@tiptap/extension-bubble-menu": "^2.5.4",
|
"@tiptap/extension-bubble-menu": "^2.5.5",
|
||||||
"@tiptap/extension-bullet-list": "^2.5.4",
|
"@tiptap/extension-bullet-list": "^2.5.5",
|
||||||
"@tiptap/extension-character-count": "^2.5.4",
|
"@tiptap/extension-character-count": "^2.5.5",
|
||||||
"@tiptap/extension-collaboration": "^2.5.4",
|
"@tiptap/extension-collaboration": "^2.5.5",
|
||||||
"@tiptap/extension-collaboration-cursor": "^2.5.4",
|
"@tiptap/extension-collaboration-cursor": "^2.5.5",
|
||||||
"@tiptap/extension-document": "^2.5.4",
|
"@tiptap/extension-document": "^2.5.5",
|
||||||
"@tiptap/extension-dropcursor": "^2.5.4",
|
"@tiptap/extension-dropcursor": "^2.5.5",
|
||||||
"@tiptap/extension-floating-menu": "^2.5.4",
|
"@tiptap/extension-floating-menu": "^2.5.5",
|
||||||
"@tiptap/extension-focus": "^2.5.4",
|
"@tiptap/extension-focus": "^2.5.5",
|
||||||
"@tiptap/extension-gapcursor": "^2.5.4",
|
"@tiptap/extension-gapcursor": "^2.5.5",
|
||||||
"@tiptap/extension-hard-break": "^2.5.4",
|
"@tiptap/extension-hard-break": "^2.5.5",
|
||||||
"@tiptap/extension-heading": "^2.5.4",
|
"@tiptap/extension-heading": "^2.5.5",
|
||||||
"@tiptap/extension-highlight": "^2.5.4",
|
"@tiptap/extension-highlight": "^2.5.5",
|
||||||
"@tiptap/extension-history": "^2.5.4",
|
"@tiptap/extension-history": "^2.5.5",
|
||||||
"@tiptap/extension-horizontal-rule": "^2.5.4",
|
"@tiptap/extension-horizontal-rule": "^2.5.5",
|
||||||
"@tiptap/extension-image": "^2.5.4",
|
"@tiptap/extension-image": "^2.5.5",
|
||||||
"@tiptap/extension-italic": "^2.5.4",
|
"@tiptap/extension-italic": "^2.5.5",
|
||||||
"@tiptap/extension-link": "^2.5.4",
|
"@tiptap/extension-link": "^2.5.5",
|
||||||
"@tiptap/extension-list-item": "^2.5.4",
|
"@tiptap/extension-list-item": "^2.5.5",
|
||||||
"@tiptap/extension-ordered-list": "^2.5.4",
|
"@tiptap/extension-ordered-list": "^2.5.5",
|
||||||
"@tiptap/extension-paragraph": "^2.5.4",
|
"@tiptap/extension-paragraph": "^2.5.5",
|
||||||
"@tiptap/extension-placeholder": "^2.5.4",
|
"@tiptap/extension-placeholder": "^2.5.5",
|
||||||
"@tiptap/extension-strike": "^2.5.4",
|
"@tiptap/extension-strike": "^2.5.5",
|
||||||
"@tiptap/extension-text": "^2.5.4",
|
"@tiptap/extension-text": "^2.5.5",
|
||||||
"@tiptap/extension-underline": "^2.5.4",
|
"@tiptap/extension-underline": "^2.5.5",
|
||||||
"@tiptap/extension-youtube": "^2.5.4",
|
"@tiptap/extension-youtube": "^2.5.5",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/cookie-signature": "^1.1.2",
|
"@types/cookie-signature": "^1.1.2",
|
||||||
"@types/node": "^20.14.11",
|
"@types/node": "^20.14.11",
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
"swiper": "^11.1.5",
|
"swiper": "^11.1.5",
|
||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.4",
|
||||||
"typograf": "^7.4.1",
|
"typograf": "^7.4.1",
|
||||||
"uniqolor": "^1.1.1",
|
"uniqolor": "^1.1.1",
|
||||||
"vinxi": "^0.4.1",
|
"vinxi": "^0.4.1",
|
||||||
|
@ -2604,9 +2604,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@graphql-tools/delegate": {
|
"node_modules/@graphql-tools/delegate": {
|
||||||
"version": "10.0.14",
|
"version": "10.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.14.tgz",
|
"resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.15.tgz",
|
||||||
"integrity": "sha512-mYrLtwVKTHg5F4OFrJbiL5F7dzopzGiac5ezkVrnlGNPBQ8GNCr1zo32c1rYyIbsa8fJSUvAJfJfFj6ipnutnw==",
|
"integrity": "sha512-18R4vcJWz/6pk6K9SslijR0jCSe0mAnSs0sd1eioTvSSCWiagPdCOOhaM9dPNfEnxp3TRHg3cnYqywRtJgKHvw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -4148,13 +4148,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.45.2",
|
"version": "1.45.3",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.2.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz",
|
||||||
"integrity": "sha512-JxG9eq92ET75EbVi3s+4sYbcG7q72ECeZNbdBlaMkGcNbiDQ4cAi8U2QP5oKkOx+1gpaiL1LDStmzCaEM1Z6fQ==",
|
"integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.45.2"
|
"playwright": "1.45.3"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
@ -4956,9 +4956,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/core": {
|
"node_modules/@tiptap/core": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.5.5.tgz",
|
||||||
"integrity": "sha512-Zs/hShr4+W02+0nOlpmr5cS2YjDRLqd+XMt+jsiQH0QNr3s1Lc82pfF6C3CjgLEZtdUzImZrW2ABtLlpvbogaA==",
|
"integrity": "sha512-VnAnyWnsqN65QijtUFHbe7EPSJCkhNEAwlatsG/HvrZvUv9KmoWWbMsHAU73wozKzPXR3nHRbCxN+LuxP5bADg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -4966,13 +4966,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-blockquote": {
|
"node_modules/@tiptap/extension-blockquote": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.5.5.tgz",
|
||||||
"integrity": "sha512-UqeJunZM3IiCQGZE0X5YNUOWYkuIieqrwPgOEghAIjnhDcQizQcouRQ5R7cwwv/scNr2JvZHncOTLrALV3Janw==",
|
"integrity": "sha512-K+fc++ASlgDRHN6i3j3JBGzWiDhhoZv0jCUB/l7Jzut4UfjIoWqKhmJajnp95Qu9tmwQUy9LMzHqG4G5wUsIsQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -4980,13 +4980,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-bold": {
|
"node_modules/@tiptap/extension-bold": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.5.5.tgz",
|
||||||
"integrity": "sha512-H5sjqloFMjq7VOSfE+U4T7dqGoflOiF6RW6/gZm/U6KYeHG2/bG0ktq7mWAnnhbiKiy7gUcxyJCV+ILdGX9C5g==",
|
"integrity": "sha512-vXqaeTKy4nf4X+s7NkFt0OsuS1eKMQhrdt7SzACf0gWi3M761WGkaKHy8XUlo7zhWhqHtkgey53Gaw0nbEY54Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -4994,13 +4994,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-bubble-menu": {
|
"node_modules/@tiptap/extension-bubble-menu": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.5.tgz",
|
||||||
"integrity": "sha512-GHwef912K1yd75pp9JGDnKSp1DvdOHH8BcHQv0no+a3q2ePFPYcgaSwVRR59jHRX9WzdVfoLcqDSAeoNGOrISw==",
|
"integrity": "sha512-7k0HqrnhQGVZk86MEc5vt8stNRxIY65AMjZfszY/mQw0Dza7EQig/9b/AEmi9n+TNW5/8Qu+OMJD9ln92d/Eog==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -5011,14 +5011,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-bullet-list": {
|
"node_modules/@tiptap/extension-bullet-list": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.5.5.tgz",
|
||||||
"integrity": "sha512-aAfpALeD6OxymkbtrzDqbgkAkzVVHudxOb8GsK1N6m42nFL7Q9JzHJ5/8KzB+xi25CcIbS+HmXJkRIQJXgNbSA==",
|
"integrity": "sha512-p89cTmGUoq3OEFzcS49iQ/tyQjDoKW1J0c7EghS7eU3wHVxeo/Ke110cY2W5o1e4KMFowo3a4jVsxKuCQJkWrA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5026,13 +5026,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-character-count": {
|
"node_modules/@tiptap/extension-character-count": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-character-count/-/extension-character-count-2.5.5.tgz",
|
||||||
"integrity": "sha512-6qwt+81I+y+t3eoFPmCG2ouQce2RccwyiUC0ZOPTG1eUB+5yXmyIwBYI4aOM4TEfxNizyaZtQw32CDdAhMr3YA==",
|
"integrity": "sha512-rh6q3YeuLV8PnaKUqQbnOQ16obXPcqsqnQ+y1XLWH74lHwdvbOvE1BCvSZD0ULPI9EcOtvhdZEZkDxlqQ9H3jg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5040,14 +5040,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-collaboration": {
|
"node_modules/@tiptap/extension-collaboration": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-2.5.5.tgz",
|
||||||
"integrity": "sha512-CpQdbr7XpQaVqRFo/A1DchrQZMDb8vrkP+FcUIgvHN0b8hwKDmXRAHDtuk8yTTEatW1EqpX8lx8UxaUTcDNbIg==",
|
"integrity": "sha512-HpDW+1VTKdtK7BglQNLFv2UzJIxtzZ9zvT+wdYDWPB3ZstoL8drpp4wGP2xt3tbki6wzGpUFkDCpVNl0oOunXQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5055,15 +5055,15 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4",
|
"@tiptap/pm": "^2.5.5",
|
||||||
"y-prosemirror": "^1.2.6"
|
"y-prosemirror": "^1.2.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-collaboration-cursor": {
|
"node_modules/@tiptap/extension-collaboration-cursor": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration-cursor/-/extension-collaboration-cursor-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration-cursor/-/extension-collaboration-cursor-2.5.5.tgz",
|
||||||
"integrity": "sha512-M32JChnP5RVdr1n+Tf0gF9bxx0gHvc0uV4SDxCMN3uaNH5YpcofmvKElS60rDGVfCdRTId/aj7P3AtwrvRlYdQ==",
|
"integrity": "sha512-DWX3eOplWyLegOWeZa0CAVbb9/UYbngiZyKjVMpDlx5qzhUuLL+Df54/UGKqB1ZrBZrxKCVQE3APMyXkxI/2VQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5071,14 +5071,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"y-prosemirror": "^1.2.6"
|
"y-prosemirror": "^1.2.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-document": {
|
"node_modules/@tiptap/extension-document": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.5.5.tgz",
|
||||||
"integrity": "sha512-4RDrhASxCTOZETYhIhEW1TfZqx3Tm+LQxouvBMFyODmT1PSgsg5Xz1FYpDPr+J49bGAK0Pr9ae0XcGW011L3sA==",
|
"integrity": "sha512-MIjYO63JepcJW37PQuKVmYuZFqkQOZ/12tV0YLU4o6gmGVdqJS0+3md9CdnyUFUDIo7x6TBh8r5i5L2xQpm3Sg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5086,13 +5086,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-dropcursor": {
|
"node_modules/@tiptap/extension-dropcursor": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.5.5.tgz",
|
||||||
"integrity": "sha512-jzSnuuYhlc0SsHvAteWkE9TJy3eRwkxQs4MO2JxALOzJECN4G82nlX8vciihBD6xf7lVgVSBACejK9+rsTHqCg==",
|
"integrity": "sha512-+K/qd115c3zFgHdvxtOkZhSTKNyPpjM0Np2v4cehqn0j+/3stOMGlAH2Jm/b2L8RylFKGtQP1b/1wsKY5feuAg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5100,14 +5100,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-floating-menu": {
|
"node_modules/@tiptap/extension-floating-menu": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.5.tgz",
|
||||||
"integrity": "sha512-EqD4rgi3UhnDcV3H1+ndAS4Ue2zpsU7hFKoevOIV6GS7xVnWN70AGt6swH24QzuHKKISFtWoLpKjrwRORNIxuA==",
|
"integrity": "sha512-1mgpxZGfy1ziNSvWz6m1nGb9ZF9fVVz4X4XwrIqwGw1Vqt9oXflm6puglnzwVLDeaMDT014VUfczJ4My3wDZzA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -5118,14 +5118,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-focus": {
|
"node_modules/@tiptap/extension-focus": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-focus/-/extension-focus-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-focus/-/extension-focus-2.5.5.tgz",
|
||||||
"integrity": "sha512-/Iq++93f9S+bNJzj3OmgOydCO58VfAhmnsImbGK/GmxV39hHbgJdazxMugwdQlvrY/oe3+Y+WY8ZI1WlWwTJ4g==",
|
"integrity": "sha512-c5ul5PNl/2HcYwEPu1kjjs/u8N5BtLnreeUyb223y8i4BEcjydVlnCfVVUdonQIWnj0mKQ8KZbyLTSYdijDsVA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5133,14 +5133,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-gapcursor": {
|
"node_modules/@tiptap/extension-gapcursor": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.5.5.tgz",
|
||||||
"integrity": "sha512-wzTh1piODZBS0wmuDgPjjg8PQwclYa5LssnxDIo9pDSnt4l3AfHSAJIJSGIfgt96KnzF1wqRTRpe08qNa1n7/g==",
|
"integrity": "sha512-An/HwTheUP+D4UU1GVy2e4ypqA1TanZ7haNcm5WB+wSZQo6UNPIszIa49TTGenkk86hP2DH9cQSlTREsyAW6wg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5148,14 +5148,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-hard-break": {
|
"node_modules/@tiptap/extension-hard-break": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.5.5.tgz",
|
||||||
"integrity": "sha512-nLn6HP9tqgdGGwbMORXVtcY30DTGctYFaWADRthvBjVgacYSeKlhUcsSu3YgaxtbxZp6BhfRvD2kKrxyQsSjnQ==",
|
"integrity": "sha512-VtrwKU0LYS/0rfH5rGz8ztKwA0bsHRyBF53G7aP2FS4BiN8aOEu8t7VkvBZAewXDITDah9K6rqfXk+MNwoul2Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5163,13 +5163,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-heading": {
|
"node_modules/@tiptap/extension-heading": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.5.5.tgz",
|
||||||
"integrity": "sha512-DuAB58/e7eho1rkyad0Z/SjW+EB+H2hRqHlswEeZZYhBTjzey5UmBwkMWTGC/SQiRisx1xYQYTd8T0fiABi5hw==",
|
"integrity": "sha512-NDnXOR6HmnkBA68oZTVf0BT5t8ikVFv9X6Ft/O5oU6IuzCswS8BUb5MJIhKBWQXJTsCNbC6EYl5jhJ3hukLcHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5177,13 +5177,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-highlight": {
|
"node_modules/@tiptap/extension-highlight": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.5.5.tgz",
|
||||||
"integrity": "sha512-TSYnFBluZu1YQdTCyXl2wuxFuhFUYFzbaV0f1wq2P2Nc8U2OiiuaNz+QggHw5Hf3ILzkRxQCUQnq97Q/5smMwQ==",
|
"integrity": "sha512-NqMmL9/82288DI1trnuxB3hcf61x+iDKFvNAE+thW6MmY6ZWi47bEnfUQGwDeInxH81NfMhTTSxuXmnuO10noQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5191,13 +5191,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-history": {
|
"node_modules/@tiptap/extension-history": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.5.5.tgz",
|
||||||
"integrity": "sha512-WB1fZYGIlpahAD6Ba+mj9vIb1tk8S3TsADXDFKxLVpZWZPQ+B7duGJP7g/vRH2XAXEs836JzC2oxjKeaop3k7A==",
|
"integrity": "sha512-CYxFpE9wayc+iZQIlXd3cbq47WP+KqjDhprbKF5Tb7+WoWLS2FB5WK3n+r/SrcoIaslIt5SYDRQPzx4fS3N7LA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5205,14 +5205,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.5.5.tgz",
|
||||||
"integrity": "sha512-uXLDe/iyzQbyfDkJ8kE5XaAkY3EOcbTFLjbueqGlkbWtjJgy+3LysGvh8fQj8PAOaIBMaFRFhTq7GMbW2ebRog==",
|
"integrity": "sha512-8oV0oLgGwJqr44wk7+bHxTAenR0bvk9aVdmE/owg1oy2tkSX0bwtvQEOnwwxtfPJGTwq8JGhefUGYcpHfG2YYg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5220,14 +5220,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-image": {
|
"node_modules/@tiptap/extension-image": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.5.5.tgz",
|
||||||
"integrity": "sha512-4ySSP7iPsbbo1SlPJYj546TKettuO6FGY5MQKxH8AGnZWyQGZYl89GpU1iGFAaeHq4dKUemM5D3ikgSynEQLow==",
|
"integrity": "sha512-DvnKf3XCGf/2GQrqtwgKwgaeqIn2dXgHTire0E2aPj8T939jA4ApX5qLPumndHX0rAckX5VAbnJjQeoxtEmMFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5235,13 +5235,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-italic": {
|
"node_modules/@tiptap/extension-italic": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.5.5.tgz",
|
||||||
"integrity": "sha512-TAhtl/fNBgv1elzF3HWES8uwVdpKBSYrq1e6yeYfj74mQn//3ksvdhWQrLzc1e+zcoHbk1PeOp/5ODdPuZ6tkg==",
|
"integrity": "sha512-PEeI68/u7Bm4n4xIcxVAV12jPhEa72fpHRnYfJe4CGp4x8mJfz/dowKN/P0/6CfjROB7Q8rY26u5E9fS+Cg73w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5249,13 +5249,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-link": {
|
"node_modules/@tiptap/extension-link": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.5.5.tgz",
|
||||||
"integrity": "sha512-xTB/+T6SHHCXInJni8WdqOfF40a/MiFUf5OoWW9cPrApx3I7TzJ9j8/WDshM0BOnDDw80w1bl9F2zkUQjC0Y2A==",
|
"integrity": "sha512-zVpNvMD8R9uW1SX1PJoj3fLyOHwuFWqiqEHN2KWfLbEnbL/KXNnpIyKdpHnI9lqFrsMf2dmyZCS3R6xIrynviQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -5266,14 +5266,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-list-item": {
|
"node_modules/@tiptap/extension-list-item": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.5.5.tgz",
|
||||||
"integrity": "sha512-bPxUCFt9HnAfoaZQgwqCfRAZ6L3QlYhIRDDbOvZag7IxCdQuZmeY4k5OZfQIGijNDTag7CN9cdL4fl9rnm6/sQ==",
|
"integrity": "sha512-CfNVCP8Pqqgr7fAQAuRvZikzXT9vCEogcW7/C16cyGykbUJBqBmpsyHcAlj7XwsBFUuJ5MCeULtk/0frUI5fMQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5281,13 +5281,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-ordered-list": {
|
"node_modules/@tiptap/extension-ordered-list": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.5.5.tgz",
|
||||||
"integrity": "sha512-cl3cTJitY6yDUmxqgjDUtDWCyX1VVsZNJ6i9yiPeARcxvzFc81KmUJxTGl8WPT5TjqmM+TleRkZjsxgvXX57+Q==",
|
"integrity": "sha512-wElnGQJhKznayP7tVGl/r42mj1dLEeU+Ln1Y3wF/m+nFwKl2Gpsy01PjBy5sXPUgskGSWgMlOgJrQyMvH9AuAw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5295,13 +5295,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-paragraph": {
|
"node_modules/@tiptap/extension-paragraph": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.5.5.tgz",
|
||||||
"integrity": "sha512-pC1YIkkRPXoU0eDrhfAf8ZrFJQzvw2ftP6KRhLnnSw/Ot1DOjT1r95l7zsFefS9oCDMT/L4HghTAiPZ4rcpPbg==",
|
"integrity": "sha512-XZO1rqsU1vlt9qeG2pVVAt2gXjD0twl2D+uxy4Nw6gxqbhSgfbNq3RP72mmtcS4KyFJi7ETANpcRpb8ZNvXfmg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5309,13 +5309,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-placeholder": {
|
"node_modules/@tiptap/extension-placeholder": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.5.5.tgz",
|
||||||
"integrity": "sha512-mcj4j2Z/L1H5dzWHbbWChuAdJK9F2p06fcjqL4iyJtVx38QQFzCdVmGaTAim8CLp/EynbAOYJ5gk9w2PTdv7+w==",
|
"integrity": "sha512-SwWLYdyrMeoVUQdivkIJ4kkAcb38pykxSetlrXitfUmnkwv0/fi+p76Rickf+roudWPsfzqvgvJ4gT6OAOJrGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5323,14 +5323,14 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/pm": "^2.5.4"
|
"@tiptap/pm": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-strike": {
|
"node_modules/@tiptap/extension-strike": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.5.5.tgz",
|
||||||
"integrity": "sha512-OSN6ePbCwEhi3hYZZOPow/P9Ym2Kv3NhVbUvasjZCiqQuk8TGc33xirPWl9DTjb/BLfL66TtJ2tKUEVOKl5dKg==",
|
"integrity": "sha512-xnVdSsP7+4yQ1E+rI77ZHvzDH1Gwe2Ty1tgXeOaLjt3RfeVx4xy75o09yHzab6J4hgPebonoXKbZV0JVTGnjtQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5338,13 +5338,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-text": {
|
"node_modules/@tiptap/extension-text": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.5.5.tgz",
|
||||||
"integrity": "sha512-+3x/hYqhmCYbvedCcQzQHFtZ5MAcMOlKuczomZtygf8AfDfuQVrG1m4GoJyNzJdqxjN80/xq4e2vDVvqQxYTCw==",
|
"integrity": "sha512-8c/hxcw7t/S3iKGSFwGNxC2I6AkKpRiySQJ95ML2miwSOAxWhnltoYYV7gobWCRgm25lnvzX/Z6BdpFzXBrBKA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5352,13 +5352,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-underline": {
|
"node_modules/@tiptap/extension-underline": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.5.5.tgz",
|
||||||
"integrity": "sha512-o8T3oWbniA3rLo6LkslPRF8pwdjsaHXJCeK4KmKeCyYhTpMfjypT3uptd+VSSJ4iQkaiFInKeIUOBqqEQ9cADw==",
|
"integrity": "sha512-3uog8d4G/AdqaJC8qutIIgkYnU2TfXW3QbtEy0Yg2WdjCz97bWXkFkNhhVZM/hvXjFCbYboRN5HLcIHl8+Zgmg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5366,13 +5366,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/extension-youtube": {
|
"node_modules/@tiptap/extension-youtube": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-youtube/-/extension-youtube-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-youtube/-/extension-youtube-2.5.5.tgz",
|
||||||
"integrity": "sha512-iHcvXOA32MZsVJTT7mvZ1CWKUo2quQMQXfBniizLm0lUG1ftSioqnDuXy4kEjeCBR2cnZr3yph6tbG/pF0RcHg==",
|
"integrity": "sha512-dPLSLsEiMdXB5q0YDRJKWiiTqdFiSeyaC5qWLio4SHYfyTYT1+M2Wwox+5Dm/OSgCHpxpT2W8JRt+H4+P38t9A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -5380,13 +5380,13 @@
|
||||||
"url": "https://github.com/sponsors/ueberdosis"
|
"url": "https://github.com/sponsors/ueberdosis"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tiptap/core": "^2.5.4"
|
"@tiptap/core": "^2.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tiptap/pm": {
|
"node_modules/@tiptap/pm": {
|
||||||
"version": "2.5.4",
|
"version": "2.5.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.5.5.tgz",
|
||||||
"integrity": "sha512-oFIsuniptdUXn93x4aM2sVN3hYKo9Fj55zAkYrWhwxFYUYcPxd5ibra2we+wRK5TaiPu098wpC+yMSTZ/KKMpA==",
|
"integrity": "sha512-ppePiLaeG6IKkm8Yq+mRENT4LIAS4qQyLT8EnKadznaTL6SNj/72mm0MjD44URkM38ySzIyvt/vqHDapNK0Hww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
|
@ -7057,9 +7057,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001642",
|
"version": "1.0.30001643",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz",
|
||||||
"integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==",
|
"integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
@ -8441,9 +8441,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.4.832",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.832.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz",
|
||||||
"integrity": "sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==",
|
"integrity": "sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
@ -11898,9 +11898,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/node-releases": {
|
"node_modules/node-releases": {
|
||||||
"version": "2.0.17",
|
"version": "2.0.18",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.17.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||||
"integrity": "sha512-Ww6ZlOiEQfPfXM45v17oabk77Z7mg5bOt7AjDyzy7RjK9OrLrLC8dyZQoAPEOtFX9SaNf1Tdvr5gRJWdTJj7GA==",
|
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
@ -12682,13 +12682,13 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.45.2",
|
"version": "1.45.3",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz",
|
||||||
"integrity": "sha512-ReywF2t/0teRvNBpfIgh5e4wnrI/8Su8ssdo5XsQKpjxJj+jspm00jSoz9BTg91TT0c9HRjXO7LBNVrgYj9X0g==",
|
"integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.45.2"
|
"playwright-core": "1.45.3"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
@ -12701,9 +12701,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.45.2",
|
"version": "1.45.3",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz",
|
||||||
"integrity": "sha512-ha175tAWb0dTK0X4orvBIqi3jGEt701SMxMhyujxNrgd8K0Uy5wMSwwcQHtyB4om7INUkfndx02XnQ2p6dvLDw==",
|
"integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -13891,9 +13891,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass/node_modules/immutable": {
|
"node_modules/sass/node_modules/immutable": {
|
||||||
"version": "4.3.6",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
|
||||||
"integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==",
|
"integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
@ -15196,9 +15196,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.5.3",
|
"version": "5.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||||
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
|
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
64
package.json
64
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "discoursio-webapp",
|
"name": "discoursio-webapp",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.9.6",
|
"version": "0.9.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vinxi dev",
|
"dev": "vinxi dev",
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
"@graphql-codegen/typescript-operations": "^4.2.3",
|
"@graphql-codegen/typescript-operations": "^4.2.3",
|
||||||
"@graphql-codegen/typescript-urql": "^4.0.0",
|
"@graphql-codegen/typescript-urql": "^4.0.0",
|
||||||
"@hocuspocus/provider": "^2.13.5",
|
"@hocuspocus/provider": "^2.13.5",
|
||||||
"@playwright/test": "^1.45.2",
|
"@playwright/test": "^1.45.3",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@solid-primitives/media": "^2.2.9",
|
"@solid-primitives/media": "^2.2.9",
|
||||||
"@solid-primitives/memo": "^1.3.9",
|
"@solid-primitives/memo": "^1.3.9",
|
||||||
|
@ -37,35 +37,35 @@
|
||||||
"@solidjs/meta": "^0.29.4",
|
"@solidjs/meta": "^0.29.4",
|
||||||
"@solidjs/router": "^0.14.1",
|
"@solidjs/router": "^0.14.1",
|
||||||
"@solidjs/start": "^1.0.6",
|
"@solidjs/start": "^1.0.6",
|
||||||
"@tiptap/core": "^2.5.4",
|
"@tiptap/core": "^2.5.5",
|
||||||
"@tiptap/extension-blockquote": "^2.5.4",
|
"@tiptap/extension-blockquote": "^2.5.5",
|
||||||
"@tiptap/extension-bold": "^2.5.4",
|
"@tiptap/extension-bold": "^2.5.5",
|
||||||
"@tiptap/extension-bubble-menu": "^2.5.4",
|
"@tiptap/extension-bubble-menu": "^2.5.5",
|
||||||
"@tiptap/extension-bullet-list": "^2.5.4",
|
"@tiptap/extension-bullet-list": "^2.5.5",
|
||||||
"@tiptap/extension-character-count": "^2.5.4",
|
"@tiptap/extension-character-count": "^2.5.5",
|
||||||
"@tiptap/extension-collaboration": "^2.5.4",
|
"@tiptap/extension-collaboration": "^2.5.5",
|
||||||
"@tiptap/extension-collaboration-cursor": "^2.5.4",
|
"@tiptap/extension-collaboration-cursor": "^2.5.5",
|
||||||
"@tiptap/extension-document": "^2.5.4",
|
"@tiptap/extension-document": "^2.5.5",
|
||||||
"@tiptap/extension-dropcursor": "^2.5.4",
|
"@tiptap/extension-dropcursor": "^2.5.5",
|
||||||
"@tiptap/extension-floating-menu": "^2.5.4",
|
"@tiptap/extension-floating-menu": "^2.5.5",
|
||||||
"@tiptap/extension-focus": "^2.5.4",
|
"@tiptap/extension-focus": "^2.5.5",
|
||||||
"@tiptap/extension-gapcursor": "^2.5.4",
|
"@tiptap/extension-gapcursor": "^2.5.5",
|
||||||
"@tiptap/extension-hard-break": "^2.5.4",
|
"@tiptap/extension-hard-break": "^2.5.5",
|
||||||
"@tiptap/extension-heading": "^2.5.4",
|
"@tiptap/extension-heading": "^2.5.5",
|
||||||
"@tiptap/extension-highlight": "^2.5.4",
|
"@tiptap/extension-highlight": "^2.5.5",
|
||||||
"@tiptap/extension-history": "^2.5.4",
|
"@tiptap/extension-history": "^2.5.5",
|
||||||
"@tiptap/extension-horizontal-rule": "^2.5.4",
|
"@tiptap/extension-horizontal-rule": "^2.5.5",
|
||||||
"@tiptap/extension-image": "^2.5.4",
|
"@tiptap/extension-image": "^2.5.5",
|
||||||
"@tiptap/extension-italic": "^2.5.4",
|
"@tiptap/extension-italic": "^2.5.5",
|
||||||
"@tiptap/extension-link": "^2.5.4",
|
"@tiptap/extension-link": "^2.5.5",
|
||||||
"@tiptap/extension-list-item": "^2.5.4",
|
"@tiptap/extension-list-item": "^2.5.5",
|
||||||
"@tiptap/extension-ordered-list": "^2.5.4",
|
"@tiptap/extension-ordered-list": "^2.5.5",
|
||||||
"@tiptap/extension-paragraph": "^2.5.4",
|
"@tiptap/extension-paragraph": "^2.5.5",
|
||||||
"@tiptap/extension-placeholder": "^2.5.4",
|
"@tiptap/extension-placeholder": "^2.5.5",
|
||||||
"@tiptap/extension-strike": "^2.5.4",
|
"@tiptap/extension-strike": "^2.5.5",
|
||||||
"@tiptap/extension-text": "^2.5.4",
|
"@tiptap/extension-text": "^2.5.5",
|
||||||
"@tiptap/extension-underline": "^2.5.4",
|
"@tiptap/extension-underline": "^2.5.5",
|
||||||
"@tiptap/extension-youtube": "^2.5.4",
|
"@tiptap/extension-youtube": "^2.5.5",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/cookie-signature": "^1.1.2",
|
"@types/cookie-signature": "^1.1.2",
|
||||||
"@types/node": "^20.14.11",
|
"@types/node": "^20.14.11",
|
||||||
|
@ -102,7 +102,7 @@
|
||||||
"swiper": "^11.1.5",
|
"swiper": "^11.1.5",
|
||||||
"throttle-debounce": "^5.0.2",
|
"throttle-debounce": "^5.0.2",
|
||||||
"tslib": "^2.6.3",
|
"tslib": "^2.6.3",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.4",
|
||||||
"typograf": "^7.4.1",
|
"typograf": "^7.4.1",
|
||||||
"uniqolor": "^1.1.1",
|
"uniqolor": "^1.1.1",
|
||||||
"vinxi": "^0.4.1",
|
"vinxi": "^0.4.1",
|
||||||
|
|
|
@ -19,8 +19,7 @@ import {
|
||||||
import { AuthorLink } from '../../Author/AuthorLink'
|
import { AuthorLink } from '../../Author/AuthorLink'
|
||||||
import { Userpic } from '../../Author/Userpic'
|
import { Userpic } from '../../Author/Userpic'
|
||||||
import { CommentDate } from '../CommentDate'
|
import { CommentDate } from '../CommentDate'
|
||||||
import { RatingControl as CommentRatingControl } from '../RatingControl'
|
import { CommentRatingControl } from '../CommentRatingControl'
|
||||||
|
|
||||||
import styles from './Comment.module.scss'
|
import styles from './Comment.module.scss'
|
||||||
|
|
||||||
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
|
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
|
||||||
|
|
122
src/components/Article/CommentRatingControl.tsx
Normal file
122
src/components/Article/CommentRatingControl.tsx
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import { createMemo } from 'solid-js'
|
||||||
|
|
||||||
|
import { useFeed } from '~/context/feed'
|
||||||
|
import { useLocalize } from '~/context/localize'
|
||||||
|
import { useReactions } from '~/context/reactions'
|
||||||
|
import { useSession } from '~/context/session'
|
||||||
|
import { useSnackbar } from '~/context/ui'
|
||||||
|
import { Reaction, ReactionKind } from '~/graphql/schema/core.gen'
|
||||||
|
import { Popup } from '../_shared/Popup'
|
||||||
|
import { VotersList } from '../_shared/VotersList'
|
||||||
|
|
||||||
|
import styles from './CommentRatingControl.module.scss'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
comment: Reaction
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CommentRatingControl = (props: Props) => {
|
||||||
|
const { t } = useLocalize()
|
||||||
|
const { loadShout } = useFeed()
|
||||||
|
const { session } = useSession()
|
||||||
|
const uid = createMemo<number>(() => session()?.user?.app_data?.profile?.id || 0)
|
||||||
|
const { showSnackbar } = useSnackbar()
|
||||||
|
const { reactionEntities, createReaction, deleteReaction, loadReactionsBy } = useReactions()
|
||||||
|
|
||||||
|
const checkReaction = (reactionKind: ReactionKind) =>
|
||||||
|
Object.values(reactionEntities).some(
|
||||||
|
(r) =>
|
||||||
|
r.kind === reactionKind &&
|
||||||
|
r.created_by.id === uid() &&
|
||||||
|
r.shout.id === props.comment.shout.id &&
|
||||||
|
r.reply_to === props.comment.id
|
||||||
|
)
|
||||||
|
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||||
|
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
|
||||||
|
const canVote = createMemo(() => uid() !== props.comment.created_by.id)
|
||||||
|
|
||||||
|
const commentRatingReactions = createMemo(() =>
|
||||||
|
Object.values(reactionEntities).filter(
|
||||||
|
(r) =>
|
||||||
|
[ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) &&
|
||||||
|
r.shout.id === props.comment.shout.id &&
|
||||||
|
r.reply_to === props.comment.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const deleteCommentReaction = async (reactionKind: ReactionKind) => {
|
||||||
|
const reactionToDelete = Object.values(reactionEntities).find(
|
||||||
|
(r) =>
|
||||||
|
r.kind === reactionKind &&
|
||||||
|
r.created_by.id === uid() &&
|
||||||
|
r.shout.id === props.comment.shout.id &&
|
||||||
|
r.reply_to === props.comment.id
|
||||||
|
)
|
||||||
|
if (reactionToDelete) return deleteReaction(reactionToDelete.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRatingChange = async (isUpvote: boolean) => {
|
||||||
|
try {
|
||||||
|
if (isUpvoted()) {
|
||||||
|
await deleteCommentReaction(ReactionKind.Like)
|
||||||
|
} else if (isDownvoted()) {
|
||||||
|
await deleteCommentReaction(ReactionKind.Dislike)
|
||||||
|
} else {
|
||||||
|
await createReaction({
|
||||||
|
reaction: {
|
||||||
|
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
||||||
|
shout: props.comment.shout.id,
|
||||||
|
reply_to: props.comment.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
showSnackbar({ type: 'error', body: t('Error') })
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadShout(props.comment.shout.slug)
|
||||||
|
await loadReactionsBy({
|
||||||
|
by: { shout: props.comment.shout.slug }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={styles.commentRating}>
|
||||||
|
<button
|
||||||
|
role="button"
|
||||||
|
disabled={!(canVote() && uid())}
|
||||||
|
onClick={() => handleRatingChange(true)}
|
||||||
|
class={clsx(styles.commentRatingControl, styles.commentRatingControlUp, {
|
||||||
|
[styles.voted]: isUpvoted()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<Popup
|
||||||
|
trigger={
|
||||||
|
<div
|
||||||
|
class={clsx(styles.commentRatingValue, {
|
||||||
|
[styles.commentRatingPositive]: (props.comment?.stat?.rating || 0) > 0,
|
||||||
|
[styles.commentRatingNegative]: (props.comment?.stat?.rating || 0) < 0
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{props.comment?.stat?.rating || 0}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
variant="tiny"
|
||||||
|
>
|
||||||
|
<VotersList
|
||||||
|
reactions={commentRatingReactions()}
|
||||||
|
fallbackMessage={t('This comment has not yet been rated')}
|
||||||
|
/>
|
||||||
|
</Popup>
|
||||||
|
<button
|
||||||
|
role="button"
|
||||||
|
disabled={!(canVote() && uid())}
|
||||||
|
onClick={() => handleRatingChange(false)}
|
||||||
|
class={clsx(styles.commentRatingControl, styles.commentRatingControlDown, {
|
||||||
|
[styles.voted]: isDownvoted()
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,15 +1,14 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { For, Show, createMemo, createSignal, lazy, onMount } from 'solid-js'
|
import { For, Show, createMemo, createSignal, lazy, onMount } from 'solid-js'
|
||||||
|
|
||||||
import { useFeed } from '~/context/feed'
|
import { useFeed } from '~/context/feed'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { COMMENTS_PER_PAGE, useReactions } from '~/context/reactions'
|
import { useReactions } from '~/context/reactions'
|
||||||
import { useSession } from '~/context/session'
|
import { useSession } from '~/context/session'
|
||||||
import { Reaction, ReactionKind, ReactionSort, Shout } from '~/graphql/schema/core.gen'
|
import { Author, Reaction, ReactionKind, ReactionSort } from '~/graphql/schema/core.gen'
|
||||||
import { byCreated, byStat } from '~/lib/sort'
|
import { byCreated, byStat } from '~/lib/sort'
|
||||||
import { SortFunction } from '~/types/common'
|
import { SortFunction } from '~/types/common'
|
||||||
import { Button } from '../_shared/Button'
|
import { Button } from '../_shared/Button'
|
||||||
import { InlineLoader } from '../_shared/InlineLoader'
|
|
||||||
import { LoadMoreItems, LoadMoreWrapper } from '../_shared/LoadMoreWrapper'
|
|
||||||
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
|
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
|
||||||
import styles from './Article.module.scss'
|
import styles from './Article.module.scss'
|
||||||
import { Comment } from './Comment'
|
import { Comment } from './Comment'
|
||||||
|
@ -17,21 +16,21 @@ import { Comment } from './Comment'
|
||||||
const SimplifiedEditor = lazy(() => import('../Editor/SimplifiedEditor'))
|
const SimplifiedEditor = lazy(() => import('../Editor/SimplifiedEditor'))
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
shout: Shout
|
articleAuthors: Author[]
|
||||||
|
shoutSlug: string
|
||||||
|
shoutId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CommentsTree = (props: Props) => {
|
export const CommentsTree = (props: Props) => {
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { reactionEntities, createReaction, loadShoutComments } = useReactions()
|
|
||||||
const { seen } = useFeed()
|
|
||||||
const [commentsOrder, setCommentsOrder] = createSignal<ReactionSort>(ReactionSort.Newest)
|
const [commentsOrder, setCommentsOrder] = createSignal<ReactionSort>(ReactionSort.Newest)
|
||||||
const [onlyNew, setOnlyNew] = createSignal(false)
|
const [onlyNew, setOnlyNew] = createSignal(false)
|
||||||
const [newReactions, setNewReactions] = createSignal<Reaction[]>([])
|
const [newReactions, setNewReactions] = createSignal<Reaction[]>([])
|
||||||
const [clearEditor, setClearEditor] = createSignal(false)
|
const [clearEditor, setClearEditor] = createSignal(false)
|
||||||
const [clickedReplyId, setClickedReplyId] = createSignal<number>()
|
const [clickedReplyId, setClickedReplyId] = createSignal<number>()
|
||||||
|
const { reactionEntities, createReaction, loadReactionsBy } = useReactions()
|
||||||
|
|
||||||
const shoutLastSeen = createMemo(() => seen()[props.shout.slug] ?? 0)
|
|
||||||
const comments = createMemo(() =>
|
const comments = createMemo(() =>
|
||||||
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
|
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
|
||||||
)
|
)
|
||||||
|
@ -49,9 +48,12 @@ export const CommentsTree = (props: Props) => {
|
||||||
}
|
}
|
||||||
return newSortedComments
|
return newSortedComments
|
||||||
})
|
})
|
||||||
|
const { seen } = useFeed()
|
||||||
|
const shoutLastSeen = createMemo(() => seen()[props.shoutSlug] ?? 0)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const currentDate = new Date()
|
const currentDate = new Date()
|
||||||
const setCookie = () => localStorage?.setItem(`${props.shout.slug}`, `${currentDate}`)
|
const setCookie = () => localStorage?.setItem(`${props.shoutSlug}`, `${currentDate}`)
|
||||||
if (!shoutLastSeen()) {
|
if (!shoutLastSeen()) {
|
||||||
setCookie()
|
setCookie()
|
||||||
} else if (currentDate.getTime() > shoutLastSeen()) {
|
} else if (currentDate.getTime() > shoutLastSeen()) {
|
||||||
|
@ -69,18 +71,6 @@ export const CommentsTree = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const [posting, setPosting] = createSignal(false)
|
const [posting, setPosting] = createSignal(false)
|
||||||
const [commentsLoading, setCommentsLoading] = createSignal(false)
|
|
||||||
const [pagination, setPagination] = createSignal(0)
|
|
||||||
const loadMoreComments = async () => {
|
|
||||||
setCommentsLoading(true)
|
|
||||||
const next = pagination() + 1
|
|
||||||
const offset = next * COMMENTS_PER_PAGE
|
|
||||||
const rrr = await loadShoutComments(props.shout.id, COMMENTS_PER_PAGE, offset)
|
|
||||||
rrr && setPagination(next)
|
|
||||||
setCommentsLoading(false)
|
|
||||||
return rrr as LoadMoreItems
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmitComment = async (value: string) => {
|
const handleSubmitComment = async (value: string) => {
|
||||||
setPosting(true)
|
setPosting(true)
|
||||||
try {
|
try {
|
||||||
|
@ -88,17 +78,18 @@ export const CommentsTree = (props: Props) => {
|
||||||
reaction: {
|
reaction: {
|
||||||
kind: ReactionKind.Comment,
|
kind: ReactionKind.Comment,
|
||||||
body: value,
|
body: value,
|
||||||
shout: props.shout.id
|
shout: props.shoutId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setClearEditor(true)
|
setClearEditor(true)
|
||||||
await loadMoreComments()
|
await loadReactionsBy({ by: { shout: props.shoutSlug } })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[handleCreate reaction]:', error)
|
console.error('[handleCreate reaction]:', error)
|
||||||
}
|
}
|
||||||
setClearEditor(false)
|
setClearEditor(false)
|
||||||
setPosting(false)
|
setPosting(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div class={styles.commentsHeaderWrapper}>
|
<div class={styles.commentsHeaderWrapper}>
|
||||||
|
@ -136,24 +127,12 @@ export const CommentsTree = (props: Props) => {
|
||||||
</ul>
|
</ul>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
<Show when={commentsLoading()}>
|
|
||||||
<InlineLoader />
|
|
||||||
</Show>
|
|
||||||
<LoadMoreWrapper
|
|
||||||
loadFunction={loadMoreComments}
|
|
||||||
pageSize={COMMENTS_PER_PAGE}
|
|
||||||
hidden={
|
|
||||||
props.shout?.stat?.commented === 0 ||
|
|
||||||
commentsLoading() ||
|
|
||||||
comments().length >= (props.shout?.stat?.commented || 0)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<ul class={styles.comments}>
|
<ul class={styles.comments}>
|
||||||
<For each={sortedComments().filter((r) => !r.reply_to)}>
|
<For each={sortedComments().filter((r) => !r.reply_to)}>
|
||||||
{(reaction) => (
|
{(reaction) => (
|
||||||
<Comment
|
<Comment
|
||||||
sortedComments={sortedComments()}
|
sortedComments={sortedComments()}
|
||||||
isArticleAuthor={props.shout.authors?.some((a) => a && reaction.created_by.id === a.id)}
|
isArticleAuthor={Boolean(props.articleAuthors.some((a) => a?.id === reaction.created_by.id))}
|
||||||
comment={reaction}
|
comment={reaction}
|
||||||
clickedReply={(id) => setClickedReplyId(id)}
|
clickedReply={(id) => setClickedReplyId(id)}
|
||||||
clickedReplyId={clickedReplyId()}
|
clickedReplyId={clickedReplyId()}
|
||||||
|
@ -162,7 +141,6 @@ export const CommentsTree = (props: Props) => {
|
||||||
)}
|
)}
|
||||||
</For>
|
</For>
|
||||||
</ul>
|
</ul>
|
||||||
</LoadMoreWrapper>
|
|
||||||
<ShowIfAuthenticated
|
<ShowIfAuthenticated
|
||||||
fallback={
|
fallback={
|
||||||
<div class={styles.signInMessage}>
|
<div class={styles.signInMessage}>
|
||||||
|
|
|
@ -6,9 +6,10 @@ import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMou
|
||||||
import { isServer } from 'solid-js/web'
|
import { isServer } from 'solid-js/web'
|
||||||
import { useFeed } from '~/context/feed'
|
import { useFeed } from '~/context/feed'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
|
import { useReactions } from '~/context/reactions'
|
||||||
import { useSession } from '~/context/session'
|
import { useSession } from '~/context/session'
|
||||||
import { DEFAULT_HEADER_OFFSET, useUI } from '~/context/ui'
|
import { DEFAULT_HEADER_OFFSET, useUI } from '~/context/ui'
|
||||||
import { type Author, type Maybe, type Shout, type Topic } from '~/graphql/schema/core.gen'
|
import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||||
import { processPrepositions } from '~/intl/prepositions'
|
import { processPrepositions } from '~/intl/prepositions'
|
||||||
import { isCyrillic } from '~/intl/translate'
|
import { isCyrillic } from '~/intl/translate'
|
||||||
import { getImageUrl } from '~/lib/getThumbUrl'
|
import { getImageUrl } from '~/lib/getThumbUrl'
|
||||||
|
@ -32,8 +33,8 @@ import styles from './Article.module.scss'
|
||||||
import { AudioHeader } from './AudioHeader'
|
import { AudioHeader } from './AudioHeader'
|
||||||
import { AudioPlayer } from './AudioPlayer'
|
import { AudioPlayer } from './AudioPlayer'
|
||||||
import { CommentsTree } from './CommentsTree'
|
import { CommentsTree } from './CommentsTree'
|
||||||
import { RatingControl as ShoutRatingControl } from './RatingControl'
|
|
||||||
import { SharePopup, getShareUrl } from './SharePopup'
|
import { SharePopup, getShareUrl } from './SharePopup'
|
||||||
|
import { ShoutRatingControl } from './ShoutRatingControl'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
article: Shout
|
article: Shout
|
||||||
|
@ -62,11 +63,15 @@ const scrollTo = (el: HTMLElement) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const imgSrcRegExp = /<img[^>]+src\s*=\s*["']([^"']+)["']/gi
|
const imgSrcRegExp = /<img[^>]+src\s*=\s*["']([^"']+)["']/gi
|
||||||
|
const COMMENTS_PER_PAGE = 30
|
||||||
|
const VOTES_PER_PAGE = 50
|
||||||
|
|
||||||
export const FullArticle = (props: Props) => {
|
export const FullArticle = (props: Props) => {
|
||||||
const [searchParams, changeSearchParams] = useSearchParams<ArticlePageSearchParams>()
|
const [searchParams, changeSearchParams] = useSearchParams<ArticlePageSearchParams>()
|
||||||
const { showModal } = useUI()
|
const { showModal } = useUI()
|
||||||
|
const { loadReactionsBy } = useReactions()
|
||||||
const [selectedImage, setSelectedImage] = createSignal('')
|
const [selectedImage, setSelectedImage] = createSignal('')
|
||||||
|
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
||||||
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
||||||
const { t, formatDate, lang } = useLocalize()
|
const { t, formatDate, lang } = useLocalize()
|
||||||
const { session, requireAuthentication } = useSession()
|
const { session, requireAuthentication } = useSession()
|
||||||
|
@ -74,6 +79,27 @@ export const FullArticle = (props: Props) => {
|
||||||
const { addSeen } = useFeed()
|
const { addSeen } = useFeed()
|
||||||
const formattedDate = createMemo(() => formatDate(new Date((props.article.published_at || 0) * 1000)))
|
const formattedDate = createMemo(() => formatDate(new Date((props.article.published_at || 0) * 1000)))
|
||||||
|
|
||||||
|
const [pages, setPages] = createSignal<Record<string, number>>({})
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
pages,
|
||||||
|
async (p: Record<string, number>) => {
|
||||||
|
await loadReactionsBy({
|
||||||
|
by: { shout: props.article.slug, comment: true },
|
||||||
|
limit: COMMENTS_PER_PAGE,
|
||||||
|
offset: COMMENTS_PER_PAGE * p.comments || 0
|
||||||
|
})
|
||||||
|
await loadReactionsBy({
|
||||||
|
by: { shout: props.article.slug, rating: true },
|
||||||
|
limit: VOTES_PER_PAGE,
|
||||||
|
offset: VOTES_PER_PAGE * p.rating || 0
|
||||||
|
})
|
||||||
|
setIsReactionsLoaded(true)
|
||||||
|
},
|
||||||
|
{ defer: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const canEdit = createMemo(
|
const canEdit = createMemo(
|
||||||
() =>
|
() =>
|
||||||
Boolean(author()?.id) &&
|
Boolean(author()?.id) &&
|
||||||
|
@ -141,7 +167,7 @@ export const FullArticle = (props: Props) => {
|
||||||
let commentsRef: HTMLDivElement | undefined
|
let commentsRef: HTMLDivElement | undefined
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (searchParams?.commentId) {
|
if (searchParams?.commentId && isReactionsLoaded()) {
|
||||||
const commentElement = document.querySelector<HTMLElement>(
|
const commentElement = document.querySelector<HTMLElement>(
|
||||||
`[id='comment_${searchParams?.commentId}']`
|
`[id='comment_${searchParams?.commentId}']`
|
||||||
)
|
)
|
||||||
|
@ -280,16 +306,9 @@ export const FullArticle = (props: Props) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(
|
onMount(() => {
|
||||||
on(
|
console.debug(props.article)
|
||||||
() => props.article,
|
setPages((_) => ({ comments: 0, rating: 0 }))
|
||||||
() => {
|
|
||||||
updateIframeSizes()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
addSeen(props.article.slug)
|
addSeen(props.article.slug)
|
||||||
document.title = props.article.title
|
document.title = props.article.title
|
||||||
updateIframeSizes()
|
updateIframeSizes()
|
||||||
|
@ -560,7 +579,13 @@ export const FullArticle = (props: Props) => {
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<div id="comments" ref={(el) => (commentsRef = el)}>
|
<div id="comments" ref={(el) => (commentsRef = el)}>
|
||||||
<CommentsTree shout={props.article} />
|
<Show when={isReactionsLoaded()}>
|
||||||
|
<CommentsTree
|
||||||
|
shoutId={props.article.id}
|
||||||
|
shoutSlug={props.article.slug}
|
||||||
|
articleAuthors={props.article.authors as Author[]}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,254 +0,0 @@
|
||||||
import { useSearchParams } from '@solidjs/router'
|
|
||||||
import { clsx } from 'clsx'
|
|
||||||
import { Show, createEffect, createMemo, createSignal, on } from 'solid-js'
|
|
||||||
import { byCreated } from '~/lib/sort'
|
|
||||||
import { useLocalize } from '../../context/localize'
|
|
||||||
import { RATINGS_PER_PAGE, useReactions } from '../../context/reactions'
|
|
||||||
import { useSession } from '../../context/session'
|
|
||||||
import { useSnackbar } from '../../context/ui'
|
|
||||||
import { Reaction, ReactionKind, Shout } from '../../graphql/schema/core.gen'
|
|
||||||
import { Icon } from '../_shared/Icon'
|
|
||||||
import { InlineLoader } from '../_shared/InlineLoader'
|
|
||||||
import { LoadMoreItems, LoadMoreWrapper } from '../_shared/LoadMoreWrapper'
|
|
||||||
import { Popup } from '../_shared/Popup'
|
|
||||||
import { VotersList } from '../_shared/VotersList'
|
|
||||||
import stylesComment from './CommentRatingControl.module.scss'
|
|
||||||
import stylesShout from './ShoutRatingControl.module.scss'
|
|
||||||
|
|
||||||
interface RatingControlProps {
|
|
||||||
shout?: Shout
|
|
||||||
comment?: Reaction
|
|
||||||
class?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RatingControl = (props: RatingControlProps) => {
|
|
||||||
const { t, lang } = useLocalize()
|
|
||||||
const [_, changeSearchParams] = useSearchParams()
|
|
||||||
const snackbar = useSnackbar()
|
|
||||||
const { session } = useSession()
|
|
||||||
const {
|
|
||||||
reactionEntities,
|
|
||||||
reactionsByShout,
|
|
||||||
createReaction,
|
|
||||||
deleteReaction,
|
|
||||||
loadShoutRatings,
|
|
||||||
loadCommentRatings
|
|
||||||
} = useReactions()
|
|
||||||
const [myRate, setMyRate] = createSignal<Reaction | undefined>()
|
|
||||||
const [ratingReactions, setRatingReactions] = createSignal<Reaction[]>([])
|
|
||||||
const [isLoading, setIsLoading] = createSignal(false)
|
|
||||||
|
|
||||||
// reaction kind
|
|
||||||
const checkReaction = (reactionKind: ReactionKind) =>
|
|
||||||
Object.values(reactionEntities).some(
|
|
||||||
(r) =>
|
|
||||||
r.kind === reactionKind &&
|
|
||||||
r.created_by.slug === session()?.user?.app_data?.profile?.slug &&
|
|
||||||
r.shout.id === props.comment?.shout.id &&
|
|
||||||
r.reply_to === props.comment?.id
|
|
||||||
)
|
|
||||||
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
|
||||||
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
const shout = props.comment?.shout.id || props.shout?.id
|
|
||||||
if (shout && !ratingReactions()) {
|
|
||||||
let result = Object.values(reactionEntities).filter(
|
|
||||||
(r) => [ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) && r.shout.id === shout
|
|
||||||
)
|
|
||||||
if (props.comment?.id) result = result.filter((r) => r.reply_to === props.comment?.id)
|
|
||||||
setRatingReactions(result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const deleteRating = async (reactionKind: ReactionKind) => {
|
|
||||||
const reactionToDelete = Object.values(reactionEntities).find(
|
|
||||||
(r) =>
|
|
||||||
r.kind === reactionKind &&
|
|
||||||
r.created_by.slug === session()?.user?.nickname &&
|
|
||||||
r.shout.id === props.comment?.shout.id &&
|
|
||||||
r.reply_to === props.comment?.id
|
|
||||||
)
|
|
||||||
return reactionToDelete && deleteReaction(reactionToDelete.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// rating change
|
|
||||||
const handleRatingChange = async (isUpvote: boolean) => {
|
|
||||||
setIsLoading(true)
|
|
||||||
let error = ''
|
|
||||||
try {
|
|
||||||
if (isUpvoted() && isUpvote) return
|
|
||||||
if (isDownvoted() && !isUpvote) return
|
|
||||||
if (isUpvoted() && !isUpvote) error = (await deleteRating(ReactionKind.Like))?.error || ''
|
|
||||||
if (isDownvoted() && isUpvote) error = (await deleteRating(ReactionKind.Dislike))?.error || ''
|
|
||||||
if (!(isUpvoted() || isDownvoted())) {
|
|
||||||
props.comment?.shout.id &&
|
|
||||||
(await createReaction({
|
|
||||||
reaction: {
|
|
||||||
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
|
||||||
shout: props.comment.shout.id,
|
|
||||||
reply_to: props.comment?.id
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
snackbar?.showSnackbar({ type: 'error', body: `${t('Error')}: ${error || err || ''}` })
|
|
||||||
}
|
|
||||||
setIsLoading(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const total = createMemo<number>(() =>
|
|
||||||
props.comment?.stat?.rating ? props.comment.stat.rating : props.shout?.stat?.rating || 0
|
|
||||||
)
|
|
||||||
|
|
||||||
createEffect(
|
|
||||||
on(
|
|
||||||
[ratingReactions, () => session()?.user?.app_data?.profile],
|
|
||||||
([reactions, me]) => {
|
|
||||||
console.debug('[RatingControl] on reactions update')
|
|
||||||
const ratingVotes = Object.values(reactions).filter((r) => !r.reply_to)
|
|
||||||
setRatingReactions((_) => ratingVotes.sort(byCreated))
|
|
||||||
const myReaction = reactions.find((r) => r.created_by.id === me?.id)
|
|
||||||
setMyRate((_) => myReaction)
|
|
||||||
},
|
|
||||||
{ defer: true }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const getTrigger = createMemo(() => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class={clsx(stylesComment.commentRatingValue, {
|
|
||||||
[stylesComment.commentRatingPositive]: total() > 0 && Boolean(props.comment?.id),
|
|
||||||
[stylesComment.commentRatingNegative]: total() < 0 && Boolean(props.comment?.id),
|
|
||||||
[stylesShout.ratingValue]: !props.comment?.id
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{total()}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
const VOTERS_PER_PAGE = 10
|
|
||||||
const [ratingPage, setRatingPage] = createSignal(0)
|
|
||||||
const [ratingLoading, setRatingLoading] = createSignal(false) // FIXME: use loading indication
|
|
||||||
const ratings = createMemo(() =>
|
|
||||||
props.shout
|
|
||||||
? reactionsByShout[props.shout?.slug]?.filter(
|
|
||||||
(r) => r.kind === ReactionKind.Like || r.kind === ReactionKind.Dislike
|
|
||||||
)
|
|
||||||
: []
|
|
||||||
)
|
|
||||||
const loadMoreReactions = async () => {
|
|
||||||
if (!(props.shout?.id || props.comment?.id)) return [] as LoadMoreItems
|
|
||||||
setRatingLoading(true)
|
|
||||||
const next = ratingPage() + 1
|
|
||||||
const offset = RATINGS_PER_PAGE * next
|
|
||||||
const loader = props.comment ? loadCommentRatings : loadShoutRatings
|
|
||||||
const rrr = await loader(props.shout?.id || 0, RATINGS_PER_PAGE, offset)
|
|
||||||
rrr && setRatingPage(next)
|
|
||||||
setRatingLoading(false)
|
|
||||||
return rrr as LoadMoreItems
|
|
||||||
}
|
|
||||||
return props.comment?.id ? (
|
|
||||||
<div class={stylesComment.commentRating}>
|
|
||||||
<button
|
|
||||||
role="button"
|
|
||||||
disabled={!session()?.user?.app_data?.profile}
|
|
||||||
onClick={() => handleRatingChange(true)}
|
|
||||||
class={clsx(stylesComment.commentRatingControl, stylesComment.commentRatingControlUp, {
|
|
||||||
[stylesComment.voted]: isUpvoted()
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<Popup
|
|
||||||
trigger={
|
|
||||||
<div
|
|
||||||
class={clsx(stylesComment.commentRatingValue, {
|
|
||||||
[stylesComment.commentRatingPositive]: (props.comment?.stat?.rating || 0) > 0,
|
|
||||||
[stylesComment.commentRatingNegative]: (props.comment?.stat?.rating || 0) < 0
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{props.comment?.stat?.rating || 0}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
variant="tiny"
|
|
||||||
>
|
|
||||||
<Show when={ratingLoading()}>
|
|
||||||
<InlineLoader />
|
|
||||||
</Show>
|
|
||||||
<LoadMoreWrapper
|
|
||||||
loadFunction={loadMoreReactions}
|
|
||||||
pageSize={VOTERS_PER_PAGE}
|
|
||||||
hidden={ratingLoading()}
|
|
||||||
>
|
|
||||||
<VotersList reactions={ratings()} fallbackMessage={t('This comment has not been rated yet')} />
|
|
||||||
</LoadMoreWrapper>
|
|
||||||
</Popup>
|
|
||||||
<button
|
|
||||||
role="button"
|
|
||||||
disabled={!session()?.user?.app_data?.profile}
|
|
||||||
onClick={() => handleRatingChange(false)}
|
|
||||||
class={clsx(stylesComment.commentRatingControl, stylesComment.commentRatingControlDown, {
|
|
||||||
[stylesComment.voted]: isDownvoted()
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div class={clsx(props.comment ? stylesComment.commentRating : stylesShout.rating, props.class)}>
|
|
||||||
<button
|
|
||||||
onClick={() => handleRatingChange(false)}
|
|
||||||
disabled={isLoading()}
|
|
||||||
class={
|
|
||||||
props.comment
|
|
||||||
? clsx(stylesComment.commentRatingControl, stylesComment.commentRatingControlUp, {
|
|
||||||
[stylesComment.voted]: myRate()?.kind === 'LIKE'
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Show when={!props.comment}>
|
|
||||||
<Icon
|
|
||||||
name={isDownvoted() ? 'rating-control-checked' : 'rating-control-less'}
|
|
||||||
class={isLoading() ? 'rotating' : ''}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
<Popup trigger={getTrigger()} variant="tiny">
|
|
||||||
<Show
|
|
||||||
when={!!session()?.user?.app_data?.profile}
|
|
||||||
fallback={
|
|
||||||
<>
|
|
||||||
<span class="link" onClick={() => changeSearchParams({ mode: 'login', m: 'auth' })}>
|
|
||||||
{t('Enter')}
|
|
||||||
</span>
|
|
||||||
{lang() === 'ru' ? ', ' : ' '}
|
|
||||||
{t('to see the voters')}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<VotersList
|
|
||||||
reactions={ratingReactions()}
|
|
||||||
fallbackMessage={isLoading() ? t('Loading') : t('No one rated yet')}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</Popup>
|
|
||||||
<button
|
|
||||||
onClick={() => handleRatingChange(true)}
|
|
||||||
disabled={isLoading()}
|
|
||||||
class={
|
|
||||||
props.comment
|
|
||||||
? clsx(stylesComment.commentRatingControl, stylesComment.commentRatingControlDown, {
|
|
||||||
[stylesComment.voted]: myRate()?.kind === 'DISLIKE'
|
|
||||||
})
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Show when={!props.comment}>
|
|
||||||
<Icon
|
|
||||||
name={isUpvoted() ? 'rating-control-checked' : 'rating-control-more'}
|
|
||||||
class={isLoading() ? 'rotating' : ''}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
107
src/components/Article/ShoutRatingControl.tsx
Normal file
107
src/components/Article/ShoutRatingControl.tsx
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import { Show, createMemo, createSignal } from 'solid-js'
|
||||||
|
import { useFeed } from '~/context/feed'
|
||||||
|
import { useLocalize } from '~/context/localize'
|
||||||
|
import { useReactions } from '~/context/reactions'
|
||||||
|
import { useSession } from '~/context/session'
|
||||||
|
import type { Author } from '~/graphql/schema/core.gen'
|
||||||
|
import { ReactionKind, Shout } from '~/graphql/schema/core.gen'
|
||||||
|
import { Icon } from '../_shared/Icon'
|
||||||
|
import { Popup } from '../_shared/Popup'
|
||||||
|
import { VotersList } from '../_shared/VotersList'
|
||||||
|
|
||||||
|
import styles from './ShoutRatingControl.module.scss'
|
||||||
|
|
||||||
|
interface ShoutRatingControlProps {
|
||||||
|
shout: Shout
|
||||||
|
class?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
||||||
|
const { t } = useLocalize()
|
||||||
|
const { loadShout } = useFeed()
|
||||||
|
const { requireAuthentication, session } = useSession()
|
||||||
|
const author = createMemo<Author>(() => session()?.user?.app_data?.profile as Author)
|
||||||
|
const { reactionEntities, createReaction, deleteReaction, loadReactionsBy } = useReactions()
|
||||||
|
const [isLoading, setIsLoading] = createSignal(false)
|
||||||
|
|
||||||
|
const checkReaction = (reactionKind: ReactionKind) =>
|
||||||
|
Object.values(reactionEntities).some(
|
||||||
|
(r) =>
|
||||||
|
r.kind === reactionKind &&
|
||||||
|
r.created_by.id === author()?.id &&
|
||||||
|
r.shout.id === props.shout.id &&
|
||||||
|
!r.reply_to
|
||||||
|
)
|
||||||
|
|
||||||
|
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||||
|
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
|
||||||
|
|
||||||
|
const shoutRatingReactions = createMemo(() =>
|
||||||
|
Object.values(reactionEntities).filter(
|
||||||
|
(r) => ['LIKE', 'DISLIKE'].includes(r.kind) && r.shout.id === props.shout.id && !r.reply_to
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const deleteShoutReaction = async (reactionKind: ReactionKind) => {
|
||||||
|
const reactionToDelete = Object.values(reactionEntities).find(
|
||||||
|
(r) =>
|
||||||
|
r.kind === reactionKind &&
|
||||||
|
r.created_by.id === author()?.id &&
|
||||||
|
r.shout.id === props.shout.id &&
|
||||||
|
!r.reply_to
|
||||||
|
)
|
||||||
|
if (reactionToDelete) return deleteReaction(reactionToDelete.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRatingChange = (isUpvote: boolean) => {
|
||||||
|
requireAuthentication(async () => {
|
||||||
|
setIsLoading(true)
|
||||||
|
if (isUpvoted()) {
|
||||||
|
await deleteShoutReaction(ReactionKind.Like)
|
||||||
|
} else if (isDownvoted()) {
|
||||||
|
await deleteShoutReaction(ReactionKind.Dislike)
|
||||||
|
} else {
|
||||||
|
await createReaction({
|
||||||
|
reaction: {
|
||||||
|
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
||||||
|
shout: props.shout.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
loadShout(props.shout.slug)
|
||||||
|
loadReactionsBy({
|
||||||
|
by: { shout: props.shout.slug }
|
||||||
|
})
|
||||||
|
|
||||||
|
setIsLoading(false)
|
||||||
|
}, 'vote')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.rating, props.class)}>
|
||||||
|
<button onClick={() => handleRatingChange(false)} disabled={isLoading()}>
|
||||||
|
<Show when={!isDownvoted()} fallback={<Icon name="rating-control-checked" />}>
|
||||||
|
<Icon name="rating-control-less" />
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
trigger={<span class={styles.ratingValue}>{props.shout.stat?.rating || 0}</span>}
|
||||||
|
variant="tiny"
|
||||||
|
>
|
||||||
|
<VotersList
|
||||||
|
reactions={shoutRatingReactions()}
|
||||||
|
fallbackMessage={t('This post has not been rated yet')}
|
||||||
|
/>
|
||||||
|
</Popup>
|
||||||
|
|
||||||
|
<button onClick={() => handleRatingChange(true)} disabled={isLoading()}>
|
||||||
|
<Show when={!isUpvoted()} fallback={<Icon name="rating-control-checked" />}>
|
||||||
|
<Icon name="rating-control-more" />
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ type Props = {
|
||||||
inviteView?: boolean
|
inviteView?: boolean
|
||||||
onInvite?: (id: number) => void
|
onInvite?: (id: number) => void
|
||||||
selected?: boolean
|
selected?: boolean
|
||||||
|
subscriptionsMode?: boolean
|
||||||
}
|
}
|
||||||
export const AuthorBadge = (props: Props) => {
|
export const AuthorBadge = (props: Props) => {
|
||||||
const { session, requireAuthentication } = useSession()
|
const { session, requireAuthentication } = useSession()
|
||||||
|
@ -116,7 +117,7 @@ export const AuthorBadge = (props: Props) => {
|
||||||
<div class={clsx('text-truncate', styles.bio)} innerHTML={props.author.bio || ''} />
|
<div class={clsx('text-truncate', styles.bio)} innerHTML={props.author.bio || ''} />
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
<Show when={props.author?.stat}>
|
<Show when={props.author?.stat && !props.subscriptionsMode}>
|
||||||
<div class={styles.bio}>
|
<div class={styles.bio}>
|
||||||
<Show when={(props.author?.stat?.shouts || 0) > 0}>
|
<Show when={(props.author?.stat?.shouts || 0) > 0}>
|
||||||
<div>{t('some posts', { count: props.author.stat?.shouts ?? 0 })}</div>
|
<div>{t('some posts', { count: props.author.stat?.shouts ?? 0 })}</div>
|
||||||
|
|
|
@ -162,7 +162,7 @@ export const AuthorCard = (props: Props) => {
|
||||||
<For each={authorSubs()}>
|
<For each={authorSubs()}>
|
||||||
{(subscription) =>
|
{(subscription) =>
|
||||||
'name' in subscription ? (
|
'name' in subscription ? (
|
||||||
<AuthorBadge author={subscription as Author} nameOnly={true} />
|
<AuthorBadge author={subscription as Author} subscriptionsMode={true} />
|
||||||
) : (
|
) : (
|
||||||
<TopicBadge topic={subscription as Topic} subscriptionsMode={true} />
|
<TopicBadge topic={subscription as Topic} subscriptionsMode={true} />
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Editor } from '@tiptap/core'
|
|
||||||
import { Blockquote } from '@tiptap/extension-blockquote'
|
import { Blockquote } from '@tiptap/extension-blockquote'
|
||||||
import { Bold } from '@tiptap/extension-bold'
|
import { Bold } from '@tiptap/extension-bold'
|
||||||
import { BubbleMenu } from '@tiptap/extension-bubble-menu'
|
import { BubbleMenu } from '@tiptap/extension-bubble-menu'
|
||||||
|
@ -11,7 +10,7 @@ import { Paragraph } from '@tiptap/extension-paragraph'
|
||||||
import { Placeholder } from '@tiptap/extension-placeholder'
|
import { Placeholder } from '@tiptap/extension-placeholder'
|
||||||
import { Text } from '@tiptap/extension-text'
|
import { Text } from '@tiptap/extension-text'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { Show, Suspense, createEffect, createMemo, createSignal, onCleanup, onMount } from 'solid-js'
|
import { Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js'
|
||||||
import { Portal } from 'solid-js/web'
|
import { Portal } from 'solid-js/web'
|
||||||
import {
|
import {
|
||||||
createEditorTransaction,
|
createEditorTransaction,
|
||||||
|
@ -20,23 +19,26 @@ import {
|
||||||
useEditorIsEmpty,
|
useEditorIsEmpty,
|
||||||
useEditorIsFocused
|
useEditorIsFocused
|
||||||
} from 'solid-tiptap'
|
} from 'solid-tiptap'
|
||||||
import { Modal } from '~/components/_shared/Modal'
|
|
||||||
import { useUI } from '~/context/ui'
|
import { useEditorContext } from '~/context/editor'
|
||||||
|
import { useLocalize } from '~/context/localize'
|
||||||
import { UploadedFile } from '~/types/upload'
|
import { UploadedFile } from '~/types/upload'
|
||||||
import { useEditorContext } from '../../context/editor'
|
|
||||||
import { useLocalize } from '../../context/localize'
|
|
||||||
import { Button } from '../_shared/Button'
|
import { Button } from '../_shared/Button'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { Loading } from '../_shared/Loading'
|
import { Loading } from '../_shared/Loading'
|
||||||
|
import { Modal } from '../_shared/Modal'
|
||||||
import { Popover } from '../_shared/Popover'
|
import { Popover } from '../_shared/Popover'
|
||||||
import { ShowOnlyOnClient } from '../_shared/ShowOnlyOnClient'
|
import { ShowOnlyOnClient } from '../_shared/ShowOnlyOnClient'
|
||||||
import { LinkBubbleMenuModule } from './LinkBubbleMenu'
|
import { LinkBubbleMenuModule } from './LinkBubbleMenu'
|
||||||
import styles from './SimplifiedEditor.module.scss'
|
|
||||||
import { TextBubbleMenu } from './TextBubbleMenu'
|
import { TextBubbleMenu } from './TextBubbleMenu'
|
||||||
import { UploadModalContent } from './UploadModalContent'
|
import { UploadModalContent } from './UploadModalContent'
|
||||||
import { Figcaption } from './extensions/Figcaption'
|
import { Figcaption } from './extensions/Figcaption'
|
||||||
import { Figure } from './extensions/Figure'
|
import { Figure } from './extensions/Figure'
|
||||||
|
|
||||||
|
import { Editor } from '@tiptap/core'
|
||||||
|
import { useUI } from '~/context/ui'
|
||||||
|
import styles from './SimplifiedEditor.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
placeholder: string
|
placeholder: string
|
||||||
initialContent?: string
|
initialContent?: string
|
||||||
|
@ -65,25 +67,31 @@ type Props = {
|
||||||
const DEFAULT_MAX_LENGTH = 400
|
const DEFAULT_MAX_LENGTH = 400
|
||||||
|
|
||||||
const SimplifiedEditor = (props: Props) => {
|
const SimplifiedEditor = (props: Props) => {
|
||||||
const maxLength = props.maxLength ?? DEFAULT_MAX_LENGTH
|
|
||||||
let wrapperEditorElRef: HTMLElement | undefined
|
|
||||||
let editorElRef: HTMLElement | undefined
|
|
||||||
let textBubbleMenuRef: HTMLDivElement | undefined
|
|
||||||
let linkBubbleMenuRef: HTMLDivElement | undefined
|
|
||||||
const { showModal, hideModal } = useUI()
|
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
const { showModal, hideModal } = useUI()
|
||||||
const [counter, setCounter] = createSignal<number>(0)
|
const [counter, setCounter] = createSignal<number>(0)
|
||||||
const [shouldShowLinkBubbleMenu, setShouldShowLinkBubbleMenu] = createSignal(false)
|
const [shouldShowLinkBubbleMenu, setShouldShowLinkBubbleMenu] = createSignal(false)
|
||||||
const isCancelButtonVisible = createMemo(() => props.isCancelButtonVisible !== false)
|
const isCancelButtonVisible = createMemo(() => props.isCancelButtonVisible !== false)
|
||||||
const { setEditor, editor } = useEditorContext()
|
const [editorElement, setEditorElement] = createSignal<HTMLDivElement>()
|
||||||
|
const { editor, setEditor } = useEditorContext()
|
||||||
|
|
||||||
|
const maxLength = props.maxLength ?? DEFAULT_MAX_LENGTH
|
||||||
|
let wrapperEditorElRef: HTMLElement | undefined
|
||||||
|
let textBubbleMenuRef: HTMLDivElement | undefined
|
||||||
|
let linkBubbleMenuRef: HTMLDivElement | undefined
|
||||||
|
|
||||||
const ImageFigure = Figure.extend({
|
const ImageFigure = Figure.extend({
|
||||||
name: 'capturedImage',
|
name: 'capturedImage',
|
||||||
content: 'figcaption image'
|
content: 'figcaption image'
|
||||||
})
|
})
|
||||||
createEffect(() => {
|
|
||||||
const e = createTiptapEditor(() => ({
|
createEffect(
|
||||||
element: editorElRef as HTMLElement,
|
on(
|
||||||
|
() => editorElement(),
|
||||||
|
(ee: HTMLDivElement | undefined) => {
|
||||||
|
if (ee && textBubbleMenuRef && linkBubbleMenuRef) {
|
||||||
|
const freshEditor = createTiptapEditor<HTMLElement>(() => ({
|
||||||
|
element: ee,
|
||||||
editorProps: {
|
editorProps: {
|
||||||
attributes: {
|
attributes: {
|
||||||
class: styles.simplifiedEditorField
|
class: styles.simplifiedEditorField
|
||||||
|
@ -140,12 +148,17 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
autofocus: props.autoFocus,
|
autofocus: props.autoFocus,
|
||||||
content: content ?? null
|
content: props.initialContent || null
|
||||||
}))
|
}))
|
||||||
|
const editorInstance = freshEditor()
|
||||||
|
if (!editorInstance) return
|
||||||
|
setEditor(editorInstance)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ defer: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
e() && setEditor(e() as Editor)
|
|
||||||
})
|
|
||||||
const content = props.initialContent
|
|
||||||
const isEmpty = useEditorIsEmpty(() => editor())
|
const isEmpty = useEditorIsEmpty(() => editor())
|
||||||
const isFocused = useEditorIsFocused(() => editor())
|
const isFocused = useEditorIsFocused(() => editor())
|
||||||
|
|
||||||
|
@ -198,7 +211,7 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
}
|
}
|
||||||
if (props.resetToInitial) {
|
if (props.resetToInitial) {
|
||||||
editor()?.commands.clearContent(true)
|
editor()?.commands.clearContent(true)
|
||||||
props.initialContent && editor()?.commands.setContent(props.initialContent)
|
if (props.initialContent) editor()?.commands.setContent(props.initialContent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -261,7 +274,6 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShowOnlyOnClient>
|
<ShowOnlyOnClient>
|
||||||
<Suspense>
|
|
||||||
<div
|
<div
|
||||||
ref={(el) => (wrapperEditorElRef = el)}
|
ref={(el) => (wrapperEditorElRef = el)}
|
||||||
class={clsx(styles.SimplifiedEditor, {
|
class={clsx(styles.SimplifiedEditor, {
|
||||||
|
@ -278,11 +290,7 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
<Show when={props.label && counter() > 0}>
|
<Show when={props.label && counter() > 0}>
|
||||||
<div class={styles.label}>{props.label}</div>
|
<div class={styles.label}>{props.label}</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
<div style={props.maxHeight ? maxHeightStyle : undefined} ref={setEditorElement} />
|
||||||
<Show when={props.maxHeight} fallback={<div ref={(el) => (editorElRef = el)} />}>
|
|
||||||
<div style={maxHeightStyle} ref={(el) => (editorElRef = el)} />
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<Show when={!props.onlyBubbleControls}>
|
<Show when={!props.onlyBubbleControls}>
|
||||||
<div class={clsx(styles.controls, { [styles.alwaysVisible]: props.controlsAlwaysVisible })}>
|
<div class={clsx(styles.controls, { [styles.alwaysVisible]: props.controlsAlwaysVisible })}>
|
||||||
<div class={styles.actions}>
|
<div class={styles.actions}>
|
||||||
|
@ -371,11 +379,14 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
<Show when={props.imageEnabled}>
|
<Show when={props.imageEnabled}>
|
||||||
<Portal>
|
<Portal>
|
||||||
<Modal variant="narrow" name="simplifiedEditorUploadImage">
|
<Modal variant="narrow" name="simplifiedEditorUploadImage">
|
||||||
<UploadModalContent onClose={(value) => value && renderImage(value)} />
|
<UploadModalContent
|
||||||
|
onClose={(value) => {
|
||||||
|
renderImage(value as UploadedFile)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
</Portal>
|
</Portal>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!!editor()}>
|
|
||||||
<Show when={props.onlyBubbleControls}>
|
<Show when={props.onlyBubbleControls}>
|
||||||
<TextBubbleMenu
|
<TextBubbleMenu
|
||||||
shouldShow={true}
|
shouldShow={true}
|
||||||
|
@ -389,9 +400,7 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
ref={(el) => (linkBubbleMenuRef = el)}
|
ref={(el) => (linkBubbleMenuRef = el)}
|
||||||
onClose={handleHideLinkBubble}
|
onClose={handleHideLinkBubble}
|
||||||
/>
|
/>
|
||||||
</Show>
|
|
||||||
</div>
|
</div>
|
||||||
</Suspense>
|
|
||||||
</ShowOnlyOnClient>
|
</ShowOnlyOnClient>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen'
|
||||||
import { capitalize } from '~/utils/capitalize'
|
import { capitalize } from '~/utils/capitalize'
|
||||||
import { descFromBody } from '~/utils/meta'
|
import { descFromBody } from '~/utils/meta'
|
||||||
import { CoverImage } from '../../Article/CoverImage'
|
import { CoverImage } from '../../Article/CoverImage'
|
||||||
import { RatingControl as ShoutRatingControl } from '../../Article/RatingControl'
|
|
||||||
import { SharePopup, getShareUrl } from '../../Article/SharePopup'
|
import { SharePopup, getShareUrl } from '../../Article/SharePopup'
|
||||||
|
import { ShoutRatingControl } from '../../Article/ShoutRatingControl'
|
||||||
import { AuthorLink } from '../../Author/AuthorLink'
|
import { AuthorLink } from '../../Author/AuthorLink'
|
||||||
import stylesHeader from '../../HeaderNav/Header.module.scss'
|
import stylesHeader from '../../HeaderNav/Header.module.scss'
|
||||||
import { CardTopic } from '../CardTopic'
|
import { CardTopic } from '../CardTopic'
|
||||||
|
|
|
@ -753,12 +753,7 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rightItem {
|
|
||||||
margin-right: 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a:link,
|
a:link,
|
||||||
|
@ -801,13 +796,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.rightItemIcon {
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 0.3em;
|
|
||||||
position: relative;
|
|
||||||
top: 0.15em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editorPopup {
|
.editorPopup {
|
||||||
border: 1px solid rgb(0 0 0 / 15%) !important;
|
border: 1px solid rgb(0 0 0 / 15%) !important;
|
||||||
border-radius: 1.6rem;
|
border-radius: 1.6rem;
|
||||||
|
|
|
@ -18,6 +18,8 @@ import stylesAuthorList from './AuthorsList.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
authors: Author[]
|
authors: Author[]
|
||||||
|
authorsByFollowers?: Author[]
|
||||||
|
authorsByShouts?: Author[]
|
||||||
isLoaded: boolean
|
isLoaded: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,43 +35,48 @@ export const AllAuthors = (props: Props) => {
|
||||||
const [searchParams, changeSearchParams] = useSearchParams<{ by?: string }>()
|
const [searchParams, changeSearchParams] = useSearchParams<{ by?: string }>()
|
||||||
const { authorsSorted, setAuthorsSort, loadAuthors } = useAuthors()
|
const { authorsSorted, setAuthorsSort, loadAuthors } = useAuthors()
|
||||||
const [loading, setLoading] = createSignal<boolean>(false)
|
const [loading, setLoading] = createSignal<boolean>(false)
|
||||||
const [searchQuery, setSearchQuery] = createSignal('')
|
const [_currentAuthors, setCurrentAuthors] = createSignal<Author[]>([])
|
||||||
const [filteredAuthors, setFilteredAuthors] = createSignal<Author[]>([])
|
|
||||||
|
|
||||||
|
// UPDATE Fetch authors initially and when searchParams.by changes
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
// Load all authors initially
|
|
||||||
fetchAuthors(searchParams.by || 'name', 0)
|
fetchAuthors(searchParams.by || 'name', 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
/* const authors = createMemo(() => {
|
const authors = createMemo(() => {
|
||||||
let sortedAuthors = [...props.authors]
|
let sortedAuthors = [...(props.authors || authorsSorted())] // Clone the array to avoid mutating the original
|
||||||
sortedAuthors = authorsSorted()
|
console.log('Before Sorting:', sortedAuthors.slice(0, 5)) // Log the first 5 authors for comparison
|
||||||
if (!searchParams.by || searchParams.by === 'name') {
|
if (searchParams.by === 'name') {
|
||||||
sortedAuthors = sortedAuthors.sort(byFirstChar)
|
sortedAuthors = sortedAuthors.sort(byFirstChar)
|
||||||
|
console.log('Sorted by Name:', sortedAuthors.slice(0, 5))
|
||||||
} else if (searchParams.by === 'shouts') {
|
} else if (searchParams.by === 'shouts') {
|
||||||
sortedAuthors = sortedAuthors.sort(byStat('shouts'))
|
sortedAuthors = sortedAuthors.sort(byStat('shouts'))
|
||||||
|
console.log('Sorted by Shouts:', sortedAuthors.slice(0, 5))
|
||||||
} else if (searchParams.by === 'followers') {
|
} else if (searchParams.by === 'followers') {
|
||||||
sortedAuthors = sortedAuthors.sort(byStat('followers'))
|
sortedAuthors = sortedAuthors.sort(byStat('followers'))
|
||||||
|
console.log('Sorted by Followers:', sortedAuthors.slice(0, 5))
|
||||||
}
|
}
|
||||||
return sortedAuthors
|
console.log('After Sorting:', sortedAuthors.slice(0, 5))
|
||||||
}) */
|
|
||||||
|
|
||||||
const authors = createMemo(() => {
|
|
||||||
let sortedAuthors: Author[] = []
|
|
||||||
if (!searchParams.by || searchParams.by === 'name') {
|
|
||||||
sortedAuthors = [...props.authors].sort(byFirstChar)
|
|
||||||
} else {
|
|
||||||
sortedAuthors = authorsSorted().sort(byStat(searchParams.by || 'shouts'))
|
|
||||||
}
|
|
||||||
return sortedAuthors
|
return sortedAuthors
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Log authors data and searchParams for debugging
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
setFilteredAuthors(dummyFilter(authors(), searchQuery(), lang()) as Author[])
|
console.log('Authors:', props.authors.slice(0, 5)) // Log the first 5 authors
|
||||||
|
console.log('Sorted Authors:', authors().slice(0, 5)) // Log the first 5 sorted authors
|
||||||
|
console.log('Search Params "by":', searchParams.by)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// filter
|
||||||
|
const [searchQuery, setSearchQuery] = createSignal('')
|
||||||
|
const [filteredAuthors, setFilteredAuthors] = createSignal<Author[]>([])
|
||||||
|
createEffect(
|
||||||
|
() => authors() && setFilteredAuthors(dummyFilter(authors(), searchQuery(), lang()) as Author[])
|
||||||
|
)
|
||||||
|
|
||||||
|
// store by first char
|
||||||
const byLetterFiltered = createMemo<{ [letter: string]: Author[] }>(() => {
|
const byLetterFiltered = createMemo<{ [letter: string]: Author[] }>(() => {
|
||||||
if (!(filteredAuthors()?.length > 0)) return {}
|
if (!(filteredAuthors()?.length > 0)) return {}
|
||||||
|
console.debug('[components.AllAuthors] update byLetterFiltered', filteredAuthors()?.length)
|
||||||
return filteredAuthors().reduce(
|
return filteredAuthors().reduce(
|
||||||
(acc, author: Author) => authorLetterReduce(acc, author, lang()),
|
(acc, author: Author) => authorLetterReduce(acc, author, lang()),
|
||||||
{} as { [letter: string]: Author[] }
|
{} as { [letter: string]: Author[] }
|
||||||
|
@ -86,6 +93,7 @@ export const AllAuthors = (props: Props) => {
|
||||||
|
|
||||||
const fetchAuthors = async (queryType: string, page: number) => {
|
const fetchAuthors = async (queryType: string, page: number) => {
|
||||||
try {
|
try {
|
||||||
|
console.debug('[components.AuthorsList] fetching authors...')
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setAuthorsSort?.(queryType)
|
setAuthorsSort?.(queryType)
|
||||||
const offset = AUTHORS_PER_PAGE * page
|
const offset = AUTHORS_PER_PAGE * page
|
||||||
|
@ -94,6 +102,8 @@ export const AllAuthors = (props: Props) => {
|
||||||
limit: AUTHORS_PER_PAGE,
|
limit: AUTHORS_PER_PAGE,
|
||||||
offset
|
offset
|
||||||
})
|
})
|
||||||
|
// UPDATE authors to currentAuthors state
|
||||||
|
setCurrentAuthors((prev) => [...prev, ...authorsSorted()])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[components.AuthorsList] error fetching authors:', error)
|
console.error('[components.AuthorsList] error fetching authors:', error)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -121,7 +131,7 @@ export const AllAuthors = (props: Props) => {
|
||||||
<ul class={clsx(styles.viewSwitcher, 'view-switcher')}>
|
<ul class={clsx(styles.viewSwitcher, 'view-switcher')}>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
['view-switcher__item--selected']: searchParams?.by === 'shouts'
|
['view-switcher__item--selected']: !searchParams?.by || searchParams?.by === 'shouts'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<a href="#" onClick={() => changeSearchParams({ by: 'shouts' })}>
|
<a href="#" onClick={() => changeSearchParams({ by: 'shouts' })}>
|
||||||
|
@ -139,7 +149,7 @@ export const AllAuthors = (props: Props) => {
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class={clsx({
|
class={clsx({
|
||||||
['view-switcher__item--selected']: !searchParams?.by || searchParams?.by === 'name'
|
['view-switcher__item--selected']: searchParams?.by === 'name'
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<a href="#" onClick={() => changeSearchParams({ by: 'name' })}>
|
<a href="#" onClick={() => changeSearchParams({ by: 'name' })}>
|
||||||
|
@ -237,13 +247,12 @@ export const AllAuthors = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Show when={props.isLoaded} fallback={<Loading />}>
|
<Show when={props.isLoaded} fallback={<Loading />}>
|
||||||
<div class="offset-md-5">
|
<div class="offset-md-5">
|
||||||
<TabNavigator />
|
<TabNavigator />
|
||||||
<Show when={!searchParams.by || searchParams.by === 'name'} fallback={<AuthorsSortedList />}>
|
<Show when={searchParams?.by === 'name'} fallback={<AuthorsSortedList />}>
|
||||||
<AbcNavigator />
|
<AbcNavigator />
|
||||||
<AbcAuthorsList />
|
<AbcAuthorsList />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
|
@ -61,7 +61,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
on(
|
on(
|
||||||
[() => session()?.user?.app_data?.profile, () => props.authorSlug || ''],
|
[() => session()?.user?.app_data?.profile, () => props.authorSlug || ''],
|
||||||
async ([me, slug]) => {
|
async ([me, slug]) => {
|
||||||
console.debug('[AuthorView] checking if my profile')
|
console.debug('check if my profile')
|
||||||
const my = slug && me?.slug === slug
|
const my = slug && me?.slug === slug
|
||||||
if (my) {
|
if (my) {
|
||||||
console.debug('[Author] my profile precached')
|
console.debug('[Author] my profile precached')
|
||||||
|
@ -86,7 +86,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
() => authorsEntities()[props.author?.slug || props.authorSlug || ''],
|
() => authorsEntities()[props.author?.slug || props.authorSlug || ''],
|
||||||
async (found) => {
|
async (found) => {
|
||||||
if (!found) return
|
if (!found) return
|
||||||
console.debug('[AuthorView] ')
|
setAuthor(found)
|
||||||
console.info(`[Author] profile for @${found.slug} fetched`)
|
console.info(`[Author] profile for @${found.slug} fetched`)
|
||||||
const followsResp = await query(getAuthorFollowsQuery, { slug: found.slug }).toPromise()
|
const followsResp = await query(getAuthorFollowsQuery, { slug: found.slug }).toPromise()
|
||||||
const follows = followsResp?.data?.get_author_followers || {}
|
const follows = followsResp?.data?.get_author_followers || {}
|
||||||
|
@ -96,7 +96,6 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
setFollowers(followersResp?.data?.get_author_followers || [])
|
setFollowers(followersResp?.data?.get_author_followers || [])
|
||||||
console.info(`[Author] followers for @${found.slug} fetched`)
|
console.info(`[Author] followers for @${found.slug} fetched`)
|
||||||
setIsFetching(false)
|
setIsFetching(false)
|
||||||
setTimeout(() => setAuthor(found), 1)
|
|
||||||
},
|
},
|
||||||
{ defer: true }
|
{ defer: true }
|
||||||
)
|
)
|
||||||
|
@ -124,37 +123,7 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
(tab) => tab && console.log('[views.Author] profile tab switched')
|
(tab) => tab && console.log('[views.Author] profile tab switched')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
const AuthorFeed = () => (
|
|
||||||
<Show when={Array.isArray(props.shouts) && props.shouts.length > 0 && props.shouts[0]}>
|
|
||||||
<Row1 article={props.shouts?.[0] as Shout} noauthor={true} nodate={true} />
|
|
||||||
|
|
||||||
<Show when={props.shouts?.length || 0}>
|
|
||||||
<Show when={props.shouts?.length === 1}>
|
|
||||||
<Row1 article={props.shouts?.[0] as Shout} noauthor={true} nodate={true} />
|
|
||||||
</Show>
|
|
||||||
<Show when={props.shouts?.length === 2}>
|
|
||||||
<Row2 articles={props.shouts as Shout[]} isEqual={true} noauthor={true} nodate={true} />
|
|
||||||
</Show>
|
|
||||||
<Show when={props.shouts?.length === 3}>
|
|
||||||
<Row3 articles={props.shouts as Shout[]} noauthor={true} nodate={true} />
|
|
||||||
</Show>
|
|
||||||
<Show when={props.shouts && props.shouts.length > 3}>
|
|
||||||
<For each={pages()}>
|
|
||||||
{(page) => (
|
|
||||||
<>
|
|
||||||
<Row1 article={page[0]} noauthor={true} nodate={true} />
|
|
||||||
<Row2 articles={page.slice(1, 3)} isEqual={true} noauthor={true} />
|
|
||||||
<Row1 article={page[3]} noauthor={true} nodate={true} />
|
|
||||||
<Row2 articles={page.slice(4, 6)} isEqual={true} noauthor={true} />
|
|
||||||
<Row1 article={page[6]} noauthor={true} nodate={true} />
|
|
||||||
<Row2 articles={page.slice(7, 9)} isEqual={true} noauthor={true} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<div class={styles.authorPage}>
|
<div class={styles.authorPage}>
|
||||||
<div class="wide-container">
|
<div class="wide-container">
|
||||||
|
@ -260,7 +229,34 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<AuthorFeed />
|
<Show when={Array.isArray(props.shouts) && props.shouts.length > 0 && props.shouts[0]}>
|
||||||
|
<Row1 article={props.shouts?.[0] as Shout} noauthor={true} nodate={true} />
|
||||||
|
|
||||||
|
<Show when={props.shouts && props.shouts.length > 1}>
|
||||||
|
<Switch>
|
||||||
|
<Match when={props.shouts && props.shouts.length === 2}>
|
||||||
|
<Row2 articles={props.shouts as Shout[]} isEqual={true} noauthor={true} nodate={true} />
|
||||||
|
</Match>
|
||||||
|
<Match when={props.shouts && props.shouts.length === 3}>
|
||||||
|
<Row3 articles={props.shouts as Shout[]} noauthor={true} nodate={true} />
|
||||||
|
</Match>
|
||||||
|
<Match when={props.shouts && props.shouts.length > 3}>
|
||||||
|
<For each={pages()}>
|
||||||
|
{(page) => (
|
||||||
|
<>
|
||||||
|
<Row1 article={page[0]} noauthor={true} nodate={true} />
|
||||||
|
<Row2 articles={page.slice(1, 3)} isEqual={true} noauthor={true} />
|
||||||
|
<Row1 article={page[3]} noauthor={true} nodate={true} />
|
||||||
|
<Row2 articles={page.slice(4, 6)} isEqual={true} noauthor={true} />
|
||||||
|
<Row1 article={page[6]} noauthor={true} nodate={true} />
|
||||||
|
<Row2 articles={page.slice(7, 9)} isEqual={true} noauthor={true} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</Show>
|
||||||
|
</Show>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,27 +9,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.invert {
|
|
||||||
filter: invert(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rotating {
|
|
||||||
/* Define the keyframes for the animation */
|
|
||||||
@keyframes rotate {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apply the animation to the element */
|
|
||||||
animation: rotate .7s ease-out infinite; /* Rotate infinitely over 2 seconds using a linear timing function */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.notificationsCounter {
|
.notificationsCounter {
|
||||||
@include media-breakpoint-up(md) {
|
@include media-breakpoint-up(md) {
|
||||||
left: 1.8rem;
|
left: 1.8rem;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Link } from '@solidjs/meta'
|
|
||||||
import type { JSX } from 'solid-js'
|
import type { JSX } from 'solid-js'
|
||||||
|
|
||||||
|
import { Link } from '@solidjs/meta'
|
||||||
import { splitProps } from 'solid-js'
|
import { splitProps } from 'solid-js'
|
||||||
|
|
||||||
import { getImageUrl } from '~/lib/getThumbUrl'
|
import { getImageUrl } from '~/lib/getThumbUrl'
|
||||||
|
|
||||||
type Props = JSX.ImgHTMLAttributes<HTMLImageElement> & {
|
type Props = JSX.ImgHTMLAttributes<HTMLImageElement> & {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
|
||||||
import { byCreated } from '~/lib/sort'
|
import { byCreated } from '~/lib/sort'
|
||||||
import { SortFunction } from '~/types/common'
|
import { SortFunction } from '~/types/common'
|
||||||
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
||||||
import { Loading } from './Loading'
|
|
||||||
|
|
||||||
export type LoadMoreItems = Shout[] | Author[] | Reaction[]
|
export type LoadMoreItems = Shout[] | Author[] | Reaction[]
|
||||||
|
|
||||||
|
@ -53,12 +52,14 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.children}
|
{props.children}
|
||||||
<Show when={isLoading()}>
|
<Show when={isLoadMoreButtonVisible() && !props.hidden}>
|
||||||
<Loading />
|
|
||||||
</Show>
|
|
||||||
<Show when={isLoadMoreButtonVisible() && !props.hidden && !isLoading()}>
|
|
||||||
<div class="load-more-container">
|
<div class="load-more-container">
|
||||||
<Button onClick={loadItems} value={t('Load more')} title={`${items().length} ${t('loaded')}`} />
|
<Button
|
||||||
|
onClick={loadItems}
|
||||||
|
disabled={isLoading()}
|
||||||
|
value={t('Load more')}
|
||||||
|
title={`${items().length} ${t('loaded')}`}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -125,7 +125,7 @@ export const AuthorsProvider = (props: { children: JSX.Element }) => {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Определяем функцию сортировки по рейтингу
|
// Определяем функцию сортировки по рейтингу
|
||||||
const sortByRating: SortFunction<{ slug: string; rating: number }> = (a, b) => a.rating - b.rating
|
const sortByRating: SortFunction<{ slug: string; rating: number }> = (a, b) => b.rating - a.rating
|
||||||
|
|
||||||
// Фильтруем и сортируем авторов
|
// Фильтруем и сортируем авторов
|
||||||
const sortedTopAuthors = filterAndSort(authors, sortByRating)
|
const sortedTopAuthors = filterAndSort(authors, sortByRating)
|
||||||
|
|
|
@ -2,12 +2,7 @@ import type { JSX } from 'solid-js'
|
||||||
|
|
||||||
import { createContext, onCleanup, useContext } from 'solid-js'
|
import { createContext, onCleanup, useContext } from 'solid-js'
|
||||||
import { createStore, reconcile } from 'solid-js/store'
|
import { createStore, reconcile } from 'solid-js/store'
|
||||||
import {
|
import { loadReactions } from '~/graphql/api/public'
|
||||||
loadCommentRatings,
|
|
||||||
loadReactions,
|
|
||||||
loadShoutComments,
|
|
||||||
loadShoutRatings
|
|
||||||
} from '~/graphql/api/public'
|
|
||||||
import createReactionMutation from '~/graphql/mutation/core/reaction-create'
|
import createReactionMutation from '~/graphql/mutation/core/reaction-create'
|
||||||
import destroyReactionMutation from '~/graphql/mutation/core/reaction-destroy'
|
import destroyReactionMutation from '~/graphql/mutation/core/reaction-destroy'
|
||||||
import updateReactionMutation from '~/graphql/mutation/core/reaction-update'
|
import updateReactionMutation from '~/graphql/mutation/core/reaction-update'
|
||||||
|
@ -22,16 +17,10 @@ import { useGraphQL } from './graphql'
|
||||||
import { useLocalize } from './localize'
|
import { useLocalize } from './localize'
|
||||||
import { useSnackbar } from './ui'
|
import { useSnackbar } from './ui'
|
||||||
|
|
||||||
export const COMMENTS_PER_PAGE = 50
|
|
||||||
export const RATINGS_PER_PAGE = 100
|
|
||||||
|
|
||||||
type ReactionsContextType = {
|
type ReactionsContextType = {
|
||||||
reactionEntities: Record<number, Reaction>
|
reactionEntities: Record<number, Reaction>
|
||||||
reactionsByShout: Record<string, Reaction[]>
|
reactionsByShout: Record<string, Reaction[]>
|
||||||
loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise<Reaction[]>
|
loadReactionsBy: (args: QueryLoad_Reactions_ByArgs) => Promise<Reaction[]>
|
||||||
loadShoutComments: (shout: number, limit?: number, offset?: number) => Promise<Reaction[]>
|
|
||||||
loadShoutRatings: (shout: number, limit?: number, offset?: number) => Promise<Reaction[]>
|
|
||||||
loadCommentRatings: (comment: number, limit?: number, offset?: number) => Promise<Reaction[]>
|
|
||||||
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
|
createReaction: (reaction: MutationCreate_ReactionArgs) => Promise<void>
|
||||||
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
|
updateReaction: (reaction: MutationUpdate_ReactionArgs) => Promise<Reaction>
|
||||||
deleteReaction: (id: number) => Promise<{ error: string } | null>
|
deleteReaction: (id: number) => Promise<{ error: string } | null>
|
||||||
|
@ -74,42 +63,6 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadShoutRatingsAdding = async (
|
|
||||||
shout: number,
|
|
||||||
limit = RATINGS_PER_PAGE,
|
|
||||||
offset = 0
|
|
||||||
): Promise<Reaction[]> => {
|
|
||||||
const fetcher = await loadShoutRatings({ shout, limit, offset })
|
|
||||||
const result = (await fetcher()) || []
|
|
||||||
console.debug('[context.reactions] shout ratings loaded', result)
|
|
||||||
result && addReactions(result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadCommentRatingsAdding = async (
|
|
||||||
comment: number,
|
|
||||||
limit = RATINGS_PER_PAGE,
|
|
||||||
offset = 0
|
|
||||||
): Promise<Reaction[]> => {
|
|
||||||
const fetcher = await loadCommentRatings({ comment, limit, offset })
|
|
||||||
const result = (await fetcher()) || []
|
|
||||||
console.debug('[context.reactions] shout ratings loaded', result)
|
|
||||||
result && addReactions(result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadShoutCommentsAdding = async (
|
|
||||||
shout: number,
|
|
||||||
limit = COMMENTS_PER_PAGE,
|
|
||||||
offset = 0
|
|
||||||
): Promise<Reaction[]> => {
|
|
||||||
const fetcher = await loadShoutComments({ shout, limit, offset })
|
|
||||||
const result = (await fetcher()) || []
|
|
||||||
console.debug('[context.reactions] shout comments loaded', result)
|
|
||||||
result && addReactions(result)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
const createReaction = async (input: MutationCreate_ReactionArgs): Promise<void> => {
|
const createReaction = async (input: MutationCreate_ReactionArgs): Promise<void> => {
|
||||||
const resp = await mutation(createReactionMutation, input).toPromise()
|
const resp = await mutation(createReactionMutation, input).toPromise()
|
||||||
const { error, reaction } = resp?.data?.create_reaction || {}
|
const { error, reaction } = resp?.data?.create_reaction || {}
|
||||||
|
@ -169,9 +122,6 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
loadReactionsBy,
|
loadReactionsBy,
|
||||||
loadShoutComments: loadShoutCommentsAdding,
|
|
||||||
loadShoutRatings: loadShoutRatingsAdding,
|
|
||||||
loadCommentRatings: loadCommentRatingsAdding,
|
|
||||||
createReaction,
|
createReaction,
|
||||||
updateReaction,
|
updateReaction,
|
||||||
deleteReaction,
|
deleteReaction,
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import { cache } from '@solidjs/router'
|
import { cache } from '@solidjs/router'
|
||||||
import { defaultClient } from '~/context/graphql'
|
import { defaultClient } from '~/context/graphql'
|
||||||
import loadShoutCommentsQuery from '~/graphql/query/core/article-comments-load'
|
|
||||||
import getShoutQuery from '~/graphql/query/core/article-load'
|
import getShoutQuery from '~/graphql/query/core/article-load'
|
||||||
import loadShoutRatingsQuery from '~/graphql/query/core/article-ratings-load'
|
|
||||||
import loadShoutsByQuery from '~/graphql/query/core/articles-load-by'
|
import loadShoutsByQuery from '~/graphql/query/core/articles-load-by'
|
||||||
import loadShoutsSearchQuery from '~/graphql/query/core/articles-load-search'
|
import loadShoutsSearchQuery from '~/graphql/query/core/articles-load-search'
|
||||||
import getAuthorQuery from '~/graphql/query/core/author-by'
|
import getAuthorQuery from '~/graphql/query/core/author-by'
|
||||||
import loadAuthorsAllQuery from '~/graphql/query/core/authors-all'
|
import loadAuthorsAllQuery from '~/graphql/query/core/authors-all'
|
||||||
import loadAuthorsByQuery from '~/graphql/query/core/authors-load-by'
|
import loadAuthorsByQuery from '~/graphql/query/core/authors-load-by'
|
||||||
import loadCommentRatingsQuery from '~/graphql/query/core/comment-ratings-load'
|
|
||||||
import loadReactionsByQuery from '~/graphql/query/core/reactions-load-by'
|
import loadReactionsByQuery from '~/graphql/query/core/reactions-load-by'
|
||||||
import loadFollowersByTopicQuery from '~/graphql/query/core/topic-followers'
|
import loadFollowersByTopicQuery from '~/graphql/query/core/topic-followers'
|
||||||
import loadTopicsQuery from '~/graphql/query/core/topics-all'
|
import loadTopicsQuery from '~/graphql/query/core/topics-all'
|
||||||
|
@ -19,7 +16,6 @@ import {
|
||||||
QueryGet_ShoutArgs,
|
QueryGet_ShoutArgs,
|
||||||
QueryLoad_Authors_ByArgs,
|
QueryLoad_Authors_ByArgs,
|
||||||
QueryLoad_Reactions_ByArgs,
|
QueryLoad_Reactions_ByArgs,
|
||||||
QueryLoad_Shout_RatingsArgs,
|
|
||||||
QueryLoad_Shouts_SearchArgs,
|
QueryLoad_Shouts_SearchArgs,
|
||||||
Reaction,
|
Reaction,
|
||||||
Shout,
|
Shout,
|
||||||
|
@ -61,39 +57,16 @@ export const loadShouts = (options: LoadShoutsOptions) => {
|
||||||
}, `shouts-${filter}-${page}`)
|
}, `shouts-${filter}-${page}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadShoutComments = (options: QueryLoad_Shout_RatingsArgs) => {
|
|
||||||
const page = `${options.offset || 0}-${(options.limit || 1) + (options.offset || 0)}`
|
|
||||||
return cache(async () => {
|
|
||||||
const resp = await defaultClient.query(loadShoutCommentsQuery, options).toPromise()
|
|
||||||
const result = resp?.data?.load_reactions_by
|
|
||||||
if (result) return result as Reaction[]
|
|
||||||
}, `shout-${options.shout}-comments-${page}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadShoutRatings = (options: QueryLoad_Shout_RatingsArgs) => {
|
|
||||||
const page = `${options.offset || 0}-${(options.limit || 1) + (options.offset || 0)}`
|
|
||||||
return cache(async () => {
|
|
||||||
const resp = await defaultClient.query(loadShoutRatingsQuery, options).toPromise()
|
|
||||||
const result = resp?.data?.load_reactions_by
|
|
||||||
if (result) return result as Reaction[]
|
|
||||||
}, `shout-${options.shout}-ratings-${page}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME: wait backend
|
|
||||||
export const loadCommentRatings = (options: any) => {
|
|
||||||
const page = `${options.offset || 0}-${(options.limit || 1) + (options.offset || 0)}`
|
|
||||||
return cache(async () => {
|
|
||||||
const resp = await defaultClient.query(loadCommentRatingsQuery, options).toPromise()
|
|
||||||
const result = resp?.data?.load_reactions_by
|
|
||||||
if (result) return result as Reaction[]
|
|
||||||
}, `comment-${options.comment}-ratings-${page}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadReactions = (options: QueryLoad_Reactions_ByArgs) => {
|
export const loadReactions = (options: QueryLoad_Reactions_ByArgs) => {
|
||||||
|
if (!options.by) {
|
||||||
|
console.debug(options)
|
||||||
|
throw new Error('[api] wrong loadReactions call')
|
||||||
|
}
|
||||||
const kind = options.by?.comment ? 'comments' : options.by?.rating ? 'votes' : 'reactions'
|
const kind = options.by?.comment ? 'comments' : options.by?.rating ? 'votes' : 'reactions'
|
||||||
const allorone = options.by?.shout ? `shout-${options.by.shout}` : 'all'
|
const allorone = options.by?.shout ? `shout-${options.by.shout}` : 'all'
|
||||||
const page = `${options.offset || 0}-${(options?.limit || 0) + (options.offset || 0)}`
|
const page = `${options.offset || 0}-${(options?.limit || 0) + (options.offset || 0)}`
|
||||||
const filter = new URLSearchParams(options.by as Record<string, string>)
|
const filter = new URLSearchParams(options.by as Record<string, string>)
|
||||||
|
// console.debug(options)
|
||||||
return cache(async () => {
|
return cache(async () => {
|
||||||
const resp = await defaultClient.query(loadReactionsByQuery, options).toPromise()
|
const resp = await defaultClient.query(loadReactionsByQuery, options).toPromise()
|
||||||
const result = resp?.data?.load_reactions_by
|
const result = resp?.data?.load_reactions_by
|
||||||
|
@ -102,6 +75,7 @@ export const loadReactions = (options: QueryLoad_Reactions_ByArgs) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getShout = (options: QueryGet_ShoutArgs) => {
|
export const getShout = (options: QueryGet_ShoutArgs) => {
|
||||||
|
// console.debug('[lib.api] get shout options', options)
|
||||||
return cache(
|
return cache(
|
||||||
async () => {
|
async () => {
|
||||||
const resp = await defaultClient.query(getShoutQuery, { ...options }).toPromise()
|
const resp = await defaultClient.query(getShoutQuery, { ...options }).toPromise()
|
||||||
|
|
|
@ -18,7 +18,6 @@ export default gql`
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
created_by {
|
created_by {
|
||||||
id
|
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
pic
|
pic
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { gql } from '@urql/core'
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
mutation DeleteReactionMutation($reaction: Int!) {
|
mutation DeleteReactionMutation($reaction_id: Int!) {
|
||||||
delete_reaction(reaction_id: $reaction) {
|
delete_reaction(reaction_id: $reaction_id) {
|
||||||
error
|
error
|
||||||
reaction {
|
reaction {
|
||||||
id
|
id
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query LoadReactions($shout: Int!, $limit: Int, $offset: Int) {
|
|
||||||
load_shout_comments(shout: $shout, limit: $limit, offset: $offset) {
|
|
||||||
id
|
|
||||||
kind
|
|
||||||
body
|
|
||||||
reply_to
|
|
||||||
shout {
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
}
|
|
||||||
created_by {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
pic
|
|
||||||
created_at
|
|
||||||
}
|
|
||||||
created_at
|
|
||||||
updated_at
|
|
||||||
stat {
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query LoadReactions($shout: Int!, $limit: Int, $offset: Int) {
|
|
||||||
load_shout_ratings(shout: $shout, limit: $limit, offset: $offset) {
|
|
||||||
id
|
|
||||||
kind
|
|
||||||
body
|
|
||||||
reply_to
|
|
||||||
shout {
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
}
|
|
||||||
created_by {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
pic
|
|
||||||
created_at
|
|
||||||
}
|
|
||||||
created_at
|
|
||||||
updated_at
|
|
||||||
stat {
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
query LoadReactions($comment: Int!, $limit: Int, $offset: Int) {
|
|
||||||
load_comment_ratings(comment: $comment, limit: $limit, offset: $offset) {
|
|
||||||
id
|
|
||||||
kind
|
|
||||||
body
|
|
||||||
reply_to
|
|
||||||
shout {
|
|
||||||
id
|
|
||||||
slug
|
|
||||||
title
|
|
||||||
}
|
|
||||||
created_by {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
slug
|
|
||||||
pic
|
|
||||||
created_at
|
|
||||||
}
|
|
||||||
created_at
|
|
||||||
updated_at
|
|
||||||
stat {
|
|
||||||
rating
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
|
@ -13,7 +13,6 @@ export default gql`
|
||||||
title
|
title
|
||||||
}
|
}
|
||||||
created_by {
|
created_by {
|
||||||
id
|
|
||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
pic
|
pic
|
||||||
|
|
|
@ -456,7 +456,7 @@
|
||||||
"Theory": "Теории",
|
"Theory": "Теории",
|
||||||
"There are unsaved changes in your profile settings. Are you sure you want to leave the page without saving?": "В настройках вашего профиля есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
|
"There are unsaved changes in your profile settings. Are you sure you want to leave the page without saving?": "В настройках вашего профиля есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
|
||||||
"There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?": "В настройках публикации есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
|
"There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?": "В настройках публикации есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
|
||||||
"This comment has not been rated yet": "Этот комментарий еще пока никто не оценил",
|
"This comment has not yet been rated": "Этот комментарий еще пока никто не оценил",
|
||||||
"This content is not published yet": "Содержимое ещё не опубликовано",
|
"This content is not published yet": "Содержимое ещё не опубликовано",
|
||||||
"This email is": "Этот email",
|
"This email is": "Этот email",
|
||||||
"This email is not verified": "Этот email не подтвержден",
|
"This email is not verified": "Этот email не подтвержден",
|
||||||
|
|
|
@ -34,7 +34,7 @@ export const byStat = (metric: string) => {
|
||||||
return (a: { stat?: SomeStat }, b: { stat?: SomeStat }) => {
|
return (a: { stat?: SomeStat }, b: { stat?: SomeStat }) => {
|
||||||
const aStat = a.stat?.[metric] ?? 0
|
const aStat = a.stat?.[metric] ?? 0
|
||||||
const bStat = b.stat?.[metric] ?? 0
|
const bStat = b.stat?.[metric] ?? 0
|
||||||
return bStat - aStat
|
return aStat - bStat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,38 @@
|
||||||
import { RouteDefinition, RoutePreloadFuncArgs, type RouteSectionProps, createAsync } from '@solidjs/router'
|
import { RouteDefinition, RouteLoadFuncArgs, type RouteSectionProps, createAsync } from '@solidjs/router'
|
||||||
import { Suspense, createEffect, on } from 'solid-js'
|
import { Suspense, createEffect, on } from 'solid-js'
|
||||||
import { AllAuthors } from '~/components/Views/AllAuthors'
|
import { AllAuthors } from '~/components/Views/AllAuthors'
|
||||||
|
import { AUTHORS_PER_PAGE } from '~/components/Views/AllAuthors/AllAuthors'
|
||||||
import { Loading } from '~/components/_shared/Loading'
|
import { Loading } from '~/components/_shared/Loading'
|
||||||
import { PageLayout } from '~/components/_shared/PageLayout'
|
import { PageLayout } from '~/components/_shared/PageLayout'
|
||||||
import { useAuthors } from '~/context/authors'
|
import { useAuthors } from '~/context/authors'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { loadAuthorsAll } from '~/graphql/api/public'
|
import { loadAuthors, loadAuthorsAll } from '~/graphql/api/public'
|
||||||
import { Author } from '~/graphql/schema/core.gen'
|
import { Author, AuthorsBy } from '~/graphql/schema/core.gen'
|
||||||
|
|
||||||
|
const fetchAuthorsWithStat = async (offset = 0, order?: string) => {
|
||||||
|
const by: AuthorsBy = { order }
|
||||||
|
const authorsFetcher = loadAuthors({ by, offset, limit: AUTHORS_PER_PAGE })
|
||||||
|
return await authorsFetcher()
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch Function
|
|
||||||
const fetchAllAuthors = async () => {
|
const fetchAllAuthors = async () => {
|
||||||
const authorsAllFetcher = loadAuthorsAll()
|
const authorsAllFetcher = loadAuthorsAll()
|
||||||
return await authorsAllFetcher()
|
return await authorsAllFetcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Route Defenition
|
|
||||||
export const route = {
|
export const route = {
|
||||||
load: async ({ location: { query: _q } }: RoutePreloadFuncArgs) => {
|
load: async ({ location: { query } }: RouteLoadFuncArgs) => {
|
||||||
|
const by = query.by
|
||||||
|
const isAll = !by || by === 'name'
|
||||||
return {
|
return {
|
||||||
authors: await fetchAllAuthors()
|
authors: isAll && (await fetchAllAuthors()),
|
||||||
}
|
authorsByFollowers: await fetchAuthorsWithStat(10, 'followers'),
|
||||||
|
authorsByShouts: await fetchAuthorsWithStat(10, 'shouts')
|
||||||
|
} as AllAuthorsData
|
||||||
}
|
}
|
||||||
} satisfies RouteDefinition
|
} satisfies RouteDefinition
|
||||||
|
|
||||||
type AllAuthorsData = { authors: Author[] }
|
type AllAuthorsData = { authors: Author[]; authorsByFollowers: Author[]; authorsByShouts: Author[] }
|
||||||
|
|
||||||
// addAuthors to context
|
// addAuthors to context
|
||||||
|
|
||||||
|
@ -31,20 +40,25 @@ export default function AllAuthorsPage(props: RouteSectionProps<AllAuthorsData>)
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { addAuthors } = useAuthors()
|
const { addAuthors } = useAuthors()
|
||||||
|
|
||||||
|
// async load data: from ssr or fetch
|
||||||
const data = createAsync<AllAuthorsData>(async () => {
|
const data = createAsync<AllAuthorsData>(async () => {
|
||||||
if (props.data) return props.data
|
if (props.data) return props.data
|
||||||
const authors = await fetchAllAuthors()
|
|
||||||
return {
|
return {
|
||||||
authors: authors || []
|
authors: await fetchAllAuthors(),
|
||||||
}
|
authorsByFollowers: await fetchAuthorsWithStat(10, 'followers'),
|
||||||
|
authorsByShouts: await fetchAuthorsWithStat(10, 'shouts')
|
||||||
|
} as AllAuthorsData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// update context when data is loaded
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(
|
||||||
[data, () => addAuthors],
|
[data, () => addAuthors],
|
||||||
([data, aa]) => {
|
([data, aa]) => {
|
||||||
if (data && aa) {
|
if (data && aa) {
|
||||||
aa(data.authors as Author[])
|
aa(data.authors as Author[])
|
||||||
|
aa(data.authorsByFollowers as Author[])
|
||||||
|
aa(data.authorsByShouts as Author[])
|
||||||
console.debug('[routes.author] added all authors:', data.authors)
|
console.debug('[routes.author] added all authors:', data.authors)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -59,7 +73,12 @@ export default function AllAuthorsPage(props: RouteSectionProps<AllAuthorsData>)
|
||||||
desc="List of authors of the open editorial community"
|
desc="List of authors of the open editorial community"
|
||||||
>
|
>
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
<AllAuthors isLoaded={Boolean(data()?.authors)} authors={data()?.authors || []} />
|
<AllAuthors
|
||||||
|
isLoaded={Boolean(data()?.authors)}
|
||||||
|
authors={data()?.authors || []}
|
||||||
|
authorsByFollowers={data()?.authorsByFollowers}
|
||||||
|
authorsByShouts={data()?.authorsByShouts}
|
||||||
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { RouteSectionProps, createAsync } from '@solidjs/router'
|
import { RouteSectionProps } from '@solidjs/router'
|
||||||
import { ErrorBoundary, createEffect, createMemo } from 'solid-js'
|
import { ErrorBoundary, createEffect, createMemo, createSignal, on } from 'solid-js'
|
||||||
import { AuthorView } from '~/components/Views/Author'
|
import { AuthorView } from '~/components/Views/Author'
|
||||||
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
import { FourOuFourView } from '~/components/Views/FourOuFour'
|
||||||
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
|
||||||
|
@ -32,7 +32,6 @@ const fetchAllTopics = async () => {
|
||||||
const fetchAuthor = async (slug: string) => {
|
const fetchAuthor = async (slug: string) => {
|
||||||
const authorFetcher = loadAuthors({ by: { slug }, limit: 1, offset: 0 } as QueryLoad_Authors_ByArgs)
|
const authorFetcher = loadAuthors({ by: { slug }, limit: 1, offset: 0 } as QueryLoad_Authors_ByArgs)
|
||||||
const aaa = await authorFetcher()
|
const aaa = await authorFetcher()
|
||||||
console.debug(aaa)
|
|
||||||
return aaa?.[0]
|
return aaa?.[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,20 +49,11 @@ export const route = {
|
||||||
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
export type AuthorPageProps = { articles?: Shout[]; author?: Author; topics?: Topic[] }
|
||||||
|
|
||||||
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
const { authorsEntities } = useAuthors()
|
const { addAuthor, authorsEntities } = useAuthors()
|
||||||
const { addFeed, feedByAuthor } = useFeed()
|
const [author, setAuthor] = createSignal<Author | undefined>(undefined)
|
||||||
|
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const author = createAsync(
|
|
||||||
async () =>
|
|
||||||
props.data.author || authorsEntities()[props.params.slug] || (await fetchAuthor(props.params.slug))
|
|
||||||
)
|
|
||||||
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
|
|
||||||
const title = createMemo(() => `${author()?.name || ''}`)
|
const title = createMemo(() => `${author()?.name || ''}`)
|
||||||
const cover = createMemo(() =>
|
|
||||||
author()?.pic
|
|
||||||
? getImageUrl(author()?.pic || '', { width: 1200 })
|
|
||||||
: getImageUrl('production/image/logo_image.png')
|
|
||||||
)
|
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (author()) {
|
if (author()) {
|
||||||
|
@ -76,7 +66,32 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const cover = createMemo(() =>
|
||||||
|
author()?.pic
|
||||||
|
? getImageUrl(author()?.pic || '', { width: 1200 })
|
||||||
|
: getImageUrl('production/image/logo_image.png')
|
||||||
|
)
|
||||||
|
|
||||||
// author shouts
|
// author shouts
|
||||||
|
const { addFeed, feedByAuthor } = useFeed()
|
||||||
|
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
|
||||||
|
|
||||||
|
createEffect(
|
||||||
|
on(
|
||||||
|
[() => props.params.slug || '', author],
|
||||||
|
async ([slug, profile]) => {
|
||||||
|
if (!profile) {
|
||||||
|
const loadedAuthor = authorsEntities()[slug] || (await fetchAuthor(slug))
|
||||||
|
if (loadedAuthor) {
|
||||||
|
addAuthor(loadedAuthor)
|
||||||
|
setAuthor(loadedAuthor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ defer: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const loadAuthorShoutsMore = async (offset: number) => {
|
const loadAuthorShoutsMore = async (offset: number) => {
|
||||||
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
||||||
loadedShouts && addFeed(loadedShouts)
|
loadedShouts && addFeed(loadedShouts)
|
||||||
|
|
|
@ -125,7 +125,7 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>)
|
||||||
key="feed"
|
key="feed"
|
||||||
desc="Independent media project about culture, science, art and society with horizontal editing"
|
desc="Independent media project about culture, science, art and society with horizontal editing"
|
||||||
>
|
>
|
||||||
<LoadMoreWrapper loadFunction={loadMoreFeed} pageSize={AUTHORS_PER_PAGE} hidden={!feed()}>
|
<LoadMoreWrapper loadFunction={loadMoreFeed} pageSize={AUTHORS_PER_PAGE}>
|
||||||
<ReactionsProvider>
|
<ReactionsProvider>
|
||||||
<Feed shouts={feed() || (shouts() as Shout[])} order={order() as FeedProps['order']} />
|
<Feed shouts={feed() || (shouts() as Shout[])} order={order() as FeedProps['order']} />
|
||||||
</ReactionsProvider>
|
</ReactionsProvider>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user