Merge branch 'main' into feature/search-modal
106
.eslintrc.cjs
Normal file
|
@ -0,0 +1,106 @@
|
|||
module.exports = {
|
||||
plugins: ["@typescript-eslint", "import", "sonarjs", "unicorn", "promise", "solid", "jest"],
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:import/recommended",
|
||||
"plugin:import/typescript",
|
||||
"prettier",
|
||||
"plugin:sonarjs/recommended",
|
||||
"plugin:unicorn/recommended",
|
||||
"plugin:promise/recommended",
|
||||
"plugin:solid/recommended",
|
||||
"plugin:jest/recommended"
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaVersion: 2021,
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: "module",
|
||||
project: "./tsconfig.json"
|
||||
},
|
||||
extends: [
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
// Maybe one day...
|
||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking'
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
argsIgnorePattern: "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
// TODO: Remove any usage and enable
|
||||
"@typescript-eslint/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
mocha: true
|
||||
},
|
||||
globals: {},
|
||||
rules: {
|
||||
// Solid
|
||||
"solid/reactivity": "off", // FIXME
|
||||
"solid/no-innerhtml": "off",
|
||||
|
||||
/** Unicorn **/
|
||||
"unicorn/no-null": "off",
|
||||
"unicorn/filename-case": "off",
|
||||
"unicorn/no-array-for-each": "off",
|
||||
"unicorn/no-array-reduce": "off",
|
||||
"unicorn/prefer-string-replace-all": "warn",
|
||||
"unicorn/prevent-abbreviations": "off",
|
||||
"unicorn/prefer-module": "off",
|
||||
"unicorn/import-style": "off",
|
||||
"unicorn/numeric-separators-style": "off",
|
||||
"unicorn/prefer-node-protocol": "off",
|
||||
"unicorn/prefer-dom-node-append": "off", // FIXME
|
||||
"unicorn/prefer-top-level-await": "warn",
|
||||
"unicorn/consistent-function-scoping": "warn",
|
||||
"unicorn/no-array-callback-reference": "warn",
|
||||
"unicorn/no-array-method-this-argument": "warn",
|
||||
"unicorn/no-for-loop": "off",
|
||||
|
||||
"sonarjs/no-duplicate-string": ["warn", { threshold: 5 }],
|
||||
|
||||
// Promise
|
||||
// 'promise/catch-or-return': 'off', // Should be enabled
|
||||
"promise/always-return": "off",
|
||||
|
||||
eqeqeq: "error",
|
||||
"no-param-reassign": "error",
|
||||
"no-nested-ternary": "error",
|
||||
"no-shadow": "error",
|
||||
|
||||
"import/order": ["warn", {
|
||||
groups: ["type", "builtin", "external", "internal", "parent", "sibling", "index"],
|
||||
distinctGroup: false,
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: "*.scss",
|
||||
patternOptions: { matchBase: true },
|
||||
group: "index",
|
||||
position: "after"
|
||||
}
|
||||
],
|
||||
"newlines-between": "always",
|
||||
alphabetize: {
|
||||
order: "asc",
|
||||
caseInsensitive: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: true,
|
||||
node: true
|
||||
}
|
||||
}
|
||||
};
|
87
.eslintrc.js
|
@ -1,87 +0,0 @@
|
|||
module.exports = {
|
||||
plugins: ['@typescript-eslint', 'import', 'sonarjs', 'unicorn', 'promise', 'solid', 'jest'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:import/typescript',
|
||||
'prettier',
|
||||
'plugin:sonarjs/recommended',
|
||||
'plugin:unicorn/recommended',
|
||||
'plugin:promise/recommended',
|
||||
'plugin:solid/recommended',
|
||||
'plugin:jest/recommended'
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2021,
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
project: './tsconfig.json'
|
||||
},
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
// Maybe one day...
|
||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking'
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
// TODO: Remove any usage and enable
|
||||
'@typescript-eslint/no-explicit-any': 'off'
|
||||
}
|
||||
}
|
||||
],
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
mocha: true
|
||||
},
|
||||
globals: {},
|
||||
rules: {
|
||||
// Solid
|
||||
'solid/reactivity': 'off', // FIXME
|
||||
'solid/no-innerhtml': 'off',
|
||||
|
||||
/** Unicorn **/
|
||||
'unicorn/no-null': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'unicorn/no-array-for-each': 'off',
|
||||
'unicorn/no-array-reduce': 'off',
|
||||
'unicorn/prefer-string-replace-all': 'warn',
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
'unicorn/prefer-module': 'off',
|
||||
'unicorn/import-style': 'off',
|
||||
'unicorn/numeric-separators-style': 'off',
|
||||
'unicorn/prefer-node-protocol': 'off',
|
||||
'unicorn/prefer-dom-node-append': 'off', // FIXME
|
||||
'unicorn/prefer-top-level-await': 'warn',
|
||||
'unicorn/consistent-function-scoping': 'warn',
|
||||
'unicorn/no-array-callback-reference': 'warn',
|
||||
'unicorn/no-array-method-this-argument': 'warn',
|
||||
|
||||
'sonarjs/no-duplicate-string': ['warn', { threshold: 5 }],
|
||||
|
||||
// Promise
|
||||
// 'promise/catch-or-return': 'off', // Should be enabled
|
||||
'promise/always-return': 'off',
|
||||
|
||||
eqeqeq: 'error',
|
||||
'no-param-reassign': 'error',
|
||||
'no-nested-ternary': 'error',
|
||||
'no-shadow': 'error'
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: true,
|
||||
node: true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"*.{js,mjs,ts,tsx,json,scss,css,html}": "prettier --write",
|
||||
"package.json": "sort-package-json"
|
||||
"*.{js,ts,tsx,json,scss,css,html}": "prettier --write",
|
||||
"package.json": "sort-package-json",
|
||||
"public/locales/**/*.json": "sort-json"
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
"singleQuote": true,
|
||||
"proseWrap": "always",
|
||||
"printWidth": 108,
|
||||
"trailingComma": "none",
|
||||
"plugins": [],
|
||||
"overrides": [
|
||||
{
|
||||
|
|
32
api/edge-ssr.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { renderPage } from 'vike/server'
|
||||
|
||||
export const config = {
|
||||
runtime: 'edge',
|
||||
}
|
||||
export default async function handler(request) {
|
||||
const { url, cookies } = request
|
||||
|
||||
const pageContext = await renderPage({ urlOriginal: url, cookies })
|
||||
|
||||
const { httpResponse, errorWhileRendering, is404 } = pageContext
|
||||
|
||||
if (errorWhileRendering && !is404) {
|
||||
console.error(errorWhileRendering)
|
||||
return new Response('', { status: 500 })
|
||||
}
|
||||
|
||||
if (!httpResponse) {
|
||||
return new Response()
|
||||
}
|
||||
|
||||
const { body, statusCode, headers: headersArray } = httpResponse
|
||||
|
||||
const headers = headersArray.reduce((acc, [name, value]) => {
|
||||
acc[name] = value
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
headers['Cache-Control'] = 's-maxage=1, stale-while-revalidate'
|
||||
|
||||
return new Response(body, { status: statusCode, headers })
|
||||
}
|
|
@ -15,7 +15,7 @@ export default async function handler(req, res) {
|
|||
from: 'Discours Feedback Robot <robot@discours.io>',
|
||||
to: 'welcome@discours.io',
|
||||
subject,
|
||||
text
|
||||
text,
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -13,18 +13,18 @@ export default async (req, res) => {
|
|||
const response = await mg.lists.members.createMember('newsletter@discours.io', {
|
||||
address: email,
|
||||
subscribed: true,
|
||||
upsert: 'yes'
|
||||
upsert: 'yes',
|
||||
})
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: 'Email was added to newsletter list',
|
||||
response: JSON.stringify(response)
|
||||
response: JSON.stringify(response),
|
||||
})
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.message
|
||||
message: error.message,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
27
api/ssr.mjs
|
@ -1,27 +0,0 @@
|
|||
import { renderPage } from 'vite-plugin-ssr/server'
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const { url, cookies } = req
|
||||
|
||||
const pageContext = await renderPage({ urlOriginal: url, cookies })
|
||||
|
||||
const { httpResponse, errorWhileRendering } = pageContext
|
||||
|
||||
if (errorWhileRendering) {
|
||||
console.error(errorWhileRendering)
|
||||
res.statusCode = 500
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
if (!httpResponse) {
|
||||
res.statusCode = 200
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
const { body, statusCode, contentType } = httpResponse
|
||||
res.statusCode = statusCode
|
||||
res.setHeader('Content-Type', contentType)
|
||||
res.end(body)
|
||||
}
|
18039
package-lock.json
generated
83
package.json
|
@ -3,6 +3,7 @@
|
|||
"version": "0.8.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"check": "npm run lint && npm run typecheck",
|
||||
|
@ -35,20 +36,21 @@
|
|||
"intl-messageformat": "10.5.3",
|
||||
"just-throttle": "4.2.0",
|
||||
"mailgun.js": "8.2.1",
|
||||
"sanitize-html": "2.11.0"
|
||||
"sanitize-html": "2.11.0",
|
||||
"mailgun.js": "8.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.21.8",
|
||||
"@graphql-codegen/cli": "3.2.2",
|
||||
"@graphql-codegen/typescript": "3.0.4",
|
||||
"@graphql-codegen/typescript-operations": "3.0.4",
|
||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||
"@graphql-codegen/urql-introspection": "2.2.1",
|
||||
"@babel/core": "7.23.3",
|
||||
"@graphql-codegen/cli": "5.0.0",
|
||||
"@graphql-codegen/typescript": "4.0.1",
|
||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||
"@graphql-codegen/typescript-urql": "4.0.0",
|
||||
"@graphql-codegen/urql-introspection": "3.0.0",
|
||||
"@graphql-tools/url-loader": "7.17.18",
|
||||
"@graphql-typed-document-node/core": "3.2.0",
|
||||
"@hocuspocus/provider": "2.0.6",
|
||||
"@nanostores/router": "0.8.3",
|
||||
"@nanostores/solid": "0.3.2",
|
||||
"@nanostores/router": "0.11.0",
|
||||
"@nanostores/solid": "0.4.2",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@sentry/browser": "5.30.0",
|
||||
"@solid-primitives/media": "2.2.3",
|
||||
|
@ -56,7 +58,7 @@
|
|||
"@solid-primitives/share": "2.0.4",
|
||||
"@solid-primitives/storage": "1.3.9",
|
||||
"@solid-primitives/upload": "0.0.110",
|
||||
"@solidjs/meta": "0.28.2",
|
||||
"@solidjs/meta": "0.29.1",
|
||||
"@thisbeyond/solid-select": "0.14.0",
|
||||
"@tiptap/core": "2.0.3",
|
||||
"@tiptap/extension-blockquote": "2.0.3",
|
||||
|
@ -87,71 +89,66 @@
|
|||
"@tiptap/extension-text": "2.0.3",
|
||||
"@tiptap/extension-underline": "2.0.3",
|
||||
"@tiptap/extension-youtube": "2.0.3",
|
||||
"@types/js-cookie": "3.0.4",
|
||||
"@types/node": "20.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.3",
|
||||
"@typescript-eslint/parser": "6.7.3",
|
||||
"@types/js-cookie": "3.0.6",
|
||||
"@types/node": "20.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.10.0",
|
||||
"@typescript-eslint/parser": "6.10.0",
|
||||
"@urql/core": "3.2.2",
|
||||
"@urql/devtools": "2.0.3",
|
||||
"babel-preset-solid": "1.7.4",
|
||||
"babel-preset-solid": "1.8.4",
|
||||
"bootstrap": "5.3.2",
|
||||
"clsx": "2.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"debounce": "1.2.1",
|
||||
"eslint": "8.50.0",
|
||||
"eslint": "8.53.0",
|
||||
"eslint-config-stylelint": "20.0.0",
|
||||
"eslint-import-resolver-typescript": "3.6.1",
|
||||
"eslint-plugin-import": "2.28.1",
|
||||
"eslint-plugin-jest": "27.4.0",
|
||||
"eslint-plugin-jsx-a11y": "6.7.1",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-jest": "27.6.0",
|
||||
"eslint-plugin-jsx-a11y": "6.8.0",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"eslint-plugin-solid": "0.13.0",
|
||||
"eslint-plugin-sonarjs": "0.21.0",
|
||||
"eslint-plugin-unicorn": "48.0.1",
|
||||
"eslint-plugin-sonarjs": "0.23.0",
|
||||
"eslint-plugin-unicorn": "49.0.0",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"graphql": "16.6.0",
|
||||
"graphql": "16.8.1",
|
||||
"graphql-tag": "2.12.6",
|
||||
"html-to-json-parser": "1.1.0",
|
||||
"husky": "8.0.3",
|
||||
"hygen": "6.2.11",
|
||||
"i18next-http-backend": "2.2.0",
|
||||
"javascript-time-ago": "2.5.9",
|
||||
"jest": "29.7.0",
|
||||
"js-cookie": "3.0.5",
|
||||
"lint-staged": "14.0.1",
|
||||
"lint-staged": "15.1.0",
|
||||
"loglevel": "1.8.1",
|
||||
"loglevel-plugin-prefix": "0.8.4",
|
||||
"markdown-it": "13.0.1",
|
||||
"markdown-it-container": "3.0.0",
|
||||
"markdown-it-implicit-figures": "0.11.0",
|
||||
"markdown-it-mark": "3.0.1",
|
||||
"markdown-it-replace-link": "1.2.0",
|
||||
"nanostores": "0.7.4",
|
||||
"prettier": "3.0.3",
|
||||
"prettier-eslint": "15.0.1",
|
||||
"nanostores": "0.9.5",
|
||||
"prettier": "3.1.0",
|
||||
"prettier-eslint": "16.1.2",
|
||||
"prosemirror-history": "1.3.0",
|
||||
"prosemirror-trailing-node": "2.0.3",
|
||||
"prosemirror-view": "1.30.2",
|
||||
"rollup": "3.21.6",
|
||||
"sass": "1.68.0",
|
||||
"solid-js": "1.7.5",
|
||||
"sass": "1.69.5",
|
||||
"solid-js": "1.8.7",
|
||||
"solid-popper": "0.3.0",
|
||||
"solid-tiptap": "0.6.0",
|
||||
"solid-transition-group": "0.2.2",
|
||||
"solid-transition-group": "0.2.3",
|
||||
"sort-json": "2.0.1",
|
||||
"sort-package-json": "2.6.0",
|
||||
"stylelint": "15.10.3",
|
||||
"stylelint-config-standard-scss": "11.0.0",
|
||||
"stylelint": "15.11.0",
|
||||
"stylelint-config-standard-scss": "11.1.0",
|
||||
"stylelint-order": "6.0.3",
|
||||
"stylelint-scss": "5.2.1",
|
||||
"swiper": "9.4.1",
|
||||
"stylelint-scss": "5.3.1",
|
||||
"swiper": "11.0.5",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"typescript": "5.2.2",
|
||||
"typograf": "7.1.0",
|
||||
"uniqolor": "1.1.0",
|
||||
"vite": "4.3.9",
|
||||
"vike": "0.4.148",
|
||||
"vite": "4.5.0",
|
||||
"vite-plugin-mkcert": "1.16.0",
|
||||
"vite-plugin-sass-dts": "1.3.11",
|
||||
"vite-plugin-solid": "2.7.0",
|
||||
"vite-plugin-ssr": "0.4.123",
|
||||
"vite-plugin-solid": "2.7.2",
|
||||
"y-prosemirror": "1.2.1",
|
||||
"yjs": "13.6.0"
|
||||
},
|
||||
|
|
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 744 KiB |
Before Width: | Height: | Size: 144 KiB |
5
public/icons/copy.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="ic24_copy">
|
||||
<path id="vector" fill-rule="evenodd" clip-rule="evenodd" d="M4 6C4 3.79086 5.79086 2 8 2H12C14.2091 2 16 3.79086 16 6V14C16 16.2091 14.2091 18 12 18H8C5.79086 18 4 16.2091 4 14V6ZM8 4C6.89543 4 6 4.89543 6 6V14C6 15.1046 6.89543 16 8 16H12C13.1046 16 14 15.1046 14 14V6C14 4.89543 13.1046 4 12 4H8ZM16.6344 6.90064C16.9109 6.42258 17.5227 6.25922 18.0007 6.53576C19.1937 7.22587 20 8.5182 20 10V18C20 20.2092 18.2091 22 16 22H12C10.5182 22 9.22586 21.1937 8.53575 20.0007C8.2592 19.5227 8.42257 18.911 8.90063 18.6344C9.37869 18.3579 9.99041 18.5212 10.267 18.9993C10.6143 19.5997 11.261 20 12 20H16C17.1046 20 18 19.1046 18 18V10C18 9.261 17.5997 8.61429 16.9993 8.26697C16.5212 7.99043 16.3579 7.3787 16.6344 6.90064Z" fill="#141414"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 866 B |
3
public/icons/lightning.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.89589 13.4965C6.77089 13.6545 6.63373 13.7326 6.48269 13.7309C6.33339 13.7291 6.21533 13.6684 6.132 13.5468C6.04867 13.4271 6.04172 13.2691 6.11637 13.0712L7.61117 9.19789H4.76915C4.65283 9.19789 4.55561 9.16143 4.47575 9.08504C4.39589 9.01039 4.35596 8.91491 4.35596 8.79859C4.35596 8.68227 4.40283 8.56421 4.49832 8.44442L9.10422 2.50345C9.22922 2.34546 9.36637 2.26733 9.51742 2.26907C9.66672 2.27081 9.78478 2.33157 9.86811 2.4531C9.95144 2.57289 9.95839 2.73088 9.88373 2.92879L8.38894 6.80206H11.231C11.3473 6.80206 11.4445 6.83852 11.5244 6.9149C11.6042 6.98956 11.6442 7.08504 11.6442 7.20136C11.6442 7.31768 11.5973 7.43574 11.5018 7.55553L6.89589 13.4965Z" fill="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 797 B |
3
public/icons/social-facebook.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.3459 13.1364H8.69673V22.1618C8.69673 22.34 8.85155 22.4844 9.04263 22.4844H13.0285C13.2196 22.4844 13.3744 22.34 13.3744 22.1618V13.1789H16.0769C16.2526 13.1789 16.4005 13.0559 16.4205 12.8931L16.831 9.57044C16.8423 9.47902 16.8112 9.38747 16.7456 9.31889C16.68 9.25025 16.586 9.21096 16.4874 9.21096H13.3746V7.12812C13.3746 6.50025 13.7371 6.18186 14.4521 6.18186C14.554 6.18186 16.4874 6.18186 16.4874 6.18186C16.6785 6.18186 16.8333 6.03741 16.8333 5.85928V2.80934C16.8333 2.63115 16.6785 2.48676 16.4874 2.48676H13.6825C13.6627 2.48586 13.6188 2.48438 13.554 2.48438C13.0673 2.48438 11.3757 2.57347 10.0394 3.71992C8.55878 4.99038 8.76459 6.51154 8.81378 6.77528V9.21089H6.3459C6.15483 9.21089 6 9.35528 6 9.53347V12.8137C6 12.9919 6.15483 13.1364 6.3459 13.1364Z" fill="#141414"/>
|
||||
</svg>
|
After Width: | Height: | Size: 901 B |
5
public/icons/social-instagram.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="vk logo">
|
||||
<path id="Vector" d="M14.88 12.4844C14.88 13.054 14.7111 13.6108 14.3946 14.0844C14.0782 14.558 13.6284 14.9272 13.1021 15.1451C12.5759 15.3631 11.9968 15.4202 11.4381 15.309C10.8795 15.1979 10.3663 14.9236 9.96353 14.5208C9.56076 14.1181 9.28646 13.6049 9.17534 13.0462C9.06421 12.4876 9.12125 11.9085 9.33923 11.3822C9.55721 10.856 9.92634 10.4062 10.4 10.0897C10.8736 9.77328 11.4304 9.60438 12 9.60438C12.7636 9.60525 13.4956 9.90896 14.0355 10.4489C14.5754 10.9888 14.8791 11.7208 14.88 12.4844ZM21 8.52437V16.4444C20.9985 17.7806 20.467 19.0617 19.5221 20.0065C18.5773 20.9514 17.2962 21.4829 15.96 21.4844H8.04C6.70377 21.4829 5.42271 20.9514 4.47785 20.0065C3.533 19.0617 3.00151 17.7806 3 16.4444V8.52437C3.00151 7.18815 3.533 5.90708 4.47785 4.96223C5.42271 4.01737 6.70377 3.48589 8.04 3.48438H15.96C17.2962 3.48589 18.5773 4.01737 19.5221 4.96223C20.467 5.90708 20.9985 7.18815 21 8.52437ZM16.32 12.4844C16.32 11.63 16.0666 10.7947 15.5919 10.0843C15.1173 9.37389 14.4426 8.82019 13.6532 8.49322C12.8638 8.16625 11.9952 8.0807 11.1572 8.24738C10.3192 8.41407 9.54946 8.82551 8.9453 9.42967C8.34114 10.0338 7.9297 10.8036 7.76301 11.6416C7.59632 12.4796 7.68187 13.3482 8.00884 14.1376C8.33581 14.9269 8.88952 15.6016 9.59994 16.0763C10.3104 16.551 11.1456 16.8044 12 16.8044C13.1453 16.8031 14.2434 16.3475 15.0533 15.5376C15.8631 14.7278 16.3187 13.6297 16.32 12.4844ZM17.76 7.80438C17.76 7.59077 17.6967 7.38196 17.578 7.20436C17.4593 7.02675 17.2906 6.88833 17.0933 6.80659C16.896 6.72484 16.6788 6.70345 16.4693 6.74513C16.2598 6.7868 16.0674 6.88966 15.9163 7.0407C15.7653 7.19174 15.6624 7.38418 15.6208 7.59368C15.5791 7.80318 15.6005 8.02033 15.6822 8.21767C15.764 8.41502 15.9024 8.58369 16.08 8.70236C16.2576 8.82103 16.4664 8.88438 16.68 8.88438C16.9664 8.88438 17.2411 8.77059 17.4437 8.56805C17.6462 8.36551 17.76 8.09081 17.76 7.80438Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
7
public/icons/social-linkedin.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="linkedin">
|
||||
<g id="Group">
|
||||
<path id="Vector" d="M6.93855 21.4004V9.51472H2.99144V21.4004H6.93896H6.93855ZM4.96582 7.89221C6.34197 7.89221 7.19872 6.97953 7.19872 5.83894C7.17296 4.67236 6.34197 3.78516 4.99199 3.78516C3.64109 3.78516 2.75879 4.67236 2.75879 5.83884C2.75879 6.97943 3.61522 7.89211 4.93996 7.89211H4.96551L4.96582 7.89221ZM9.12333 21.4004H13.0701V14.7636C13.0701 14.4089 13.0959 14.0532 13.2002 13.7998C13.4854 13.0897 14.1348 12.3548 15.2254 12.3548C16.6533 12.3548 17.2248 13.4446 17.2248 15.0426V21.4004H21.1715V14.5855C21.1715 10.9349 19.2246 9.23607 16.6278 9.23607C14.4987 9.23607 13.5637 10.4271 13.0442 11.2383H13.0704V9.51513H9.12353C9.17505 10.6302 9.12322 21.4008 9.12322 21.4008L9.12333 21.4004Z" fill="black"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 859 B |
5
public/icons/social-telegram.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="telegram logo">
|
||||
<path id="Icon" d="M2.35326 12.1563L6.96167 13.7367L8.7454 19.0076C8.85953 19.3452 9.3088 19.4699 9.607 19.2459L12.1758 17.3218C12.4451 17.1202 12.8286 17.1101 13.11 17.2978L17.7432 20.3886C18.0622 20.6016 18.5141 20.441 18.5941 20.0869L21.9882 5.08587C22.0756 4.69898 21.6618 4.37623 21.2609 4.51871L2.34786 11.2226C1.88113 11.388 1.88519 11.9952 2.35326 12.1563ZM8.45793 12.8954L17.4645 7.79852C17.6263 7.70719 17.7929 7.90829 17.6539 8.02676L10.2209 14.3753C9.9596 14.5988 9.79107 14.8978 9.74334 15.2224L9.49014 16.9465C9.4566 17.1767 9.10467 17.1996 9.03553 16.9768L8.06173 13.8328C7.9502 13.4742 8.11273 13.0912 8.45793 12.8954Z" fill="#141414"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 784 B |
4
public/icons/social-twitter.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M19.5467 1.84651C19.2325 1.98268 18.9095 2.0976 18.5794 2.1909C18.9702 1.75908 19.2681 1.25097 19.45 0.694956C19.4908 0.570325 19.4485 0.433906 19.3437 0.352395C19.239 0.270821 19.0935 0.26105 18.9782 0.327809C18.2772 0.734041 17.521 1.02598 16.728 1.19669C15.9293 0.434032 14.8444 0 13.7228 0C11.3554 0 9.42934 1.88194 9.42934 4.19514C9.42934 4.37733 9.44114 4.55851 9.4645 4.73716C6.52677 4.48513 3.79561 3.07422 1.92014 0.826269C1.8533 0.746145 1.75033 0.702962 1.64491 0.71122C1.53943 0.71929 1.44465 0.777413 1.39136 0.866741C1.01098 1.50452 0.809882 2.23396 0.809882 2.97613C0.809882 3.98698 1.17924 4.94608 1.83169 5.6955C1.6333 5.62836 1.44078 5.54446 1.25704 5.44479C1.1584 5.39114 1.03801 5.39196 0.940011 5.44687C0.841946 5.50178 0.780463 5.60277 0.777882 5.71315C0.77743 5.73175 0.777431 5.75035 0.777431 5.76919C0.777431 7.27806 1.60852 8.63652 2.87917 9.37693C2.77 9.36627 2.66091 9.35083 2.55252 9.33059C2.44078 9.30972 2.32588 9.34799 2.25052 9.43127C2.17504 9.51448 2.15007 9.63047 2.18485 9.73638C2.65517 11.1712 3.86607 12.2265 5.32993 12.5483C4.11581 13.2913 2.72736 13.6806 1.26982 13.6806C0.965688 13.6806 0.659818 13.6631 0.360464 13.6285C0.211755 13.6112 0.0695618 13.697 0.0189168 13.8352C-0.0317281 13.9734 0.0219491 14.1276 0.148465 14.2068C2.02091 15.3799 4.186 16 6.40954 16C10.7808 16 13.5153 13.9859 15.0394 12.2962C16.9401 10.1893 18.0301 7.40061 18.0301 4.64519C18.0301 4.53007 18.0283 4.41383 18.0247 4.29796C18.7746 3.74592 19.4202 3.07782 19.9456 2.30992C20.0254 2.1933 20.0167 2.03916 19.9243 1.93181C19.832 1.82439 19.6781 1.78965 19.5467 1.84651Z" fill="currentColor"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
3
public/icons/social-vk.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.2129 13.7512C19.8763 14.4052 20.5765 15.0206 21.1715 15.7406C21.4344 16.0605 21.6832 16.3907 21.8736 16.762C22.1433 17.2898 21.899 17.8707 21.4303 17.9022L18.5166 17.9009C17.7652 17.9638 17.1657 17.6584 16.6616 17.1395C16.2582 16.7246 15.8846 16.2831 15.4967 15.8542C15.3377 15.6789 15.1713 15.5139 14.9724 15.3835C14.5747 15.1228 14.2294 15.2026 14.0021 15.6215C13.7706 16.0476 13.7181 16.5193 13.6954 16.9942C13.6642 17.687 13.4568 17.8691 12.7676 17.9008C11.2947 17.9709 9.89691 17.7459 8.59838 16.9957C7.45355 16.3343 6.56579 15.4006 5.79308 14.3435C4.28861 12.2852 3.13649 10.0234 2.10101 7.6983C1.86793 7.17444 2.03838 6.89324 2.6108 6.88329C3.56132 6.86464 4.5117 6.86597 5.46334 6.88196C5.84966 6.88767 6.10541 7.11141 6.25458 7.47993C6.76884 8.75675 7.39809 9.97154 8.18794 11.0975C8.39829 11.3973 8.61277 11.6971 8.9182 11.9081C9.25609 12.1417 9.51335 12.0643 9.6723 11.6842C9.77317 11.4432 9.81733 11.1837 9.84007 10.9256C9.91537 10.0376 9.92529 9.15109 9.79321 8.26621C9.71213 7.71396 9.40407 7.35645 8.85833 7.25194C8.57985 7.19866 8.62131 7.09402 8.75615 6.93352C8.99035 6.65669 9.21061 6.48438 9.6497 6.48438H12.9426C13.461 6.58769 13.5761 6.82284 13.6471 7.34955L13.6499 11.0429C13.6442 11.2468 13.7508 11.8519 14.1145 11.9869C14.4056 12.0829 14.5975 11.8478 14.7721 11.6614C15.5605 10.8165 16.1232 9.81793 16.6259 8.78396C16.849 8.32931 17.0408 7.85714 17.2267 7.38538C17.3644 7.0353 17.5806 6.86305 17.9711 6.87068L21.1403 6.87353C21.2343 6.87353 21.3294 6.87493 21.4204 6.89065C21.9544 6.98255 22.1007 7.21452 21.9358 7.74109C21.6759 8.56725 21.1703 9.25572 20.6759 9.94738C20.1473 10.6858 19.5821 11.399 19.058 12.1418C18.5764 12.8201 18.6147 13.162 19.2129 13.7512Z" fill="#141414"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
|
@ -1,4 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.1512 4.42386L4.42326 17.1518L6.84763 19.5761L19.5756 6.84822L17.1512 4.42386Z" fill="#393840"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.5755 17.1518L6.84763 4.42386L4.42326 6.84822L17.1512 19.5761L19.5755 17.1518Z" fill="#393840"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0001 10.2321L18.1873 4.04492L19.9551 5.81269L13.7679 11.9999L19.9551 18.1871L18.1873 19.9548L12.0001 13.7676L5.81293 19.9548L4.04517 18.1871L10.2324 11.9999L4.04517 5.81269L5.81293 4.04492L12.0001 10.2321Z" fill="#141414"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 380 B |
|
@ -1,3 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 6.73787L19.2621 4L9.78964 13.4725L5.73787 9.42071L3 12.1586L9.78964 18.9482L22 6.73787Z" fill="#393840"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 6.73787L19.2621 4L9.78964 13.4725L5.73787 9.42071L3 12.1586L9.78964 18.9482L22 6.73787Z" fill="#141414"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 264 B |
8
public/icons/user-image-black.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<path d="M2.70423 20.2826H18.9296C19.6763 20.2826 20.2817 19.6773 20.2817 18.9305V15.9658C20.2817 15.6139 20.1445 15.2759 19.8992 15.0235L16.5187 11.5449C15.9878 10.9986 15.1106 10.9985 14.5796 11.5447L13.0829 13.084C12.5107 13.6726 11.5502 13.6196 11.0462 12.9716L8.31344 9.45821C7.77885 8.77092 6.74349 8.76071 6.19546 9.43733L1.65353 15.0449C1.45852 15.2857 1.35211 15.5861 1.35211 15.896V18.9305C1.35211 19.6773 1.95747 20.2826 2.70423 20.2826Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5775 2.70423H2.70423V17.5775H17.5775V2.70423ZM0 0V20.2817H20.2817V0H0Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 5.40845H21.9718V21.9718H5.40845V24H24V5.40845Z" fill="currentColor"/>
|
||||
<path d="M14.8732 6.08451C14.8732 7.20463 13.9652 8.11268 12.8451 8.11268C11.7249 8.11268 10.8169 7.20463 10.8169 6.08451C10.8169 4.96438 11.7249 4.05634 12.8451 4.05634C13.9652 4.05634 14.8732 4.96438 14.8732 6.08451Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
8
public/icons/user-image-gray.svg
Normal file
|
@ -0,0 +1,8 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Icon">
|
||||
<path d="M2.70423 20.2826H18.9296C19.6763 20.2826 20.2817 19.6773 20.2817 18.9305V15.9658C20.2817 15.6139 20.1445 15.2759 19.8992 15.0235L16.5187 11.5449C15.9878 10.9986 15.1106 10.9985 14.5796 11.5447L13.0829 13.084C12.5107 13.6726 11.5502 13.6196 11.0462 12.9716L8.31344 9.45821C7.77885 8.77092 6.74349 8.76071 6.19546 9.43733L1.65353 15.0449C1.45852 15.2857 1.35211 15.5861 1.35211 15.896V18.9305C1.35211 19.6773 1.95747 20.2826 2.70423 20.2826Z" fill="#9FA1A7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.5775 2.70423H2.70423V17.5775H17.5775V2.70423ZM0 0V20.2817H20.2817V0H0Z" fill="#9FA1A7"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 5.40845H21.9718V21.9718H5.40845V24H24V5.40845Z" fill="#9FA1A7"/>
|
||||
<path d="M14.8732 6.08451C14.8732 7.20463 13.9652 8.11268 12.8451 8.11268C11.7249 8.11268 10.8169 7.20463 10.8169 6.08451C10.8169 4.96438 11.7249 4.05634 12.8451 4.05634C13.9652 4.05634 14.8732 4.96438 14.8732 6.08451Z" fill="#9FA1A7"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"...subscribing": "...subscribing",
|
||||
"A guide to horizontal editorial: how an open journal works": "A guide to horizontal editorial: how an open journal works",
|
||||
"About": "About",
|
||||
"About the project": "About the project",
|
||||
"Add": "Add",
|
||||
"Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title": "Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title",
|
||||
"Add a link or click plus to embed media": "Add a link or click plus to embed media",
|
||||
"Add an embed widget": "Add an embed widget",
|
||||
|
@ -18,12 +19,13 @@
|
|||
"Add signature": "Add signature",
|
||||
"Add subtitle": "Add subtitle",
|
||||
"Add url": "Add url",
|
||||
"Address on Discourse": "Address on Discourse",
|
||||
"Address on Discours": "Address on Discours",
|
||||
"Album name": "Название aльбома",
|
||||
"Alignment center": "Alignment center",
|
||||
"Alignment left": "Alignment left",
|
||||
"Alignment right": "Alignment right",
|
||||
"All": "All",
|
||||
"All articles": "All articles",
|
||||
"All authors": "All authors",
|
||||
"All posts": "All posts",
|
||||
"All topics": "All topics",
|
||||
|
@ -41,6 +43,7 @@
|
|||
"Back": "Back",
|
||||
"Back to editor": "Back to editor",
|
||||
"Back to main page": "Back to main page",
|
||||
"Be the first to rate": "Be the first to rate",
|
||||
"Become an author": "Become an author",
|
||||
"Bold": "Bold",
|
||||
"Bookmarked": "Saved",
|
||||
|
@ -58,22 +61,34 @@
|
|||
"By title": "By title",
|
||||
"By updates": "By updates",
|
||||
"By views": "By views",
|
||||
"Can make any changes, accept or reject suggestions, and share access with others": "Can make any changes, accept or reject suggestions, and share access with others",
|
||||
"Can offer edits and comments, but cannot edit the post or share access with others": "Can offer edits and comments, but cannot edit the post or share access with others",
|
||||
"Can write and edit text directly, and accept or reject suggestions from others": "Can write and edit text directly, and accept or reject suggestions from others",
|
||||
"Cancel": "Cancel",
|
||||
"Cancel changes": "Cancel changes",
|
||||
"Change password": "Change password",
|
||||
"Characters": "Знаков",
|
||||
"Chat Title": "Chat Title",
|
||||
"Choose a post type": "Choose a post type",
|
||||
"Choose a title image for the article. You can immediately see how the publication card will look like.": "Choose a title image for the article. You can immediately see how the publication card will look like.",
|
||||
"Choose who you want to write to": "Choose who you want to write to",
|
||||
"Close": "Close",
|
||||
"Co-author": "Co-author",
|
||||
"Collaborate": "Help Edit",
|
||||
"Collaborators": "Collaborators",
|
||||
"Collections": "Collections",
|
||||
"Come up with a subtitle for your story": "Come up with a subtitle for your story",
|
||||
"Come up with a title for your story": "Come up with a title for your story",
|
||||
"Coming soon": "Coming soon",
|
||||
"Comment successfully deleted": "Comment successfully deleted",
|
||||
"Commentator": "Commentator",
|
||||
"Comments": "Comments",
|
||||
"Communities": "Communities",
|
||||
"Community Discussion Rules": "Community Discussion Rules",
|
||||
"Community Principles": "Community Principles",
|
||||
"Community values and rules of engagement for the open editorial team": "Community values and rules of engagement for the open editorial team",
|
||||
"Confirm": "Confirm",
|
||||
"Contribute to free samizdat. Support Discours - an independent non-profit publication that works only for you. Become a pillar of the open newsroom": "Contribute to free samizdat. Support Discours - an independent non-profit publication that works only for you. Become a pillar of the open newsroom",
|
||||
"Cooperate": "Cooperate",
|
||||
"Copy": "Copy",
|
||||
"Copy link": "Copy link",
|
||||
|
@ -95,14 +110,18 @@
|
|||
"Decline": "Decline",
|
||||
"Delete": "Delete",
|
||||
"Delete cover": "Delete cover",
|
||||
"Delete userpic": "Delete userpic",
|
||||
"Description": "Description",
|
||||
"Discours": "Discours",
|
||||
"Discours Manifest": "Discours Manifest",
|
||||
"Discours Partners": "Discours Partners",
|
||||
"Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects.<br/><em>We are convinced that one voice is good, but many is better. We create the most amazing stories together</em>",
|
||||
"Discours is created with our common effort": "Discours exists because of our common effort",
|
||||
"Discours – an open magazine about culture, science and society": "Discours – an open magazine about culture, science and society",
|
||||
"Discussing": "Discussing",
|
||||
"Discussion rules": "Discussion rules",
|
||||
"Discussion rules in social networks": "Discussion rules",
|
||||
"Discussions": "Discussions",
|
||||
"Do you really want to reset all changes?": "Do you really want to reset all changes?",
|
||||
"Dogma": "Dogma",
|
||||
"Draft successfully deleted": "Draft successfully deleted",
|
||||
"Drafts": "Drafts",
|
||||
|
@ -111,9 +130,11 @@
|
|||
"Edit": "Edit",
|
||||
"Edit profile": "Edit profile",
|
||||
"Editing": "Editing",
|
||||
"Editor": "Editor",
|
||||
"Email": "Mail",
|
||||
"Enter": "Enter",
|
||||
"Enter URL address": "Enter URL address",
|
||||
"Enter a new password": "Enter a new password",
|
||||
"Enter footnote text": "Enter footnote text",
|
||||
"Enter image description": "Enter image description",
|
||||
"Enter image title": "Enter image title",
|
||||
|
@ -126,6 +147,7 @@
|
|||
"FAQ": "Tips and suggestions",
|
||||
"Favorite": "Favorites",
|
||||
"Favorite topics": "Favorite topics",
|
||||
"Feed": "Feed",
|
||||
"Feed settings": "Feed settings",
|
||||
"Feedback": "Feedback",
|
||||
"Fill email": "Fill email",
|
||||
|
@ -150,25 +172,30 @@
|
|||
"Help": "Помощь",
|
||||
"Help to edit": "Help to edit",
|
||||
"Here you can customize your profile the way you want.": "Here you can customize your profile the way you want.",
|
||||
"Here you can manage all your Discourse subscriptions": "Here you can manage all your Discourse subscriptions",
|
||||
"Here you can manage all your Discours subscriptions": "Here you can manage all your Discours subscriptions",
|
||||
"Here you can upload your photo": "Here you can upload your photo",
|
||||
"Hide table of contents": "Hide table of contents",
|
||||
"Highlight": "Highlight",
|
||||
"Hooray! Welcome!": "Hooray! Welcome!",
|
||||
"Horizontal collaborative journalistic platform": "Horizontal collaborative journalism platform",
|
||||
"Hot topics": "Hot topics",
|
||||
"Hotkeys": "Горячие клавиши",
|
||||
"How Discours works": "How Discours works",
|
||||
"How can I help/skills": "How can I help/skills",
|
||||
"How it works": "How it works",
|
||||
"How to help": "How to help?",
|
||||
"How to write a good article": "Как написать хорошую статью",
|
||||
"How to write an article": "How to write an article",
|
||||
"Hundreds of people from different countries and cities share their knowledge and art on the Discours. Join us!": "Hundreds of people from different countries and cities share their knowledge and art on the Discours. Join us!",
|
||||
"I have an account": "I have an account!",
|
||||
"I have no account yet": "I don't have an account yet",
|
||||
"I know the password": "I know the password",
|
||||
"Image format not supported": "Image format not supported",
|
||||
"In bookmarks, you can save favorite discussions and materials that you want to return to": "In bookmarks, you can save favorite discussions and materials that you want to return to",
|
||||
"Inbox": "Inbox",
|
||||
"Incut": "Incut",
|
||||
"Independant magazine with an open horizontal cooperation about culture, science and society": "Independant magazine with an open horizontal cooperation about culture, science and society",
|
||||
"Independent media project about culture, science, art and society with horizontal editing": "Independent media project about culture, science, art and society with horizontal editing",
|
||||
"Insert footnote": "Insert footnote",
|
||||
"Insert video link": "Insert video link",
|
||||
"Interview": "Interview",
|
||||
|
@ -177,6 +204,7 @@
|
|||
"Invalid image URL": "Invalid image URL",
|
||||
"Invalid url format": "Invalid url format",
|
||||
"Invite co-authors": "Invite co-authors",
|
||||
"Invite collaborators": "Invite collaborators",
|
||||
"Invite to collab": "Invite to Collab",
|
||||
"It does not look like url": "It doesn't look like a link",
|
||||
"Italic": "Italic",
|
||||
|
@ -190,16 +218,19 @@
|
|||
"Last rev.": "Посл. изм.",
|
||||
"Let's log in": "Let's log in",
|
||||
"Link copied": "Link copied",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Link sent, check your email": "Link sent, check your email",
|
||||
"List of authors of the open editorial community": "List of authors of the open editorial community",
|
||||
"Lists": "Lists",
|
||||
"Literature": "Literature",
|
||||
"Load more": "Show more",
|
||||
"Loading": "Loading",
|
||||
"Logout": "Logout",
|
||||
"Looks like you forgot to upload the video": "Looks like you forgot to upload the video",
|
||||
"Manifest": "Manifest",
|
||||
"Manifest of samizdat: principles and mission of an open magazine with a horizontal editorial board": "Manifest of samizdat: principles and mission of an open magazine with a horizontal editorial board",
|
||||
"Manifesto": "Manifesto",
|
||||
"Many files, choose only one": "Many files, choose only one",
|
||||
"Mark as read": "Mark as read",
|
||||
"Material card": "Material card",
|
||||
"Message": "Message",
|
||||
"More": "More",
|
||||
|
@ -227,10 +258,12 @@
|
|||
"NotificationNewReplyText2": "from",
|
||||
"NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}",
|
||||
"Notifications": "Notifications",
|
||||
"Now you can enter a new password, it must contain at least 8 characters and not be the same as the previous password": "Now you can enter a new password, it must contain at least 8 characters and not be the same as the previous password",
|
||||
"Or paste a link to an image": "Or paste a link to an image",
|
||||
"Ordered list": "Ordered list",
|
||||
"Our regular contributor": "Our regular contributor",
|
||||
"Paragraphs": "Абзацев",
|
||||
"Participate in the Discours: share information, join the editorial team": "Участвуйте в Дискурсе: делитесь информацией, присоединяйтесь к редакции",
|
||||
"Participating": "Participating",
|
||||
"Participation": "Participation",
|
||||
"Partners": "Partners",
|
||||
|
@ -239,6 +272,7 @@
|
|||
"Password should be at least 8 characters": "Password should be at least 8 characters",
|
||||
"Password should contain at least one number": "Password should contain at least one number",
|
||||
"Password should contain at least one special character: !@#$%^&*": "Password should contain at least one special character: !@#$%^&*",
|
||||
"Password updated!": "Password updated!",
|
||||
"Passwords are not equal": "Passwords are not equal",
|
||||
"Paste Embed code": "Paste Embed code",
|
||||
"Personal": "Personal",
|
||||
|
@ -256,12 +290,14 @@
|
|||
"Popular": "Popular",
|
||||
"Popular authors": "Popular authors",
|
||||
"Principles": "Community principles",
|
||||
"Professional principles that the open editorial team follows in its work": "Professional principles that the open editorial team follows in its work",
|
||||
"Profile": "Profile",
|
||||
"Profile settings": "Profile settings",
|
||||
"Publications": "Publications",
|
||||
"PublicationsWithCount": "{count, plural, =0 {no publications} one {{count} publication} other {{count} publications}}",
|
||||
"Publish Album": "Publish Album",
|
||||
"Publish Settings": "Publish Settings",
|
||||
"Published": "Published",
|
||||
"Punchline": "Punchline",
|
||||
"Quit": "Quit",
|
||||
"Quote": "Quote",
|
||||
|
@ -276,6 +312,7 @@
|
|||
"Required": "Required",
|
||||
"Resend code": "Send confirmation",
|
||||
"Restore password": "Restore password",
|
||||
"Rules of the journal Discours": "Rules of the journal Discours",
|
||||
"Save draft": "Save draft",
|
||||
"Save settings": "Save settings",
|
||||
"Saving...": "Saving...",
|
||||
|
@ -286,11 +323,13 @@
|
|||
"Sections": "Sections",
|
||||
"Security": "Security",
|
||||
"Select": "Select",
|
||||
"Self-publishing exists thanks to the help of wonderful people from all over the world. Thank you!": "Samizdat exists thanks to the help of wonderful people from all over the world. Thank you!",
|
||||
"Send": "Send",
|
||||
"Send link again": "Send link again",
|
||||
"Settings": "Settings",
|
||||
"Site search": "Site search",
|
||||
"Share": "Share",
|
||||
"Share publication": "Share publication",
|
||||
"Show": "Show",
|
||||
"Show lyrics": "Show lyrics",
|
||||
"Show more": "Show more",
|
||||
|
@ -302,6 +341,7 @@
|
|||
"Something went wrong, please try again": "Something went wrong, please try again",
|
||||
"Song lyrics": "Song lyrics...",
|
||||
"Song title": "Song title",
|
||||
"Soon": "Скоро",
|
||||
"Sorry, this address is already taken, please choose another one.": "Sorry, this address is already taken, please choose another one",
|
||||
"Special Projects": "Special Projects",
|
||||
"Special projects": "Special projects",
|
||||
|
@ -321,21 +361,31 @@
|
|||
"Success": "Success",
|
||||
"Successfully authorized": "Authorization successful",
|
||||
"Suggest an idea": "Suggest an idea",
|
||||
"Support Discours": "Support Discours",
|
||||
"Support the project": "Support the project",
|
||||
"Support us": "Help the magazine",
|
||||
"Support us": "Support us",
|
||||
"Terms of use": "Site rules",
|
||||
"Text checking": "Text checking",
|
||||
"Thank you": "Thank you",
|
||||
"Thank you!": "Thank you!",
|
||||
"The address is already taken": "The address is already taken",
|
||||
"The most interesting publications on the topic": "The most interesting publications on the topic {topicName}",
|
||||
"Thematic table of contents of the magazine. Here you can find all the topics that community authors have written about.": "Thematic table of contents of the magazine. Here you can find all the topics that community authors have written about.",
|
||||
"Thematic table of contents of the magazine. Here you can find all the topics that the community authors wrote about": "Thematic table of contents of the magazine. Here you can find all the topics that the community authors wrote about",
|
||||
"Themes and plots": "Themes and plots",
|
||||
"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 yet been rated": "This comment has not yet been rated",
|
||||
"This email is already taken. If it's you": "This email is already taken. If it's you",
|
||||
"This functionality is currently not available, we would like to work on this issue. Use the download link.": "This functionality is currently not available, we would like to work on this issue. Use the download link.",
|
||||
"This month": "This month",
|
||||
"This post has not been rated yet": "This post has not been rated yet",
|
||||
"This way we ll realize that you re a real person and ll take your vote into account. And you ll see how others voted": "This way we ll realize that you re a real person and ll take your vote into account. And you ll see how others voted",
|
||||
"This way you ll be able to subscribe to authors, interesting topics and customize your feed": "This way you ll be able to subscribe to authors, interesting topics and customize your feed",
|
||||
"To find publications, art, comments, authors and topics of interest to you, just start typing your query": "To find publications, art, comments, authors and topics of interest to you, just start typing your query",
|
||||
"This week": "This week",
|
||||
"This year": "This year",
|
||||
"To leave a comment please": "To leave a comment please",
|
||||
"To write a comment, you must": "To write a comment, you must",
|
||||
"Top authors": "Authors rating",
|
||||
|
@ -355,6 +405,7 @@
|
|||
"Unnamed draft": "Unnamed draft",
|
||||
"Upload": "Upload",
|
||||
"Upload error": "Upload error",
|
||||
"Upload userpic": "Upload userpic",
|
||||
"Upload video": "Upload video",
|
||||
"Uploading image": "Uploading image",
|
||||
"Username": "Username",
|
||||
|
@ -364,6 +415,7 @@
|
|||
"Video format not supported": "Video format not supported",
|
||||
"Views": "Views",
|
||||
"We couldn't find anything for your request": "We couldn’t find anything for your request",
|
||||
"We are working on collaborative editing of articles and in the near future you will have an amazing opportunity - to create together with your colleagues": "We are working on collaborative editing of articles and in the near future you will have an amazing opportunity - to create together with your colleagues",
|
||||
"We can't find you, check email or": "We can't find you, check email or",
|
||||
"We know you, please try to login": "This email address is already registered, please try to login",
|
||||
"We've sent you a message with a link to enter our website.": "We've sent you an email with a link to your email. Follow the link in the email to enter our website.",
|
||||
|
@ -375,8 +427,9 @@
|
|||
"Welcome to Discours to subscribe to new publications": "Welcome to Discours to subscribe to new publications",
|
||||
"Welcome to Discours to vote": "Welcome to Discours to vote",
|
||||
"Where": "From",
|
||||
"Why you can earn a hole in your karma and how to receive rays of gratitude for your contribution to discussions in samizdat communities": "Why you can earn a hole in your karma and how to receive rays of gratitude for your contribution to discussions in samizdat communities",
|
||||
"Words": "Слов",
|
||||
"Work with us": "Cooperate with Discourse",
|
||||
"Work with us": "Cooperate with Discours",
|
||||
"Write a comment...": "Write a comment...",
|
||||
"Write a short introduction": "Write a short introduction",
|
||||
"Write about the topic": "Write about the topic",
|
||||
|
@ -385,7 +438,9 @@
|
|||
"Write good articles, comment\nand it won't be so empty here": "Write good articles, comment\nand it won't be so empty here",
|
||||
"Write message": "Write a message",
|
||||
"Write to us": "Write to us",
|
||||
"Write your colleagues name or email": "Write your colleague's name or email",
|
||||
"You can download multiple tracks at once in .mp3, .wav or .flac formats": "You can download multiple tracks at once in .mp3, .wav or .flac formats",
|
||||
"You can now login using your new password": "Теперь вы можете входить с помощью нового пароля",
|
||||
"You were successfully authorized": "You were successfully authorized",
|
||||
"You ll be able to participate in discussions, rate others' comments and learn about new responses": "You ll be able to participate in discussions, rate others' comments and learn about new responses",
|
||||
"You've confirmed email": "You've confirmed email",
|
||||
|
@ -407,7 +462,8 @@
|
|||
"community": "community",
|
||||
"contents": "contents",
|
||||
"delimiter": "delimiter",
|
||||
"discussion": "discourse",
|
||||
"discussion": "Discours",
|
||||
"dogma keywords": "Discours.io, dogma, editorial principles, code of ethics, journalism, community",
|
||||
"drafts": "drafts",
|
||||
"earlier": "earlier",
|
||||
"email not confirmed": "email not confirmed",
|
||||
|
@ -423,6 +479,7 @@
|
|||
"italic": "italic",
|
||||
"journal": "journal",
|
||||
"jpg, .png, max. 10 mb.": "jpg, .png, макс. 10 мб.",
|
||||
"keywords": "Discours.io, Discours magazine, Discours, culture, science, art, society, independent journalism, literature, music, cinema, video, photography",
|
||||
"literature": "literature",
|
||||
"marker list": "marker list",
|
||||
"min. 1400×1400 pix": "мин. 1400×1400 пикс.",
|
||||
|
@ -432,6 +489,7 @@
|
|||
"or sign in with social networks": "or sign in with social networks",
|
||||
"personal data usage and email notifications": "to process personal data and receive email notifications",
|
||||
"post": "post",
|
||||
"principles keywords": "Discours.io, communities, values, editorial rules, polyphony, creation",
|
||||
"register": "register",
|
||||
"repeat": "repeat",
|
||||
"shout": "post",
|
||||
|
@ -441,11 +499,14 @@
|
|||
"subscriber": "subscriber",
|
||||
"subscriber_rp": "subscriber",
|
||||
"subscribers": "subscribers",
|
||||
"subscribing...": "subscribing...",
|
||||
"subscription": "subscription",
|
||||
"subscription_rp": "subscription",
|
||||
"subscriptions": "subscriptions",
|
||||
"terms of use": "terms of use",
|
||||
"terms of use keywords": "Discours.io, site rules, terms of use",
|
||||
"today": "today",
|
||||
"topicKeywords": "{topic}, Discours.io, articles, journalism, research",
|
||||
"topics": "topics",
|
||||
"user already exist": "user already exists",
|
||||
"video": "video",
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"...subscribing": "...подписываем",
|
||||
"A guide to horizontal editorial: how an open journal works": "Гид по горизонтальной редакции: как работает открытый журнал",
|
||||
"A short introduction to keep the reader interested": "Добавьте вступление, чтобы заинтересовать читателя",
|
||||
"About": "О себе",
|
||||
"About the project": "О проекте",
|
||||
"Add": "Добавить",
|
||||
"Add a few topics so that the reader knows what your content is about and can find it on pages of topics that interest them. Topics can be swapped, the first topic becomes the title": "Добавьте несколько тем, чтобы читатель знал, о чем ваш материал, и мог найти его на страницах интересных ему тем. Темы можно менять местами, первая тема становится заглавной",
|
||||
"Add a link or click plus to embed media": "Добавьте ссылку или нажмите плюс для вставки медиа",
|
||||
"Add an embed widget": "Добавить embed-виджет",
|
||||
|
@ -20,12 +21,13 @@
|
|||
"Add subtitle": "Добавить подзаголовок",
|
||||
"Add to bookmarks": "Добавить в закладки",
|
||||
"Add url": "Добавить ссылку",
|
||||
"Address on Discourse": "Адрес на Дискурсе",
|
||||
"Address on Discours": "Адрес на Дискурсе",
|
||||
"Album name": "Название альбома",
|
||||
"Alignment center": "По центру",
|
||||
"Alignment left": "По левому краю",
|
||||
"Alignment right": "По правому краю",
|
||||
"All": "Все",
|
||||
"All articles": "Все материалы",
|
||||
"All authors": "Все авторы",
|
||||
"All posts": "Все публикации",
|
||||
"All topics": "Все темы",
|
||||
|
@ -44,6 +46,7 @@
|
|||
"Back": "Назад",
|
||||
"Back to editor": "Вернуться в редактор",
|
||||
"Back to main page": "Вернуться на главную",
|
||||
"Be the first to rate": "Оцените первым",
|
||||
"Become an author": "Стать автором",
|
||||
"Bold": "Жирный",
|
||||
"Bookmarked": "Сохранено",
|
||||
|
@ -61,22 +64,35 @@
|
|||
"By title": "По названию",
|
||||
"By updates": "По обновлениям",
|
||||
"By views": "По просмотрам",
|
||||
"Can make any changes, accept or reject suggestions, and share access with others": "Может вносить любые изменения, принимать и отклонять предложения, а также делиться доступом с другими",
|
||||
"Can offer edits and comments, but cannot edit the post or share access with others": "Может предлагать правки и комментарии, но не может изменять пост и делиться доступом с другими",
|
||||
"Can write and edit text directly, and accept or reject suggestions from others": "Может писать и редактировать текст напрямую, а также принимать или отклонять предложения других",
|
||||
"Cancel": "Отмена",
|
||||
"Cancel changes": "Отменить изменения",
|
||||
"Change password": "Сменить пароль",
|
||||
"Characters": "Знаков",
|
||||
"Chat Title": "Тема дискурса",
|
||||
"Choose a post type": "Выберите тип публикации",
|
||||
"Choose a title image for the article. You can immediately see how the publication card will look like.": "Выберите заглавное изображение для статьи. Тут же сразу можно увидеть как будет выглядеть карточка публикации.",
|
||||
"Choose who you want to write to": "Выберите кому хотите написать",
|
||||
"Close": "Закрыть",
|
||||
"Co-author": "Соавтор",
|
||||
"Collaborate": "Помочь редактировать",
|
||||
"Collaborators": "Соавторы",
|
||||
"Collections": "Коллекции",
|
||||
"Come up with a subtitle for your story": "Придумайте подзаголовок вашей истории",
|
||||
"Come up with a title for your story": "Придумайте заголовок вашей истории",
|
||||
"Coming soon": "Уже скоро",
|
||||
"Comment": "Комментировать",
|
||||
"Comment successfully deleted": "Комментарий успешно удален",
|
||||
"Commentator": "Комментатор",
|
||||
"Comments": "Комментарии",
|
||||
"Communities": "Сообщества",
|
||||
"Community Discussion Rules": "Правила дискуссий в сообществе",
|
||||
"Community Principles": "Принципы сообщества",
|
||||
"Community values and rules of engagement for the open editorial team": "Ценности сообщества и правила взаимодействия открытой редакции",
|
||||
"Confirm": "Подтвердить",
|
||||
"Contribute to free samizdat. Support Discours - an independent non-profit publication that works only for you. Become a pillar of the open newsroom": "Внесите вклад в свободный самиздат. Поддержите Дискурс — независимое некоммерческое издание, которое работает только для вас. Станьте опорой открытой редакции",
|
||||
"Cooperate": "Соучаствовать",
|
||||
"Copy": "Скопировать",
|
||||
"Copy link": "Скопировать ссылку",
|
||||
|
@ -98,14 +114,19 @@
|
|||
"Decline": "Отмена",
|
||||
"Delete": "Удалить",
|
||||
"Delete cover": "Удалить обложку",
|
||||
"Delete userpic": "Удалить аватар",
|
||||
"Description": "Описание",
|
||||
"Discours": "Дискурс",
|
||||
"Discours Manifest": "Манифест Дискурса",
|
||||
"Discours Partners": "Партнеры Дискурса",
|
||||
"Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects": "Дискурс — это интеллектуальная среда, веб-пространство и инструменты, которые позволяют авторам сотрудничать с читателями и объединяться для совместного создания публикаций и медиапроектов.<br/>Мы убеждены, один голос хорошо, а много — лучше. Самые потрясающиe истории мы создаём вместе.",
|
||||
"Discours is created with our common effort": "Дискурс существует благодаря нашему общему вкладу",
|
||||
"Discours – an open magazine about culture, science and society": "Дискурс – открытый журнал о культуре, науке и обществе",
|
||||
"Discours_theme": "Тема дискурса",
|
||||
"Discussing": "Обсуждаемое",
|
||||
"Discussion rules": "Правила дискуссий",
|
||||
"Discussion rules in social networks": "Правила сообществ самиздата в соцсетях",
|
||||
"Discussions": "Дискуссии",
|
||||
"Do you really want to reset all changes?": "Вы действительно хотите сбросить все изменения?",
|
||||
"Dogma": "Догма",
|
||||
"Draft successfully deleted": "Черновик успешно удален",
|
||||
"Drafts": "Черновики",
|
||||
|
@ -115,9 +136,11 @@
|
|||
"Edit profile": "Редактировать профиль",
|
||||
"Edited": "Отредактирован",
|
||||
"Editing": "Редактирование",
|
||||
"Editor": "Редактор",
|
||||
"Email": "Почта",
|
||||
"Enter": "Войти",
|
||||
"Enter URL address": "Введите адрес ссылки",
|
||||
"Enter a new password": "Введите новый пароль",
|
||||
"Enter footnote text": "Введите текст сноски",
|
||||
"Enter image description": "Введите описание изображения",
|
||||
"Enter image title": "Введите название изображения",
|
||||
|
@ -130,6 +153,7 @@
|
|||
"FAQ": "Советы и предложения",
|
||||
"Favorite": "Избранное",
|
||||
"Favorite topics": "Избранные темы",
|
||||
"Feed": "Лента",
|
||||
"Feed settings": "Настройки ленты",
|
||||
"Feedback": "Обратная связь",
|
||||
"Fill email": "Введите почту",
|
||||
|
@ -157,25 +181,30 @@
|
|||
"Help": "Помощь",
|
||||
"Help to edit": "Помочь редактировать",
|
||||
"Here you can customize your profile the way you want.": "Здесь можно настроить свой профиль так, как вы хотите.",
|
||||
"Here you can manage all your Discourse subscriptions": "Здесь можно управлять всеми своими подписками на Дискурсе",
|
||||
"Here you can manage all your Discours subscriptions": "Здесь можно управлять всеми своими подписками на Дискурсе",
|
||||
"Here you can upload your photo": "Здесь вы можете загрузить свою фотографию",
|
||||
"Hide table of contents": "Скрыть главление",
|
||||
"Highlight": "Подсветка",
|
||||
"Hooray! Welcome!": "Ура! Добро пожаловать!",
|
||||
"Horizontal collaborative journalistic platform": "Открытая платформа<br/>для независимой журналистики",
|
||||
"Hot topics": "Горячие темы",
|
||||
"Hotkeys": "Горячие клавиши",
|
||||
"How Discours works": "Как устроен Дискурс",
|
||||
"How can I help/skills": "Чем могу помочь/навыки",
|
||||
"How it works": "Как это работает",
|
||||
"How to help": "Как помочь?",
|
||||
"How to write a good article": "Как написать хорошую статью",
|
||||
"How to write an article": "Как написать статью",
|
||||
"Hundreds of people from different countries and cities share their knowledge and art on the Discours. Join us!": "Сотни людей из разных стран и городов делятся своими знаниями и искусством на Дискурсе. Присоединяйтесь!",
|
||||
"I have an account": "У меня есть аккаунт!",
|
||||
"I have no account yet": "У меня еще нет аккаунта",
|
||||
"I know the password": "Я знаю пароль!",
|
||||
"Image format not supported": "Тип изображения не поддерживается",
|
||||
"In bookmarks, you can save favorite discussions and materials that you want to return to": "В закладках можно сохранять избранные дискуссии и материалы, к которым хочется вернуться",
|
||||
"Inbox": "Входящие",
|
||||
"Incut": "Подверстка",
|
||||
"Independant magazine with an open horizontal cooperation about culture, science and society": "Независимый журнал с открытой горизонтальной редакцией о культуре, науке и обществе",
|
||||
"Independent media project about culture, science, art and society with horizontal editing": "Независимый медиапроект о культуре, науке, искусстве и обществе с горизонтальной редакцией",
|
||||
"Insert footnote": "Вставить сноску",
|
||||
"Insert video link": "Вставить ссылку на видео",
|
||||
"Interview": "Интервью",
|
||||
|
@ -184,6 +213,7 @@
|
|||
"Invalid image URL": "Некорректная ссылка на изображение",
|
||||
"Invalid url format": "Неверный формат ссылки",
|
||||
"Invite co-authors": "Пригласить соавторов",
|
||||
"Invite collaborators": "Пригласить соавторов",
|
||||
"Invite experts": "Пригласить экспертов",
|
||||
"Invite to collab": "Пригласить к участию",
|
||||
"It does not look like url": "Это не похоже на ссылку",
|
||||
|
@ -199,16 +229,19 @@
|
|||
"Last rev.": "Посл. изм.",
|
||||
"Let's log in": "Давайте авторизуемся",
|
||||
"Link copied": "Ссылка скопирована",
|
||||
"Link copied to clipboard": "Ссылка скопирована в буфер обмена",
|
||||
"Link sent, check your email": "Ссылка отправлена, проверьте почту",
|
||||
"List of authors of the open editorial community": "Список авторов сообщества открытой редакции",
|
||||
"Lists": "Списки",
|
||||
"Literature": "Литература",
|
||||
"Load more": "Показать ещё",
|
||||
"Loading": "Загрузка",
|
||||
"Logout": "Выход",
|
||||
"Looks like you forgot to upload the video": "Похоже, что вы забыли загрузить видео",
|
||||
"Manifest": "Манифест",
|
||||
"Manifest of samizdat: principles and mission of an open magazine with a horizontal editorial board": "Манифест самиздата: принципы и миссия открытого журнала с горизонтальной редакцией",
|
||||
"Manifesto": "Манифест",
|
||||
"Many files, choose only one": "Много файлов, выберете один",
|
||||
"Mark as read": "Отметить прочитанным",
|
||||
"Material card": "Карточка материала",
|
||||
"Message": "Написать",
|
||||
"More": "Ещё",
|
||||
|
@ -237,10 +270,12 @@
|
|||
"NotificationNewReplyText2": "от",
|
||||
"NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
|
||||
"Notifications": "Уведомления",
|
||||
"Now you can enter a new password, it must contain at least 8 characters and not be the same as the previous password": "Теперь можете ввести новый пароль, он должен содержать минимум 8 символов и не совпадать с предыдущим паролем",
|
||||
"Or paste a link to an image": "Или вставьте ссылку на изображение",
|
||||
"Ordered list": "Нумерованный список",
|
||||
"Our regular contributor": "Наш постоянный автор",
|
||||
"Paragraphs": "Абзацев",
|
||||
"Participate in the Discours: share information, join the editorial team": "Participate in the Discours: share information, join the editorial team",
|
||||
"Participating": "Участвовать",
|
||||
"Participation": "Соучастие",
|
||||
"Partners": "Партнёры",
|
||||
|
@ -249,6 +284,7 @@
|
|||
"Password should be at least 8 characters": "Пароль должен быть не менее 8 символов",
|
||||
"Password should contain at least one number": "Пароль должен содержать хотя бы одну цифру",
|
||||
"Password should contain at least one special character: !@#$%^&*": "Пароль должен содержать хотя бы один спецсимвол: !@#$%^&*",
|
||||
"Password updated!": "Пароль обновлен!",
|
||||
"Passwords are not equal": "Пароли не совпадают",
|
||||
"Paste Embed code": "Вставьте embed код",
|
||||
"Personal": "Личные",
|
||||
|
@ -267,6 +303,7 @@
|
|||
"Popular authors": "Популярные авторы",
|
||||
"Preview": "Предпросмотр",
|
||||
"Principles": "Принципы сообщества",
|
||||
"Professional principles that the open editorial team follows in its work": "Профессиональные принципы, которым открытая редакция следует в работе",
|
||||
"Profile": "Профиль",
|
||||
"Profile settings": "Настройки профиля",
|
||||
"Profile successfully saved": "Профиль успешно сохранён",
|
||||
|
@ -276,6 +313,7 @@
|
|||
"Publish": "Опубликовать",
|
||||
"Publish Album": "Опубликовать альбом",
|
||||
"Publish Settings": "Настройки публикации",
|
||||
"Published": "Опубликованные",
|
||||
"Punchline": "Панчлайн",
|
||||
"Quit": "Выйти",
|
||||
"Quote": "Цитата",
|
||||
|
@ -291,6 +329,7 @@
|
|||
"Required": "Поле обязательно для заполнения",
|
||||
"Resend code": "Выслать подтверждение",
|
||||
"Restore password": "Восстановить пароль",
|
||||
"Rules of the journal Discours": "Правила журнала Дискурс",
|
||||
"Save": "Сохранить",
|
||||
"Save draft": "Сохранить черновик",
|
||||
"Save settings": "Сохранить настройки",
|
||||
|
@ -302,11 +341,13 @@
|
|||
"Sections": "Разделы",
|
||||
"Security": "Безопасность",
|
||||
"Select": "Выбрать",
|
||||
"Self-publishing exists thanks to the help of wonderful people from all over the world. Thank you!": "Самиздат существуют благодаря помощи замечательных людей со всего мира. Спасибо Вам!",
|
||||
"Send": "Отправить",
|
||||
"Send link again": "Прислать ссылку ещё раз",
|
||||
"Settings": "Настройки",
|
||||
"Site search": "Поиск по сайту",
|
||||
"Share": "Поделиться",
|
||||
"Share publication": "Поделиться публикацией",
|
||||
"Short opening": "Расскажите вашу историю...",
|
||||
"Show": "Показать",
|
||||
"Show lyrics": "Текст песни",
|
||||
|
@ -319,6 +360,7 @@
|
|||
"Something went wrong, please try again": "Что-то пошло не так, попробуйте еще раз",
|
||||
"Song lyrics": "Текст песни...",
|
||||
"Song title": "Название песни",
|
||||
"Soon": "Скоро",
|
||||
"Sorry, this address is already taken, please choose another one.": "Увы, этот адрес уже занят, выберите другой",
|
||||
"Special Projects": "Спецпроекты",
|
||||
"Special projects": "Спецпроекты",
|
||||
|
@ -339,21 +381,31 @@
|
|||
"Success": "Успешно",
|
||||
"Successfully authorized": "Авторизация успешна",
|
||||
"Suggest an idea": "Предложить идею",
|
||||
"Support Discours": "Поддержите Дискурс",
|
||||
"Support the project": "Поддержать проект",
|
||||
"Support us": "Помочь журналу",
|
||||
"Terms of use": "Правила сайта",
|
||||
"Text checking": "Проверка текста",
|
||||
"Thank you": "Благодарности",
|
||||
"Thank you!": "Спасибо Вам!",
|
||||
"The address is already taken": "Адрес уже занят",
|
||||
"The most interesting publications on the topic": "Самые интересные публикации по теме {topicName}",
|
||||
"Thematic table of contents of the magazine. Here you can find all the topics that community authors have written about.": "Тематическое оглавление журнала. Здесь можно найти все темы, о которых писали авторы сообщества.",
|
||||
"Thematic table of contents of the magazine. Here you can find all the topics that the community authors wrote about": "Тематическое оглавление журнала. Здесь можно найти все темы, о которых писали авторы сообщества",
|
||||
"Themes and plots": "Темы и сюжеты",
|
||||
"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 publishing settings. Are you sure you want to leave the page without saving?": "В настройках публикации есть несохраненные изменения. Уверены, что хотите покинуть страницу без сохранения?",
|
||||
"This comment has not yet been rated": "Этот комментарий еще пока никто не оценил",
|
||||
"This email is already taken. If it's you": "Такой email уже зарегистрирован. Если это вы",
|
||||
"This functionality is currently not available, we would like to work on this issue. Use the download link.": "В данный момент этот функционал не доступен, бы работаем над этой проблемой. Воспользуйтесь загрузкой по ссылке.",
|
||||
"This month": "За месяц",
|
||||
"This post has not been rated yet": "Эту публикацию еще пока никто не оценил",
|
||||
"This way we ll realize that you re a real person and ll take your vote into account. And you ll see how others voted": "Так мы поймем, что вы реальный человек, и учтем ваш голос. А вы увидите, как проголосовали другие",
|
||||
"This way you ll be able to subscribe to authors, interesting topics and customize your feed": "Так вы сможете подписаться на авторов, интересные темы и настроить свою ленту",
|
||||
"To find publications, art, comments, authors and topics of interest to you, just start typing your query": "Для поиска публикаций, искусства, комментариев, интересных вам авторов и тем, просто начните вводить ваш запрос",
|
||||
"This week": "За неделю",
|
||||
"This year": "За год",
|
||||
"To leave a comment please": "Чтобы оставить комментарий, необходимо",
|
||||
"To write a comment, you must": "Чтобы написать комментарий, необходимо",
|
||||
"Top authors": "Рейтинг авторов",
|
||||
|
@ -373,6 +425,7 @@
|
|||
"Unnamed draft": "Черновик без названия",
|
||||
"Upload": "Загрузить",
|
||||
"Upload error": "Ошибка загрузки",
|
||||
"Upload userpic": "Загрузить аватар",
|
||||
"Upload video": "Загрузить видео",
|
||||
"Uploading image": "Загружаем изображение",
|
||||
"Username": "Имя пользователя",
|
||||
|
@ -382,6 +435,7 @@
|
|||
"Video format not supported": "Тип видео не поддерживается",
|
||||
"Views": "Просмотры",
|
||||
"We couldn't find anything for your request": "Мы не смогли ничего найти по вашему запросу",
|
||||
"We are working on collaborative editing of articles and in the near future you will have an amazing opportunity - to create together with your colleagues": "Мы работаем над коллаборативным редактированием статей и в ближайшем времени у вас появиться удивительная возможность - творить вместе с коллегами",
|
||||
"We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или",
|
||||
"We know you, please try to login": "Такой адрес почты уже зарегистрирован, попробуйте залогиниться",
|
||||
"We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.",
|
||||
|
@ -394,6 +448,7 @@
|
|||
"Welcome to Discours to vote": "Войдите в Дискурс, чтобы голосовать",
|
||||
"Welcome!": "Добро пожаловать!",
|
||||
"Where": "Откуда",
|
||||
"Why you can earn a hole in your karma and how to receive rays of gratitude for your contribution to discussions in samizdat communities": "За что можно заслужить дырку в карме и как получить лучи благодарности за вклад в дискуссии в сообществах самиздата",
|
||||
"Words": "Слов",
|
||||
"Work with us": "Сотрудничать с Дискурсом",
|
||||
"Write a comment...": "Написать комментарий...",
|
||||
|
@ -404,7 +459,9 @@
|
|||
"Write good articles, comment\nand it won't be so empty here": "Пишите хорошие статьи, комментируйте,\nи здесь станет не так пусто",
|
||||
"Write message": "Написать сообщение",
|
||||
"Write to us": "Напишите нам",
|
||||
"Write your colleagues name or email": "Напишите имя или e-mail коллеги",
|
||||
"You can download multiple tracks at once in .mp3, .wav or .flac formats": "Можно загрузить сразу несколько треков в форматах .mp3, .wav или .flac",
|
||||
"You can now login using your new password": "Теперь вы можете входить с помощью нового пароля",
|
||||
"You was successfully authorized": "Вы были успешно авторизованы",
|
||||
"You ll be able to participate in discussions, rate others' comments and learn about new responses": "Вы сможете участвовать в обсуждениях, оценивать комментарии других и узнавать о новых ответах",
|
||||
"You've confirmed email": "Вы подтвердили почту",
|
||||
|
@ -429,8 +486,8 @@
|
|||
"create_chat": "Создать чат",
|
||||
"create_group": "Создать группу",
|
||||
"delimiter": "разделитель",
|
||||
"discourse_theme": "Тема дискурса",
|
||||
"discussion": "дискурс",
|
||||
"dogma keywords": "Discours.io, догма, редакционные принципы, этический кодекс, журналистика, сообщество",
|
||||
"drafts": "черновики",
|
||||
"earlier": "ранее",
|
||||
"email not confirmed": "email не подтвержден",
|
||||
|
@ -446,6 +503,7 @@
|
|||
"italic": "курсив",
|
||||
"journal": "журнал",
|
||||
"jpg, .png, max. 10 mb.": "jpg, .png, макс. 10 мб.",
|
||||
"keywords": "Discours.io, журнал Дискурс, Дискурс, культура, наука, искусство, общество, независимая журналистика, литература, музыка, кино, видео, фотографии",
|
||||
"literature": "литература",
|
||||
"marker list": "маркир. список",
|
||||
"min. 1400×1400 pix": "мин. 1400×1400 пикс.",
|
||||
|
@ -456,6 +514,7 @@
|
|||
"or sign in with social networks": "или войдите через соцсеть",
|
||||
"personal data usage and email notifications": "на обработку персональных данных и на получение почтовых уведомлений",
|
||||
"post": "пост",
|
||||
"principles keywords": "Discours.io, сообщества, ценности, правила редакции, многоголосие, созидание",
|
||||
"register": "зарегистрируйтесь",
|
||||
"repeat": "повторить",
|
||||
"shout": "пост",
|
||||
|
@ -468,8 +527,11 @@
|
|||
"subscriber": "подписчик",
|
||||
"subscriber_rp": "подписчика",
|
||||
"subscribers": "подписчиков",
|
||||
"subscribing...": "Подписка...",
|
||||
"terms of use": "правилами пользования сайтом",
|
||||
"terms of use keywords": "Discours.io, правила сайта, terms of use",
|
||||
"today": "сегодня",
|
||||
"topicKeywords": "{topic}, Discours.io, статьи, журналистика, исследования",
|
||||
"topics": "темы",
|
||||
"user already exist": "пользователь уже существует",
|
||||
"video": "видео",
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"theme_color": "#111111",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"name": "discours.io",
|
||||
"short_name": "discours.io",
|
||||
"description": "Дискурс - коллаборативная журналистика",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.png",
|
||||
"sizes": "200x200",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,2 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow: /
|
||||
Allow: /
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
// FIXME: breaks on vercel, research
|
||||
// import 'solid-devtools'
|
||||
import type { PageProps, RootSearchParams } from '../pages/types'
|
||||
|
||||
import { hideModal, MODALS, showModal } from '../stores/ui'
|
||||
import { Meta, MetaProvider } from '@solidjs/meta'
|
||||
import { Component, createEffect, createMemo } from 'solid-js'
|
||||
import { ROUTES, useRouter } from '../stores/router'
|
||||
import { Dynamic } from 'solid-js/web'
|
||||
|
||||
import type { PageProps, RootSearchParams } from '../pages/types'
|
||||
import { HomePage } from '../pages/index.page'
|
||||
import { AllTopicsPage } from '../pages/allTopics.page'
|
||||
import { TopicPage } from '../pages/topic.page'
|
||||
import { AllAuthorsPage } from '../pages/allAuthors.page'
|
||||
import { AuthorPage } from '../pages/author.page'
|
||||
import { FeedPage } from '../pages/feed.page'
|
||||
import { ArticlePage } from '../pages/article.page'
|
||||
import { SearchPage } from '../pages/search.page'
|
||||
import { FourOuFourPage } from '../pages/fourOuFour.page'
|
||||
import { ConfirmProvider } from '../context/confirm'
|
||||
import { EditorProvider } from '../context/editor'
|
||||
import { LocalizeProvider } from '../context/localize'
|
||||
import { MediaQueryProvider } from '../context/mediaQuery'
|
||||
import { NotificationsProvider } from '../context/notifications'
|
||||
import { SessionProvider } from '../context/session'
|
||||
import { SnackbarProvider } from '../context/snackbar'
|
||||
import { DiscussionRulesPage } from '../pages/about/discussionRules.page'
|
||||
import { DogmaPage } from '../pages/about/dogma.page'
|
||||
import { GuidePage } from '../pages/about/guide.page'
|
||||
|
@ -26,21 +21,26 @@ import { PrinciplesPage } from '../pages/about/principles.page'
|
|||
import { ProjectsPage } from '../pages/about/projects.page'
|
||||
import { TermsOfUsePage } from '../pages/about/termsOfUse.page'
|
||||
import { ThanksPage } from '../pages/about/thanks.page'
|
||||
import { CreatePage } from '../pages/create.page'
|
||||
import { EditPage } from '../pages/edit.page'
|
||||
import { AllAuthorsPage } from '../pages/allAuthors.page'
|
||||
import { AllTopicsPage } from '../pages/allTopics.page'
|
||||
import { ArticlePage } from '../pages/article.page'
|
||||
import { AuthorPage } from '../pages/author.page'
|
||||
import { ConnectPage } from '../pages/connect.page'
|
||||
import { InboxPage } from '../pages/inbox.page'
|
||||
import { ExpoPage } from '../pages/expo/expo.page'
|
||||
import { SessionProvider } from '../context/session'
|
||||
import { ProfileSettingsPage } from '../pages/profile/profileSettings.page'
|
||||
import { ProfileSecurityPage } from '../pages/profile/profileSecurity.page'
|
||||
import { ProfileSubscriptionsPage } from '../pages/profile/profileSubscriptions.page'
|
||||
import { CreatePage } from '../pages/create.page'
|
||||
import { DraftsPage } from '../pages/drafts.page'
|
||||
import { SnackbarProvider } from '../context/snackbar'
|
||||
import { LocalizeProvider } from '../context/localize'
|
||||
import { ConfirmProvider } from '../context/confirm'
|
||||
import { EditorProvider } from '../context/editor'
|
||||
import { NotificationsProvider } from '../context/notifications'
|
||||
import { EditPage } from '../pages/edit.page'
|
||||
import { ExpoPage } from '../pages/expo/expo.page'
|
||||
import { FeedPage } from '../pages/feed.page'
|
||||
import { FourOuFourPage } from '../pages/fourOuFour.page'
|
||||
import { InboxPage } from '../pages/inbox.page'
|
||||
import { HomePage } from '../pages/index.page'
|
||||
import { ProfileSecurityPage } from '../pages/profile/profileSecurity.page'
|
||||
import { ProfileSettingsPage } from '../pages/profile/profileSettings.page'
|
||||
import { ProfileSubscriptionsPage } from '../pages/profile/profileSubscriptions.page'
|
||||
import { SearchPage } from '../pages/search.page'
|
||||
import { TopicPage } from '../pages/topic.page'
|
||||
import { ROUTES, useRouter } from '../stores/router'
|
||||
import { hideModal, MODALS, showModal } from '../stores/ui'
|
||||
|
||||
// TODO: lazy load
|
||||
// const SomePage = lazy(() => import('./Pages/SomePage'))
|
||||
|
@ -51,7 +51,6 @@ const pagesMap: Record<keyof typeof ROUTES, Component<PageProps>> = {
|
|||
authorAbout: AuthorPage,
|
||||
inbox: InboxPage,
|
||||
expo: ExpoPage,
|
||||
expoLayout: ExpoPage,
|
||||
connect: ConnectPage,
|
||||
create: CreatePage,
|
||||
edit: EditPage,
|
||||
|
@ -82,11 +81,14 @@ const pagesMap: Record<keyof typeof ROUTES, Component<PageProps>> = {
|
|||
profileSettings: ProfileSettingsPage,
|
||||
profileSecurity: ProfileSecurityPage,
|
||||
profileSubscriptions: ProfileSubscriptionsPage,
|
||||
fourOuFour: FourOuFourPage
|
||||
fourOuFour: FourOuFourPage,
|
||||
}
|
||||
|
||||
export const App = (props: PageProps) => {
|
||||
type Props = PageProps & { is404: boolean }
|
||||
|
||||
export const App = (props: Props) => {
|
||||
const { page, searchParams } = useRouter<RootSearchParams>()
|
||||
let is404 = props.is404
|
||||
|
||||
createEffect(() => {
|
||||
if (!searchParams().modal) {
|
||||
|
@ -102,7 +104,8 @@ export const App = (props: PageProps) => {
|
|||
const pageComponent = createMemo(() => {
|
||||
const result = pagesMap[page()?.route || 'home']
|
||||
|
||||
if (!result || page()?.path === '/404') {
|
||||
if (is404 || !result || page()?.path === '/404') {
|
||||
is404 = false
|
||||
return FourOuFourPage
|
||||
}
|
||||
|
||||
|
@ -110,7 +113,10 @@ export const App = (props: PageProps) => {
|
|||
})
|
||||
|
||||
return (
|
||||
<MetaProvider>
|
||||
<Meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<LocalizeProvider>
|
||||
<MediaQueryProvider>
|
||||
<SnackbarProvider>
|
||||
<ConfirmProvider>
|
||||
<SessionProvider>
|
||||
|
@ -122,6 +128,8 @@ export const App = (props: PageProps) => {
|
|||
</SessionProvider>
|
||||
</ConfirmProvider>
|
||||
</SnackbarProvider>
|
||||
</MediaQueryProvider>
|
||||
</LocalizeProvider>
|
||||
</MetaProvider>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
h1 {
|
||||
@include font-size(4rem);
|
||||
|
||||
line-height: 1.1;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@include font-size(4rem);
|
||||
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
|
@ -14,30 +16,22 @@ img {
|
|||
}
|
||||
|
||||
.shoutHeader {
|
||||
margin-bottom: 2em;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
margin: 0 0 2em;
|
||||
}
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.shoutCover {
|
||||
background-size: cover;
|
||||
height: 0;
|
||||
padding-bottom: 56.2%;
|
||||
.articleContent {
|
||||
img:not([data-disable-lightbox='true']) {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
}
|
||||
|
||||
.shoutBody {
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.6;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
blockquote[data-type='punchline'] {
|
||||
clear: both;
|
||||
font-size: 2.6rem;
|
||||
font-weight: bold;
|
||||
line-height: 1.4;
|
||||
|
@ -61,6 +55,7 @@ img {
|
|||
ta-quotation {
|
||||
border: solid #000;
|
||||
border-width: 0 0 0 2px;
|
||||
clear: both;
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
line-height: 1.6;
|
||||
|
@ -70,7 +65,12 @@ img {
|
|||
&[data-float='left'],
|
||||
&[data-float='right'] {
|
||||
@include font-size(2.2rem);
|
||||
|
||||
line-height: 1.4;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
clear: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
|
@ -92,9 +92,11 @@ img {
|
|||
ta-sub,
|
||||
ta-selection-frame,
|
||||
ta-border-sub {
|
||||
background: #f1f2f3;
|
||||
display: block;
|
||||
@include font-size(1.4rem);
|
||||
|
||||
background: #f1f2f3;
|
||||
clear: both;
|
||||
display: block;
|
||||
margin: 3.2rem 0;
|
||||
padding: 3.2rem;
|
||||
|
||||
|
@ -108,6 +110,10 @@ img {
|
|||
}
|
||||
}
|
||||
|
||||
ta-sub {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
*[data-bg='black'] {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
|
@ -173,9 +179,10 @@ img {
|
|||
|
||||
:global(.img-align-left) {
|
||||
float: left;
|
||||
margin: 1em 8.3333% 1.5em 0;
|
||||
margin: 0 8.3333% 1.5em 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
:global(.width-30) {
|
||||
width: 30%;
|
||||
}
|
||||
|
@ -183,6 +190,7 @@ img {
|
|||
:global(.width-50) {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.img-align-left.width-50) {
|
||||
@include media-breakpoint-up(xl) {
|
||||
|
@ -191,13 +199,15 @@ img {
|
|||
}
|
||||
|
||||
:global(.img-align-right) {
|
||||
@include media-breakpoint-up(sm) {
|
||||
float: right;
|
||||
margin: 1em 0 1.5em 8.3333%;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.img-align-right.width-50) {
|
||||
@include media-breakpoint-up(xl) {
|
||||
margin-right: -16.6666%;
|
||||
margin-right: -8.3333%;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,22 +305,24 @@ img {
|
|||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding: 3rem 0 0;
|
||||
position: relative;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
@include media-breakpoint-down(lg) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.shoutStatsItem {
|
||||
@include font-size(1.5rem);
|
||||
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
margin: 0 6% 1em 0;
|
||||
margin: 0 2rem 1em 0;
|
||||
vertical-align: baseline;
|
||||
cursor: pointer;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
@include media-breakpoint-up(xl) {
|
||||
margin-right: 3.2rem;
|
||||
}
|
||||
|
||||
|
@ -354,6 +366,14 @@ img {
|
|||
}
|
||||
}
|
||||
|
||||
.shoutStatsItemBookmarks {
|
||||
margin-left: auto;
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shoutStatsItemInner {
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -377,32 +397,41 @@ img {
|
|||
|
||||
.shoutStatsItemAdditionalData {
|
||||
color: rgb(0 0 0 / 40%);
|
||||
cursor: default;
|
||||
font-weight: normal;
|
||||
justify-self: flex-end;
|
||||
margin-right: 0;
|
||||
margin-left: auto;
|
||||
white-space: nowrap;
|
||||
cursor: default;
|
||||
|
||||
.icon {
|
||||
opacity: 0.4;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
flex: 1 40%;
|
||||
@include media-breakpoint-down(lg) {
|
||||
flex: 1 100%;
|
||||
order: 9;
|
||||
|
||||
.shoutStatsItemAdditionalDataItem {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shoutStatsItemViews {
|
||||
color: rgb(0 0 0 / 40%);
|
||||
cursor: default;
|
||||
font-weight: normal;
|
||||
margin-left: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
color: rgb(0 0 0 / 0.4);
|
||||
@include media-breakpoint-down(lg) {
|
||||
bottom: 0;
|
||||
flex: 1 40%;
|
||||
justify-content: end;
|
||||
margin-right: 0;
|
||||
order: 10;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
.icon {
|
||||
display: none !important;
|
||||
|
@ -411,15 +440,20 @@ img {
|
|||
}
|
||||
|
||||
.shoutStatsItemLabel {
|
||||
font-weight: normal;
|
||||
margin-left: 0.3em;
|
||||
}
|
||||
|
||||
.commentsTextLabel {
|
||||
display: none;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: none;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.shoutStatsItemCount {
|
||||
@include media-breakpoint-down(sm) {
|
||||
@include media-breakpoint-down(lg) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +461,8 @@ img {
|
|||
.shoutStatsItemAdditionalDataItem {
|
||||
font-weight: normal;
|
||||
display: inline-block;
|
||||
margin-left: 2rem;
|
||||
|
||||
// margin-left: 2rem;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
cursor: default;
|
||||
|
@ -441,6 +476,7 @@ img {
|
|||
|
||||
.topicsList {
|
||||
@include font-size(1.2rem);
|
||||
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
letter-spacing: 0.08em;
|
||||
margin-top: 1.6rem;
|
||||
|
@ -480,12 +516,15 @@ img {
|
|||
}
|
||||
|
||||
.commentsHeaderWrapper {
|
||||
@include media-breakpoint-up(sm) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.commentsHeader {
|
||||
@include font-size(2.4rem);
|
||||
|
||||
margin-bottom: 1em;
|
||||
|
||||
.newReactions {
|
||||
|
@ -519,6 +558,7 @@ img {
|
|||
|
||||
button {
|
||||
@include font-size(1.5rem);
|
||||
|
||||
border-radius: 0.8rem;
|
||||
margin-right: 1.2rem;
|
||||
padding: 0.9rem 1.2rem;
|
||||
|
@ -600,13 +640,14 @@ a[data-toggle='tooltip'] {
|
|||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 4px 4px 0 4px;
|
||||
border-width: 4px 4px 0;
|
||||
border-color: var(--black-500) transparent transparent transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.lead {
|
||||
@include font-size(1.8rem);
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
b,
|
||||
|
@ -614,3 +655,19 @@ a[data-toggle='tooltip'] {
|
|||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.articlePopupOpener {
|
||||
.iconHover {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.iconHover {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { clsx } from 'clsx'
|
||||
import styles from './AudioHeader.module.scss'
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
import { createSignal, Show } from 'solid-js'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import { Topic } from '../../../graphql/types.gen'
|
||||
import { CardTopic } from '../../Feed/CardTopic'
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Image } from '../../_shared/Image'
|
||||
import { CardTopic } from '../../Feed/CardTopic'
|
||||
|
||||
import styles from './AudioHeader.module.scss'
|
||||
|
||||
type Props = {
|
||||
title: string
|
||||
|
@ -19,7 +21,7 @@ export const AudioHeader = (props: Props) => {
|
|||
return (
|
||||
<div class={clsx(styles.AudioHeader, { [styles.expandedImage]: expandedImage() })}>
|
||||
<div class={styles.cover}>
|
||||
<Image class={styles.image} src={props.cover} alt={props.title} width={200} />
|
||||
<Image class={styles.image} src={props.cover} alt={props.title} width={100} />
|
||||
<Show when={props.cover}>
|
||||
<button type="button" class={styles.expand} onClick={() => setExpandedImage(!expandedImage())}>
|
||||
<Icon name="expand-circle" />
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: #141414;
|
||||
|
@ -108,7 +107,7 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid #cccccc;
|
||||
border-bottom: 2px solid #ccc;
|
||||
}
|
||||
|
||||
.progressFilled {
|
||||
|
@ -126,7 +125,6 @@
|
|||
position: absolute;
|
||||
bottom: -10px;
|
||||
right: -8px;
|
||||
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
|
@ -140,7 +138,6 @@
|
|||
padding-top: 14px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
|
@ -157,7 +154,7 @@ $vendors-track: ('::-webkit-slider-runnable-track', '::-moz-range-track', '::-ms
|
|||
$vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thumb');
|
||||
|
||||
.volume {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: 19px;
|
||||
float: left;
|
||||
outline: none;
|
||||
|
@ -182,7 +179,7 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
@each $vendor in $vendors-thumb {
|
||||
&#{$vendor} {
|
||||
position: relative;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
box-sizing: content-box;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
@ -190,7 +187,7 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
border: 4px solid var(--default-color);
|
||||
background-color: var(--background-color);
|
||||
cursor: pointer;
|
||||
margin: -7px 0 0 0;
|
||||
margin: -7px 0 0;
|
||||
}
|
||||
&:active#{$vendor} {
|
||||
transform: scale(1.2);
|
||||
|
@ -201,6 +198,7 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
&::-moz-range-progress {
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
|
||||
&::-moz-focus-outer {
|
||||
border: 0;
|
||||
}
|
||||
|
@ -209,7 +207,6 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
.playlist {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
list-style-type: none;
|
||||
margin: 32px 0 16px;
|
||||
padding: 0;
|
||||
|
@ -222,7 +219,6 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
.playlistItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
min-height: 56px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
@ -319,6 +315,7 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
&:not([disabled]):hover {
|
||||
border-color: var(--background-color-invert);
|
||||
background: var(--background-color-invert);
|
||||
|
||||
img {
|
||||
filter: var(--icon-filter-hover);
|
||||
}
|
||||
|
@ -334,7 +331,7 @@ $vendors-thumb: ('::-webkit-slider-thumb', '::-moz-moz-range-thumb', '::-ms-thum
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding: 8px 0 24px 0;
|
||||
padding: 8px 0 24px;
|
||||
|
||||
.description,
|
||||
.lyrics {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { createEffect, createMemo, createSignal, on, onMount, Show } from 'solid-js'
|
||||
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
|
||||
import { PlayerHeader } from './PlayerHeader'
|
||||
import { PlayerPlaylist } from './PlayerPlaylist'
|
||||
|
||||
import styles from './AudioPlayer.module.scss'
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
|
||||
type Props = {
|
||||
media: MediaItem[]
|
||||
|
@ -35,8 +38,8 @@ export const AudioPlayer = (props: Props) => {
|
|||
() => {
|
||||
setCurrentTrackDuration(0)
|
||||
},
|
||||
{ defer: true }
|
||||
)
|
||||
{ defer: true },
|
||||
),
|
||||
)
|
||||
|
||||
const handlePlayMedia = async (trackIndex: number) => {
|
||||
|
@ -131,7 +134,7 @@ export const AudioPlayer = (props: Props) => {
|
|||
<div
|
||||
class={styles.progressFilled}
|
||||
style={{
|
||||
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`
|
||||
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { createSignal, Show } from 'solid-js'
|
||||
import { clsx } from 'clsx'
|
||||
import { createSignal, Show } from 'solid-js'
|
||||
|
||||
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
||||
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import styles from './AudioPlayer.module.scss'
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import styles from './AudioPlayer.module.scss'
|
||||
|
||||
type Props = {
|
||||
onPlayMedia: () => void
|
||||
|
@ -18,7 +18,7 @@ type Props = {
|
|||
|
||||
export const PlayerHeader = (props: Props) => {
|
||||
const volumeContainerRef: { current: HTMLDivElement } = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
|
||||
const [isVolumeBarOpened, setIsVolumeBarOpened] = createSignal(false)
|
||||
|
@ -30,7 +30,7 @@ export const PlayerHeader = (props: Props) => {
|
|||
useOutsideClickHandler({
|
||||
containerRef: volumeContainerRef,
|
||||
predicate: () => isVolumeBarOpened(),
|
||||
handler: () => toggleVolumeBar()
|
||||
handler: () => toggleVolumeBar(),
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -42,7 +42,7 @@ export const PlayerHeader = (props: Props) => {
|
|||
onClick={props.onPlayMedia}
|
||||
class={clsx(
|
||||
styles.playButton,
|
||||
props.isPlaying ? styles.playButtonInvertPause : styles.playButtonInvertPlay
|
||||
props.isPlaying ? styles.playButtonInvertPause : styles.playButtonInvertPlay,
|
||||
)}
|
||||
aria-label="Play"
|
||||
data-playing="false"
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import { createSignal, For, Show } from 'solid-js'
|
||||
import { SharePopup, getShareUrl } from '../SharePopup'
|
||||
import { getDescription } from '../../../utils/meta'
|
||||
import { createSignal, For, lazy, Show } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import styles from './AudioPlayer.module.scss'
|
||||
import { GrowingTextarea } from '../../_shared/GrowingTextarea'
|
||||
import MD from '../MD'
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
import SimplifiedEditor from '../../Editor/SimplifiedEditor'
|
||||
import { getDescription } from '../../../utils/meta'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
import { SharePopup, getShareUrl } from '../SharePopup'
|
||||
|
||||
import styles from './AudioPlayer.module.scss'
|
||||
|
||||
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
|
||||
const GrowingTextarea = lazy(() => import('../../_shared/GrowingTextarea/GrowingTextarea'))
|
||||
|
||||
type Props = {
|
||||
media: MediaItem[]
|
||||
|
@ -146,12 +148,12 @@ export const PlayerPlaylist = (props: Props) => {
|
|||
<div class={styles.descriptionBlock}>
|
||||
<Show when={mi.body}>
|
||||
<div class={styles.description}>
|
||||
<MD body={mi.body} />
|
||||
<div innerHTML={mi.body} />
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={mi.lyrics}>
|
||||
<div class={styles.lyrics}>
|
||||
<MD body={mi.lyrics} />
|
||||
<div innerHTML={mi.lyrics} />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
position: relative;
|
||||
list-style: none;
|
||||
|
||||
& .comment {
|
||||
margin-right: -1rem;
|
||||
@include media-breakpoint-down(sm) {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
&.isNew {
|
||||
|
@ -14,11 +14,9 @@
|
|||
background: rgb(38 56 217 / 5%);
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
margin-right: -1.2rem;
|
||||
}
|
||||
|
||||
.comment {
|
||||
margin-right: -1rem;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
|
@ -68,6 +66,8 @@
|
|||
}
|
||||
|
||||
.commentContent {
|
||||
padding: 0 1rem 1rem 0;
|
||||
|
||||
&:hover {
|
||||
.commentControlReply,
|
||||
.commentControlShare,
|
||||
|
@ -78,6 +78,10 @@
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.commentControlReply,
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
import { Show, createMemo, createSignal, For, lazy, Suspense } from 'solid-js'
|
||||
import { clsx } from 'clsx'
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import { clsx } from 'clsx'
|
||||
import { Show, createMemo, createSignal, For, lazy, Suspense } from 'solid-js'
|
||||
|
||||
import MD from '../MD'
|
||||
import { Userpic } from '../../Author/Userpic'
|
||||
import { CommentRatingControl } from '../CommentRatingControl'
|
||||
import { CommentDate } from '../CommentDate'
|
||||
import { ShowIfAuthenticated } from '../../_shared/ShowIfAuthenticated'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import { useSession } from '../../../context/session'
|
||||
import { useConfirm } from '../../../context/confirm'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { useReactions } from '../../../context/reactions'
|
||||
import { useSession } from '../../../context/session'
|
||||
import { useSnackbar } from '../../../context/snackbar'
|
||||
import { useConfirm } from '../../../context/confirm'
|
||||
|
||||
import { Author, Reaction, ReactionKind } from '../../../graphql/types.gen'
|
||||
import { router } from '../../../stores/router'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { ShowIfAuthenticated } from '../../_shared/ShowIfAuthenticated'
|
||||
import { AuthorLink } from '../../Author/AhtorLink'
|
||||
import { Userpic } from '../../Author/Userpic'
|
||||
import { CommentDate } from '../CommentDate'
|
||||
import { CommentRatingControl } from '../CommentRatingControl'
|
||||
|
||||
import styles from './Comment.module.scss'
|
||||
import { AuthorLink } from '../../Author/AhtorLink'
|
||||
|
||||
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
|
||||
|
||||
|
@ -44,15 +41,15 @@ export const Comment = (props: Props) => {
|
|||
const { session } = useSession()
|
||||
|
||||
const {
|
||||
actions: { createReaction, deleteReaction, updateReaction }
|
||||
actions: { createReaction, deleteReaction, updateReaction },
|
||||
} = useReactions()
|
||||
|
||||
const {
|
||||
actions: { showConfirm }
|
||||
actions: { showConfirm },
|
||||
} = useConfirm()
|
||||
|
||||
const {
|
||||
actions: { showSnackbar }
|
||||
actions: { showSnackbar },
|
||||
} = useSnackbar()
|
||||
|
||||
const isCommentAuthor = createMemo(() => props.comment.createdBy?.slug === session()?.user?.slug)
|
||||
|
@ -66,7 +63,7 @@ export const Comment = (props: Props) => {
|
|||
confirmBody: t('Are you sure you want to delete this comment?'),
|
||||
confirmButtonLabel: t('Delete'),
|
||||
confirmButtonVariant: 'danger',
|
||||
declineButtonVariant: 'primary'
|
||||
declineButtonVariant: 'primary',
|
||||
})
|
||||
|
||||
if (isConfirmed) {
|
||||
|
@ -87,7 +84,7 @@ export const Comment = (props: Props) => {
|
|||
kind: ReactionKind.Comment,
|
||||
replyTo: props.comment.id,
|
||||
body: value,
|
||||
shout: props.comment.shout.id
|
||||
shout: props.comment.shout.id,
|
||||
})
|
||||
setClearEditor(true)
|
||||
setIsReplyVisible(false)
|
||||
|
@ -108,7 +105,7 @@ export const Comment = (props: Props) => {
|
|||
await updateReaction(props.comment.id, {
|
||||
kind: ReactionKind.Comment,
|
||||
body: value,
|
||||
shout: props.comment.shout.id
|
||||
shout: props.comment.shout.id,
|
||||
})
|
||||
setEditMode(false)
|
||||
setLoading(false)
|
||||
|
@ -123,7 +120,7 @@ export const Comment = (props: Props) => {
|
|||
<li
|
||||
id={`comment_${comment().id}`}
|
||||
class={clsx(styles.comment, props.class, {
|
||||
[styles.isNew]: !isCommentAuthor() && createdAt > props.lastSeen
|
||||
[styles.isNew]: !isCommentAuthor() && createdAt > props.lastSeen,
|
||||
})}
|
||||
>
|
||||
<Show when={!!body()}>
|
||||
|
@ -136,7 +133,7 @@ export const Comment = (props: Props) => {
|
|||
name={comment().createdBy.name}
|
||||
userpic={comment().createdBy.userpic}
|
||||
class={clsx({
|
||||
[styles.compactUserpic]: props.compact
|
||||
[styles.compactUserpic]: props.compact,
|
||||
})}
|
||||
/>
|
||||
<small>
|
||||
|
@ -171,7 +168,7 @@ export const Comment = (props: Props) => {
|
|||
</div>
|
||||
</Show>
|
||||
<div class={styles.commentBody}>
|
||||
<Show when={editMode()} fallback={<MD body={body()} />}>
|
||||
<Show when={editMode()} fallback={<div innerHTML={body()} />}>
|
||||
<Suspense fallback={<p>{t('Loading')}</p>}>
|
||||
<SimplifiedEditor
|
||||
initialContent={comment().body}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { Show } from 'solid-js'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import type { Reaction } from '../../../graphql/types.gen'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
import { Show } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import styles from './CommentDate.module.scss'
|
||||
|
||||
type Props = {
|
||||
|
@ -27,7 +30,7 @@ export const CommentDate = (props: Props) => {
|
|||
<div
|
||||
class={clsx(styles.commentDates, {
|
||||
[styles.commentDatesLastInRow]: props.isLastInRow,
|
||||
[styles.showOnHover]: props.showOnHover
|
||||
[styles.showOnHover]: props.showOnHover,
|
||||
})}
|
||||
>
|
||||
<time class={styles.date}>{formattedDate(props.comment.createdAt)}</time>
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import { clsx } from 'clsx'
|
||||
import styles from './CommentRatingControl.module.scss'
|
||||
import type { Reaction } from '../../graphql/types.gen'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
import { useSession } from '../../context/session'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
import { createMemo } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { useSession } from '../../context/session'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
import { loadShout } from '../../stores/zine/articles'
|
||||
import { Popup } from '../_shared/Popup'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
import { VotersList } from '../_shared/VotersList'
|
||||
|
||||
import styles from './CommentRatingControl.module.scss'
|
||||
|
||||
type Props = {
|
||||
comment: Reaction
|
||||
}
|
||||
|
@ -19,11 +22,11 @@ export const CommentRatingControl = (props: Props) => {
|
|||
const { t } = useLocalize()
|
||||
const { user } = useSession()
|
||||
const {
|
||||
actions: { showSnackbar }
|
||||
actions: { showSnackbar },
|
||||
} = useSnackbar()
|
||||
const {
|
||||
reactionEntities,
|
||||
actions: { createReaction, deleteReaction, loadReactionsBy }
|
||||
actions: { createReaction, deleteReaction, loadReactionsBy },
|
||||
} = useReactions()
|
||||
|
||||
const checkReaction = (reactionKind: ReactionKind) =>
|
||||
|
@ -32,7 +35,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === user()?.slug &&
|
||||
r.shout.id === props.comment.shout.id &&
|
||||
r.replyTo === props.comment.id
|
||||
r.replyTo === props.comment.id,
|
||||
)
|
||||
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
|
||||
|
@ -43,8 +46,8 @@ export const CommentRatingControl = (props: Props) => {
|
|||
(r) =>
|
||||
[ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) &&
|
||||
r.shout.id === props.comment.shout.id &&
|
||||
r.replyTo === props.comment.id
|
||||
)
|
||||
r.replyTo === props.comment.id,
|
||||
),
|
||||
)
|
||||
|
||||
const deleteCommentReaction = async (reactionKind: ReactionKind) => {
|
||||
|
@ -53,7 +56,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === user()?.slug &&
|
||||
r.shout.id === props.comment.shout.id &&
|
||||
r.replyTo === props.comment.id
|
||||
r.replyTo === props.comment.id,
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
@ -68,7 +71,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
await createReaction({
|
||||
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
||||
shout: props.comment.shout.id,
|
||||
replyTo: props.comment.id
|
||||
replyTo: props.comment.id,
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
|
@ -77,7 +80,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
|
||||
await loadShout(props.comment.shout.slug)
|
||||
await loadReactionsBy({
|
||||
by: { shout: props.comment.shout.slug }
|
||||
by: { shout: props.comment.shout.slug },
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -88,7 +91,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
disabled={!canVote() || !user()}
|
||||
onClick={() => handleRatingChange(true)}
|
||||
class={clsx(styles.commentRatingControl, styles.commentRatingControlUp, {
|
||||
[styles.voted]: isUpvoted()
|
||||
[styles.voted]: isUpvoted(),
|
||||
})}
|
||||
/>
|
||||
<Popup
|
||||
|
@ -96,7 +99,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
<div
|
||||
class={clsx(styles.commentRatingValue, {
|
||||
[styles.commentRatingPositive]: props.comment.stat.rating > 0,
|
||||
[styles.commentRatingNegative]: props.comment.stat.rating < 0
|
||||
[styles.commentRatingNegative]: props.comment.stat.rating < 0,
|
||||
})}
|
||||
>
|
||||
{props.comment.stat.rating || 0}
|
||||
|
@ -114,7 +117,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
disabled={!canVote() || !user()}
|
||||
onClick={() => handleRatingChange(false)}
|
||||
class={clsx(styles.commentRatingControl, styles.commentRatingControlDown, {
|
||||
[styles.voted]: isDownvoted()
|
||||
[styles.voted]: isDownvoted(),
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import { Show, createMemo, createSignal, onMount, For } from 'solid-js'
|
||||
import { Comment } from './Comment'
|
||||
import styles from './Article.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import { Author, Reaction, ReactionKind } from '../../graphql/types.gen'
|
||||
import { useSession } from '../../context/session'
|
||||
import { Button } from '../_shared/Button'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { byCreated } from '../../utils/sortby'
|
||||
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
|
||||
import { Show, createMemo, createSignal, onMount, For, lazy } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import SimplifiedEditor from '../Editor/SimplifiedEditor'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { useSession } from '../../context/session'
|
||||
import { Author, Reaction, ReactionKind } from '../../graphql/types.gen'
|
||||
import { byCreated } from '../../utils/sortby'
|
||||
import { Button } from '../_shared/Button'
|
||||
import { ShowIfAuthenticated } from '../_shared/ShowIfAuthenticated'
|
||||
|
||||
import { Comment } from './Comment'
|
||||
|
||||
import styles from './Article.module.scss'
|
||||
|
||||
const SimplifiedEditor = lazy(() => import('../Editor/SimplifiedEditor'))
|
||||
|
||||
type CommentsOrder = 'createdAt' | 'rating' | 'newOnly'
|
||||
|
||||
|
@ -48,11 +52,11 @@ export const CommentsTree = (props: Props) => {
|
|||
|
||||
const {
|
||||
reactionEntities,
|
||||
actions: { createReaction }
|
||||
actions: { createReaction },
|
||||
} = useReactions()
|
||||
|
||||
const comments = createMemo(() =>
|
||||
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
|
||||
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT'),
|
||||
)
|
||||
|
||||
const sortedComments = createMemo(() => {
|
||||
|
@ -96,7 +100,7 @@ export const CommentsTree = (props: Props) => {
|
|||
await createReaction({
|
||||
kind: ReactionKind.Comment,
|
||||
body: value,
|
||||
shout: props.shoutId
|
||||
shout: props.shoutId,
|
||||
})
|
||||
setClearEditor(true)
|
||||
} catch (error) {
|
||||
|
@ -154,7 +158,7 @@ export const CommentsTree = (props: Props) => {
|
|||
<Comment
|
||||
sortedComments={sortedComments()}
|
||||
isArticleAuthor={Boolean(
|
||||
props.articleAuthors.some((a) => a.slug === reaction.createdBy.slug)
|
||||
props.articleAuthors.some((a) => a.slug === reaction.createdBy.slug),
|
||||
)}
|
||||
comment={reaction}
|
||||
clickedReply={(id) => setClickedReplyId(id)}
|
||||
|
|
40
src/components/Article/CoverImage/CoverImage.tsx
Normal file
|
@ -0,0 +1,40 @@
|
|||
import type { CoverImageProps } from './types'
|
||||
|
||||
import { CoverImage1 } from './images/CoverImage1'
|
||||
import { CoverImage10 } from './images/CoverImage10'
|
||||
import { CoverImage11 } from './images/CoverImage11'
|
||||
import { CoverImage12 } from './images/CoverImage12'
|
||||
import { CoverImage2 } from './images/CoverImage2'
|
||||
import { CoverImage3 } from './images/CoverImage3'
|
||||
import { CoverImage4 } from './images/CoverImage4'
|
||||
import { CoverImage5 } from './images/CoverImage5'
|
||||
import { CoverImage6 } from './images/CoverImage6'
|
||||
import { CoverImage7 } from './images/CoverImage7'
|
||||
import { CoverImage8 } from './images/CoverImage8'
|
||||
import { CoverImage9 } from './images/CoverImage9'
|
||||
|
||||
// not pretty, but I don't want to use dynamic imports
|
||||
const coverImages = [
|
||||
CoverImage1,
|
||||
CoverImage2,
|
||||
CoverImage3,
|
||||
CoverImage4,
|
||||
CoverImage5,
|
||||
CoverImage6,
|
||||
CoverImage7,
|
||||
CoverImage8,
|
||||
CoverImage9,
|
||||
CoverImage10,
|
||||
CoverImage11,
|
||||
CoverImage12,
|
||||
]
|
||||
|
||||
let counter = 0
|
||||
export const CoverImage = (props: CoverImageProps) => {
|
||||
const CoverImageComponent = coverImages[counter]
|
||||
counter++
|
||||
if (counter === coverImages.length) {
|
||||
counter = 0
|
||||
}
|
||||
return <CoverImageComponent {...props} />
|
||||
}
|
13
src/components/Article/CoverImage/images/CoverImage1.tsx
Normal file
14
src/components/Article/CoverImage/images/CoverImage10.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage10 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
stroke="#000"
|
||||
stroke-width=".25"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-2.531-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM9.373-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM21.279-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM33.184-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM45.09-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM56.996-.107H65.5v8.503zm0 11.906H65.5v8.504zm0 11.905H65.5v8.504zm0 11.905H65.5v8.504zm0 11.907H65.5v8.504zm0 11.904H65.5v8.504zm0 11.906H65.5v8.504zm0 11.905H65.5v8.504zm0 11.906H65.5v8.504zm0 11.906H65.5v8.504zm0 11.905H65.5v8.504zM68.9-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM80.807-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM92.713-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM104.617-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zM116.523-.107h8.504v8.503zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.905h8.504v8.504zm0 11.907h8.504v8.504zm0 11.904h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504zm0 11.906h8.504v8.504zm0 11.906h8.504v8.504zm0 11.905h8.504v8.504z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
15
src/components/Article/CoverImage/images/CoverImage11.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage11 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="4.949"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M2.457-.255v5.262h5.262m9.47-5.262v5.262h5.261m9.469-5.262v5.262h5.262m9.47-5.262v5.262h5.262m9.469-5.262v5.262h5.261m9.47-5.262v5.262h5.262m9.471-5.262v5.262h5.261m9.469-5.262v5.262h5.261M2.457 29.209v5.261h5.262m9.47-5.261v5.261h5.261m9.469-5.261v5.261h5.262m9.47-5.261v5.261h5.262m9.469-5.261v5.261h5.261m9.47-5.261v5.261h5.262m9.471-5.261v5.261h5.261m9.469-5.261v5.261h5.261M2.457 58.671v5.261h5.262m9.47-5.261v5.261h5.261m9.469-5.261v5.261h5.262m9.47-5.261v5.261h5.262m9.469-5.261v5.261h5.261m9.47-5.261v5.261h5.262m9.471-5.261v5.261h5.261m9.469-5.261v5.261h5.261M2.457 88.133v5.261h5.262m9.47-5.261v5.261h5.261m9.469-5.261v5.261h5.262m9.47-5.261v5.261h5.262m9.469-5.261v5.261h5.261m9.47-5.261v5.261h5.262m9.471-5.261v5.261h5.261m9.469-5.261v5.261h5.261M2.457 117.596v2.261m14.732-2.261v2.261m14.73-2.261v2.261m14.732-2.261v2.261m14.731-2.261v2.261m14.731-2.261v2.261m14.733-2.261v2.261m14.73-2.261v2.261M-1.909 19.737H.353m9.47-5.26v5.26h5.262m9.468-5.26v5.26h5.261m9.471-5.26v5.26h5.262m9.471-5.26v5.26h5.261m9.469-5.26v5.26h5.261m9.47-5.26v5.26h5.262m9.469-5.26v5.26h5.261m9.471-5.26v5.26h5.261M-1.909 49.199H.353m9.47-5.26v5.26h5.262m9.468-5.26v5.26h5.261m9.471-5.26v5.26h5.262m9.471-5.26v5.26h5.261m9.469-5.26v5.26h5.261m9.47-5.26v5.26h5.262m9.469-5.26v5.26h5.261m9.471-5.26v5.26h5.261M-1.909 78.663H.353m9.47-5.262v5.262h5.262m9.468-5.262v5.262h5.261m9.471-5.262v5.262h5.262m9.471-5.262v5.262h5.261m9.469-5.262v5.262h5.261m9.47-5.262v5.262h5.262m9.469-5.262v5.262h5.261m9.471-5.262v5.262h5.261M-1.909 108.126H.353m9.47-5.261v5.261h5.262m9.468-5.261v5.261h5.261m9.471-5.261v5.261h5.262m9.471-5.261v5.261h5.261m9.469-5.261v5.261h5.261m9.47-5.261v5.261h5.262m9.469-5.261v5.261h5.261m9.471-5.261v5.261h5.261"
|
||||
/>
|
||||
</svg>
|
||||
)
|
12
src/components/Article/CoverImage/images/CoverImage12.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage12 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
stroke="#000"
|
||||
stroke-width="3.23"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-.509-2.401h4.578v4.578H-.509v-4.578zm0 12.819h4.578v4.578H-.509v-4.578zm0 12.817h4.578v4.578H-.509v-4.578zm0 12.82h4.578v4.578H-.509v-4.578zm0 12.819h4.578v4.578H-.509v-4.578zm0 12.818h4.578v4.579H-.509v-4.579zm0 12.82h4.578v4.578H-.509v-4.578zm0 12.818h4.578v4.579H-.509V87.33zm0 12.818h4.578v4.579H-.509v-4.579zm0 12.82h4.578v4.578H-.509v-4.578zM12.311-2.401h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zM25.128-2.401h4.579v4.578h-4.579v-4.578zm0 12.819h4.579v4.578h-4.579v-4.578zm0 12.817h4.579v4.578h-4.579v-4.578zm0 12.82h4.579v4.578h-4.579v-4.578zm0 12.819h4.579v4.578h-4.579v-4.578zm0 12.818h4.579v4.579h-4.579v-4.579zm0 12.82h4.579v4.578h-4.579v-4.578zm0 12.818h4.579v4.579h-4.579V87.33zm0 12.818h4.579v4.579h-4.579v-4.579zm0 12.82h4.579v4.578h-4.579v-4.578zM37.947-2.401h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zM50.767-2.401h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zM63.586-2.401h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zM76.404-2.401h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zM89.223-2.401h4.579v4.578h-4.579v-4.578zm0 12.819h4.579v4.578h-4.579v-4.578zm0 12.817h4.579v4.578h-4.579v-4.578zm0 12.82h4.579v4.578h-4.579v-4.578zm0 12.819h4.579v4.578h-4.579v-4.578zm0 12.818h4.579v4.579h-4.579v-4.579zm0 12.82h4.579v4.578h-4.579v-4.578zm0 12.818h4.579v4.579h-4.579V87.33zm0 12.818h4.579v4.579h-4.579v-4.579zm0 12.82h4.579v4.578h-4.579v-4.578zm12.82-115.369h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zM114.86-2.401h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.817h4.578v4.578h-4.578v-4.578zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.819h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578zm0 12.818h4.578v4.579h-4.578V87.33zm0 12.818h4.578v4.579h-4.578v-4.579zm0 12.82h4.578v4.578h-4.578v-4.578z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
15
src/components/Article/CoverImage/images/CoverImage2.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage2 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="2"
|
||||
stroke-miterlimit="3.864"
|
||||
d="m-1.753-1.524-3.592 7.179h7.18zm14.36 0-3.59 7.179h7.179zm14.362 0-3.592 7.179h7.18zm14.36 0-3.591 7.179h7.18zm14.361 0-3.591 7.179h7.18zm14.362 0-3.593 7.179h7.181zm14.36 0-3.593 7.179h7.182zm14.359 0-3.59 7.179h7.18zm14.362 0-3.591 7.179h7.18zM-1.753 27.199l-3.592 7.18h7.18zm14.36 0-3.59 7.18h7.179zm14.362 0-3.592 7.18h7.18zm14.36 0-3.591 7.18h7.18zm14.361 0-3.591 7.18h7.18zm14.362 0-3.593 7.18h7.181zm14.36 0-3.593 7.18h7.182zm14.359 0-3.59 7.18h7.18zm14.362 0-3.591 7.18h7.18zM-1.753 55.921l-3.592 7.18h7.18zm14.36 0-3.59 7.18h7.179zm14.362 0-3.592 7.18h7.18zm14.36 0-3.591 7.18h7.18zm14.361 0-3.591 7.18h7.18zm14.362 0-3.593 7.18h7.181zm14.36 0-3.593 7.18h7.182zm14.359 0-3.59 7.18h7.18zm14.362 0-3.591 7.18h7.18zM-1.753 84.64l-3.592 7.182h7.18zm14.36 0-3.59 7.182h7.179zm14.362 0-3.592 7.182h7.18zm14.36 0-3.591 7.182h7.18zm14.361 0-3.591 7.182h7.18zm14.362 0-3.593 7.182h7.181zm14.36 0-3.593 7.182h7.182zm14.359 0-3.59 7.182h7.18zm14.362 0-3.591 7.182h7.18zM-1.753 113.363l-3.592 7.182h7.18zm14.36 0-3.59 7.182h7.179zm14.362 0-3.592 7.182h7.18zm14.36 0-3.591 7.182h7.18zm14.361 0-3.591 7.182h7.18zm14.362 0-3.593 7.182h7.181zm14.36 0-3.593 7.182h7.182zm14.359 0-3.59 7.182h7.18zm14.362 0-3.591 7.182h7.18zM5.428 12.838l-3.593 7.18h7.182zm14.361 0-3.593 7.18h7.181zm14.36 0-3.592 7.18h7.181zm14.36 0-3.591 7.18h7.181zm14.361 0-3.591 7.18h7.18zm14.361 0-3.591 7.18h7.179zm14.361 0-3.591 7.18h7.18zm14.361 0-3.592 7.18h7.181zm14.36 0-3.591 7.18h7.18zM5.428 41.56l-3.593 7.179h7.182zm14.361 0-3.593 7.179h7.181zm14.36 0-3.592 7.179h7.181zm14.36 0-3.591 7.179h7.181zm14.361 0-3.591 7.179h7.18zm14.361 0-3.591 7.179h7.179zm14.361 0-3.591 7.179h7.18zm14.361 0-3.592 7.179h7.181zm14.36 0-3.591 7.179h7.18zM5.428 70.279l-3.593 7.183h7.182zm14.361 0-3.593 7.183h7.181zm14.36 0-3.592 7.183h7.181zm14.36 0-3.591 7.183h7.181zm14.361 0-3.591 7.183h7.18zm14.361 0-3.591 7.183h7.179zm14.361 0-3.591 7.183h7.18zm14.361 0-3.592 7.183h7.181zm14.36 0-3.591 7.183h7.18zM5.428 99.002l-3.593 7.182h7.182zm14.361 0-3.593 7.182h7.181zm14.36 0-3.592 7.182h7.181zm14.36 0-3.591 7.182h7.181zm14.361 0-3.591 7.182h7.18zm14.361 0-3.591 7.182h7.179zm14.361 0-3.591 7.182h7.18zm14.361 0-3.592 7.182h7.181zm14.36 0-3.591 7.182h7.18z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
13
src/components/Article/CoverImage/images/CoverImage3.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage3 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
d="M3.647 8.782h5.291M1 .845v5.29m18.521 2.647h5.29M16.874.845v5.29m18.522 2.647h5.289M32.748.845v5.29M51.27 8.782h5.289M48.622.845v5.29m18.52 2.647h5.29M64.495.845v5.29m18.522 2.647h5.29M80.371.845v5.29m18.52 2.647h5.291M96.244.845v5.29m18.521 2.647h5.29M112.118.845v5.29M3.647 24.656h5.291M1 16.719v5.29m18.521 2.647h5.29m-7.937-7.937v5.289m18.522 2.648h5.289m-7.937-7.937v5.289m18.522 2.648h5.289m-7.937-7.937v5.289m18.52 2.648h5.29m-7.937-7.937v5.289m18.522 2.648h5.29m-7.936-7.937v5.289m18.52 2.648h5.291m-7.938-7.937v5.289m18.521 2.648h5.29m-7.937-7.937v5.289M3.647 40.53h5.291M1 32.593v5.29m18.521 2.647h5.29m-7.937-7.938v5.291m18.522 2.647h5.289m-7.937-7.938v5.291M51.27 40.53h5.289m-7.937-7.938v5.291m18.52 2.647h5.29m-7.937-7.938v5.291m18.522 2.647h5.29m-7.936-7.938v5.291m18.52 2.647h5.291m-7.938-7.938v5.291m18.521 2.647h5.29m-7.937-7.938v5.291M3.647 56.403h5.291M1 48.467v5.29m18.521 2.646h5.29m-7.937-7.936v5.29m18.522 2.646h5.289m-7.937-7.936v5.29m18.522 2.646h5.289m-7.937-7.936v5.29m18.52 2.646h5.29m-7.937-7.936v5.29m18.522 2.646h5.29m-7.936-7.936v5.29m18.52 2.646h5.291m-7.938-7.936v5.29m18.521 2.646h5.29m-7.937-7.936v5.29M3.647 72.277h5.291M1 64.34v5.29m18.521 2.647h5.29m-7.937-7.937v5.29m18.522 2.647h5.289m-7.937-7.937v5.29m18.522 2.647h5.289m-7.937-7.937v5.29m18.52 2.647h5.29m-7.937-7.937v5.29m18.522 2.647h5.29m-7.936-7.937v5.29m18.52 2.647h5.291m-7.938-7.937v5.29m18.521 2.647h5.29m-7.937-7.937v5.29M3.647 88.15h5.291M1 80.215v5.289m18.521 2.646h5.29m-7.937-7.935v5.289m18.522 2.646h5.289m-7.937-7.935v5.289M51.27 88.15h5.289m-7.937-7.935v5.289m18.52 2.646h5.29m-7.937-7.935v5.289m18.522 2.646h5.29m-7.936-7.935v5.289m18.52 2.646h5.291m-7.938-7.935v5.289m18.521 2.646h5.29m-7.937-7.935v5.289M3.647 104.025h5.291M1 96.088v5.29m18.521 2.647h5.29m-7.937-7.937v5.29m18.522 2.647h5.289m-7.937-7.937v5.29m18.522 2.647h5.289m-7.937-7.937v5.29m18.52 2.647h5.29m-7.937-7.937v5.29m18.522 2.647h5.29m-7.936-7.937v5.29m18.52 2.647h5.291m-7.938-7.937v5.29m18.521 2.647h5.29m-7.937-7.937v5.29M1 111.963v5.29m15.874-5.29v5.29m15.874-5.29v5.29m15.874-5.29v5.29m15.873-5.29v5.29m15.876-5.29v5.29m15.873-5.29v5.29m15.874-5.29v5.29"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="3.733"
|
||||
stroke-miterlimit="3.864"
|
||||
/>
|
||||
</svg>
|
||||
)
|
24
src/components/Article/CoverImage/images/CoverImage4.tsx
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage4 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="none"
|
||||
stroke="#231F20"
|
||||
stroke-width=".974"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-1.149-.979 1.772 120M1.611-.979 4.533 120M4.37-.979 7.293 120M7.133-.979 10.054 120m-.159-92.634 2.92 120.98m2.599-74.662 2.921 100.977M15.414-.979 18.336 120M18.174-.979 21.096 120M20.935-.979 23.856 120m1.299-92.634 2.921 120.98m2.6-58.311 2.922 120.979M29.216-.979 32.137 120M31.978-.979 34.899 120m-4.382-89.798 2.921 120.98M37.499-.979 40.42 120M40.259-.979 43.182 120M43.019-.979 45.94 120m-5.681-89.798 2.922 120.98M48.541-.979 51.462 120M51.301-.979 54.222 120M54.062-.979 56.982 120M49.84 49.547l2.921 120.98M62.343-.979 65.265 120M65.102-.979 68.023 120M67.863-.979 70.785 120M70.624-.979 73.546 120m-2.922-87.462 2.922 120.98M78.904-.979 81.826 120M81.666-.979 84.587 120M84.426-.979 87.348 120M87.188-.979 90.109 120M89.948-.979 92.869 120M92.708-.979 95.63 120m-4.383-32.145 2.922 100.979M98.229-.979 101.15 120M100.989-.979 103.91 120M103.75-.979 106.672 120M106.51-.979 109.432 120M109.271-.979 112.192 120m-5.682-64.286 2.922 120.979M114.791-.979 117.713 120m-.16-120.979L120.474 120M120.312-.979 123.234 120"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="none"
|
||||
stroke="#231F20"
|
||||
stroke-width=".984"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M6.316-.016V120m11.858-60.97v120.016m8.442-165.37v120.015M31.418-.016V120M39.784-.016V120m11.517-31.662v100.014m8.281-132.156v120.016m9.742-113.424v120.016M89.987-.016V120M98.354-.016V120M106.722-.016V120M115.088-.016V120"
|
||||
/>
|
||||
</svg>
|
||||
)
|
15
src/components/Article/CoverImage/images/CoverImage5.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage5 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
d="M.679 8.708 8.666.722m0 7.986L16.65.722m0 7.986L24.635.722m0 7.986L32.622.722m0 7.986L40.607.722m0 7.986 7.99-7.986m0 7.986L56.58.722m0 7.986L64.568.722m0 7.986L72.555.722m0 7.986L80.542.722m0 7.986L88.527.722m0 7.986L96.513.722m0 7.986L104.5.722m0 7.986 7.985-7.986m0 7.986 7.988-7.986M.679 16.694l7.987-7.986m0 7.986 7.984-7.986m0 7.986 7.985-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.99-7.986m0 7.986 7.983-7.986m0 7.986 7.988-7.986m0 7.986 7.987-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.986-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.988-7.986M.679 24.681l7.987-7.987m0 7.987 7.984-7.987m0 7.987 7.985-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.99-7.987m0 7.987 7.983-7.987m0 7.987 7.988-7.987m0 7.987 7.987-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.986-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.988-7.987M.679 32.666l7.987-7.985m0 7.985 7.984-7.985m0 7.985 7.985-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.99-7.985m0 7.985 7.983-7.985m0 7.985 7.988-7.985m0 7.985 7.987-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.986-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.988-7.985M.679 40.653l7.987-7.987m0 7.987 7.984-7.987m0 7.987 7.985-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.99-7.987m0 7.987 7.983-7.987m0 7.987 7.988-7.987m0 7.987 7.987-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.986-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.988-7.987M.679 48.641l7.987-7.988m0 7.988 7.984-7.988m0 7.988 7.985-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.99-7.988m0 7.988 7.983-7.988m0 7.988 7.988-7.988m0 7.988 7.987-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.986-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.988-7.988M.679 56.625l7.987-7.984m0 7.984 7.984-7.984m0 7.984 7.985-7.984m0 7.984 7.987-7.984m0 7.984 7.985-7.984m0 7.984 7.99-7.984m0 7.984 7.983-7.984m0 7.984 7.988-7.984m0 7.984 7.987-7.984m0 7.984 7.987-7.984m0 7.984 7.985-7.984m0 7.984 7.986-7.984m0 7.984 7.987-7.984m0 7.984 7.985-7.984m0 7.984 7.988-7.984M.679 64.611l7.987-7.986m0 7.986 7.984-7.986m0 7.986 7.985-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.99-7.986m0 7.986 7.983-7.986m0 7.986 7.988-7.986m0 7.986 7.987-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.986-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.988-7.986M.679 72.597l7.987-7.986m0 7.986 7.984-7.986m0 7.986 7.985-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.99-7.986m0 7.986 7.983-7.986m0 7.986 7.988-7.986m0 7.986 7.987-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.986-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.988-7.986M.679 80.585l7.987-7.988m0 7.988 7.984-7.988m0 7.988 7.985-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.99-7.988m0 7.988 7.983-7.988m0 7.988 7.988-7.988m0 7.988 7.987-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.986-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.988-7.988M.679 88.57l7.987-7.985m0 7.985 7.984-7.985m0 7.985 7.985-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.99-7.985m0 7.985 7.983-7.985m0 7.985 7.988-7.985m0 7.985 7.987-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.986-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.988-7.985M.679 96.556l7.987-7.986m0 7.986 7.984-7.986m0 7.986 7.985-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.99-7.986m0 7.986 7.983-7.986m0 7.986 7.988-7.986m0 7.986 7.987-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.986-7.986m0 7.986 7.987-7.986m0 7.986 7.985-7.986m0 7.986 7.988-7.986M.679 104.543l7.987-7.987m0 7.987 7.984-7.987m0 7.987 7.985-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.99-7.987m0 7.987 7.983-7.987m0 7.987 7.988-7.987m0 7.987 7.987-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.986-7.987m0 7.987 7.987-7.987m0 7.987 7.985-7.987m0 7.987 7.988-7.987M.679 112.528l7.987-7.985m0 7.985 7.984-7.985m0 7.985 7.985-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.99-7.985m0 7.985 7.983-7.985m0 7.985 7.988-7.985m0 7.985 7.987-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.986-7.985m0 7.985 7.987-7.985m0 7.985 7.985-7.985m0 7.985 7.988-7.985M.679 120.516l7.987-7.988m0 7.988 7.984-7.988m0 7.988 7.985-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.99-7.988m0 7.988 7.983-7.988m0 7.988 7.988-7.988m0 7.988 7.987-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.986-7.988m0 7.988 7.987-7.988m0 7.988 7.985-7.988m0 7.988 7.988-7.988"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.878"
|
||||
stroke-miterlimit="3.864"
|
||||
/>
|
||||
</svg>
|
||||
)
|
15
src/components/Article/CoverImage/images/CoverImage6.tsx
Normal file
15
src/components/Article/CoverImage/images/CoverImage7.tsx
Normal file
209
src/components/Article/CoverImage/images/CoverImage8.tsx
Normal file
|
@ -0,0 +1,209 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage8 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M129.44-7.29c0 11.343-11.396 11.343-11.396 22.792m.001 0c0 11.452 11.396 11.452 11.396 22.791 0 11.342-11.396 11.342-11.396 22.796m0 0c0 11.447 11.396 11.447 11.396 22.79 0 11.341-11.396 11.341-11.396 22.788m0 0c0 11.452 11.396 11.452 11.396 22.791M124.886-7.29c0 11.343-11.398 11.343-11.398 22.792"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M113.487 15.502c0 11.452 11.398 11.452 11.398 22.791 0 11.342-11.398 11.342-11.398 22.796m0 0c0 11.447 11.398 11.447 11.398 22.79 0 11.341-11.398 11.341-11.398 22.788m0 0c0 11.452 11.398 11.452 11.398 22.791M120.326-7.29c0 11.343-11.401 11.343-11.401 22.792"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M108.925 15.502c0 11.452 11.401 11.452 11.401 22.791 0 11.342-11.401 11.342-11.401 22.796m0 0c0 11.447 11.401 11.447 11.401 22.79 0 11.341-11.401 11.341-11.401 22.788m0 0c0 11.452 11.401 11.452 11.401 22.791m-4.669-136.83c.018 11.343-11.377 11.361-11.358 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M104.299 15.438c.018 11.453 11.412 11.435 11.431 22.773.019 11.341-11.377 11.359-11.358 22.814m-.001.001c.019 11.446 11.414 11.428 11.432 22.771.019 11.342-11.377 11.36-11.359 22.807m-.001 0c.019 11.453 11.414 11.435 11.432 22.773M111.096-7.365c.018 11.344-11.375 11.362-11.356 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M99.739 15.445c.018 11.453 11.41 11.435 11.429 22.773.018 11.341-11.375 11.359-11.356 22.814m0 .001c.019 11.447 11.411 11.428 11.429 22.771.019 11.341-11.374 11.359-11.356 22.806m-.001 0c.019 11.453 11.411 11.435 11.429 22.773m-4.778-136.74c.018 11.343-11.369 11.361-11.351 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M95.185 15.453c.019 11.452 11.404 11.434 11.423 22.772.018 11.342-11.368 11.36-11.35 22.815m0 .001c.019 11.446 11.405 11.428 11.423 22.771.019 11.341-11.368 11.36-11.351 22.807"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M95.33 106.618c.019 11.453 11.405 11.434 11.423 22.773M101.981-7.351c.018 11.344-11.375 11.363-11.356 22.811m0 0c.018 11.453 11.41 11.435 11.429 22.773.018 11.341-11.375 11.359-11.356 22.814m-.001.001c.019 11.446 11.412 11.428 11.43 22.771.019 11.341-11.375 11.359-11.357 22.806m0 0c.019 11.453 11.412 11.435 11.43 22.773M97.428-7.343C97.445 4 86.044 4.019 86.062 15.468"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M86.062 15.468C86.08 26.92 97.481 26.901 97.5 38.24c.018 11.342-11.383 11.36-11.364 22.814m0 .001c.019 11.447 11.419 11.429 11.437 22.771.02 11.341-11.382 11.359-11.364 22.807m-.001 0c.019 11.452 11.42 11.434 11.438 22.772M92.868-7.336c.018 11.343-11.384 11.362-11.365 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M81.503 15.475c.018 11.453 11.419 11.435 11.438 22.773.018 11.341-11.384 11.359-11.365 22.814m-.001 0c.019 11.446 11.42 11.428 11.438 22.771.019 11.342-11.383 11.36-11.365 22.807m-.001 0c.019 11.453 11.42 11.435 11.438 22.773M88.308-7.328c.018 11.343-11.377 11.361-11.358 22.81"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M76.949 15.481c.018 11.453 11.412 11.435 11.431 22.773.018 11.341-11.377 11.359-11.358 22.814m-.001.001c.019 11.447 11.414 11.429 11.432 22.771.019 11.341-11.377 11.359-11.359 22.807m0 0c.019 11.452 11.414 11.434 11.432 22.772M83.748-7.321c.018 11.342-11.38 11.361-11.361 22.81"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M72.387 15.489c.018 11.453 11.415 11.434 11.434 22.773.018 11.341-11.38 11.359-11.361 22.814m-.001.001c.02 11.446 11.416 11.428 11.434 22.771.019 11.342-11.378 11.36-11.36 22.807m-.001-.001c.019 11.453 11.415 11.435 11.433 22.773M79.191-7.314c.018 11.343-11.375 11.362-11.356 22.81"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M67.835 15.496c.018 11.453 11.411 11.435 11.43 22.773.018 11.341-11.376 11.359-11.356 22.814m-.001.001c.019 11.446 11.411 11.428 11.429 22.771.019 11.341-11.374 11.359-11.356 22.806m-.001 0c.019 11.453 11.411 11.435 11.429 22.773M74.632-7.307c.017 11.343-11.377 11.362-11.359 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M63.273 15.504c.018 11.452 11.412 11.434 11.431 22.772.018 11.342-11.377 11.36-11.358 22.815m0 .001c.019 11.446 11.413 11.428 11.431 22.771.019 11.341-11.376 11.359-11.358 22.807m-.001-.001c.019 11.452 11.414 11.434 11.432 22.773M70.078-7.3C70.096 4.044 58.701 4.062 58.72 15.511"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M58.72 15.511c.018 11.453 11.412 11.435 11.431 22.773.018 11.341-11.377 11.359-11.358 22.814m-.001.001c.019 11.446 11.413 11.428 11.431 22.771.019 11.341-11.376 11.359-11.358 22.806m-.001 0c.019 11.453 11.413 11.435 11.431 22.773M65.518-7.292c.018 11.343-11.375 11.361-11.356 22.81"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M54.161 15.518c.018 11.453 11.411 11.435 11.43 22.773.018 11.341-11.376 11.36-11.356 22.814m-.001 0c.019 11.447 11.411 11.429 11.429 22.771.019 11.341-11.374 11.359-11.356 22.807m0 .001c.019 11.452 11.411 11.434 11.429 22.772M60.964-7.285C60.981 4.058 49.581 4.076 49.6 15.525"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M49.6 15.525c.018 11.453 11.418 11.435 11.438 22.773.018 11.341-11.384 11.359-11.365 22.814m-.001.001c.019 11.446 11.42 11.428 11.438 22.771.019 11.342-11.383 11.36-11.365 22.807m-.001-.001c.019 11.453 11.42 11.435 11.438 22.773M56.398-7.278c.018 11.344-11.38 11.362-11.361 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M45.037 15.532c.018 11.453 11.415 11.435 11.434 22.773.018 11.341-11.38 11.359-11.361 22.814m-.001.001c.019 11.447 11.416 11.428 11.434 22.771.019 11.341-11.379 11.359-11.361 22.806m0 0c.02 11.453 11.416 11.435 11.434 22.773M51.842-7.271C51.859 4.072 40.467 4.091 40.485 15.54"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M40.485 15.54c.018 11.452 11.41 11.434 11.429 22.772.018 11.342-11.375 11.36-11.356 22.815m0 .001c.019 11.446 11.412 11.428 11.43 22.771.019 11.341-11.374 11.36-11.356 22.807m-.001-.001c.019 11.453 11.411 11.434 11.429 22.773M47.284-7.264c.018 11.344-11.38 11.362-11.36 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M35.924 15.547C35.941 27 47.338 26.981 47.356 38.32c.018 11.341-11.379 11.359-11.36 22.814m0 .001c.019 11.446 11.415 11.428 11.433 22.771.019 11.341-11.378 11.359-11.36 22.806m-.001 0c.019 11.453 11.416 11.435 11.434 22.773M42.722-7.256c.017 11.343-11.377 11.361-11.359 22.811"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M31.363 15.555c.019 11.452 11.412 11.434 11.432 22.772.018 11.342-11.378 11.36-11.359 22.814m0 .001c.018 11.447 11.414 11.428 11.431 22.771.019 11.341-11.377 11.359-11.359 22.807"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M31.508 106.72c.02 11.452 11.414 11.434 11.432 22.772M38.168-7.249C38.186 4.094 26.791 4.113 26.81 15.562m0 0c.019 11.453 11.412 11.435 11.431 22.773.018 11.341-11.377 11.359-11.358 22.814m-.001 0C26.9 72.596 38.296 72.577 38.313 83.92c.019 11.342-11.377 11.36-11.359 22.807m0 0c.019 11.453 11.414 11.435 11.432 22.773M33.614-7.24C33.632 4.102 22.23 4.12 22.25 15.568"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M22.25 15.568c.019 11.453 11.418 11.435 11.437 22.773.018 11.341-11.383 11.359-11.364 22.814m-.001.001c.019 11.447 11.42 11.429 11.438 22.771.019 11.341-11.383 11.359-11.365 22.807m0 0c.019 11.452 11.42 11.434 11.438 22.772M29.055-7.234c.017 11.342-11.386 11.361-11.367 22.81"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M17.688 15.576c.018 11.453 11.421 11.434 11.439 22.774.018 11.34-11.386 11.359-11.367 22.814m0 0c.019 11.446 11.422 11.428 11.439 22.771.019 11.342-11.385 11.36-11.367 22.807"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M17.832 106.741c.019 11.453 11.422 11.435 11.439 22.773M24.492-7.227c.018 11.344-11.377 11.362-11.358 22.81m0 0c.019 11.453 11.413 11.436 11.431 22.773.018 11.341-11.377 11.359-11.358 22.814m-.001.001c.019 11.446 11.413 11.428 11.431 22.771.02 11.341-11.376 11.359-11.358 22.806m-.001 0c.019 11.453 11.414 11.435 11.432 22.773M20.043-7.29c0 11.343-11.396 11.343-11.396 22.792"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M8.646 15.502c0 11.452 11.396 11.452 11.396 22.791 0 11.342-11.396 11.342-11.396 22.796m0 0c0 11.447 11.396 11.447 11.396 22.79 0 11.341-11.396 11.341-11.396 22.788m0 0c0 11.452 11.396 11.452 11.396 22.791M15.487-7.29c0 11.343-11.396 11.343-11.396 22.792"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M4.092 15.502c0 11.452 11.396 11.452 11.396 22.791 0 11.342-11.396 11.342-11.396 22.796m0 0c0 11.447 11.396 11.447 11.396 22.79 0 11.341-11.396 11.341-11.396 22.788m0 0c0 11.452 11.396 11.452 11.396 22.791M10.927-7.29C10.927 4.053-.47 4.053-.47 15.502"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-.47 15.502c0 11.452 11.396 11.452 11.396 22.791C10.926 49.635-.47 49.635-.47 61.089m0 0c0 11.447 11.396 11.447 11.396 22.79C10.926 95.22-.47 95.22-.47 106.667m0 0c0 11.452 11.396 11.452 11.396 22.791M6.371-7.29c0 11.343-11.393 11.343-11.393 22.792"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-5.021 15.502c0 11.452 11.393 11.452 11.393 22.791 0 11.342-11.393 11.342-11.393 22.796m0 0c0 11.447 11.393 11.447 11.393 22.79 0 11.341-11.393 11.341-11.393 22.788m0 0c0 11.452 11.393 11.452 11.393 22.791"
|
||||
/>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="1.608"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-9.59 15.502c0 11.452 11.403 11.452 11.403 22.791 0 11.342-11.403 11.342-11.403 22.796m0 0c0 11.447 11.403 11.447 11.403 22.79C1.813 95.22-9.59 95.22-9.59 106.667m0 0c0 11.452 11.403 11.452 11.403 22.791"
|
||||
/>
|
||||
</svg>
|
||||
)
|
13
src/components/Article/CoverImage/images/CoverImage9.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { CoverImageProps } from '../types'
|
||||
|
||||
export const CoverImage9 = (props: CoverImageProps) => (
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 120 120" {...props}>
|
||||
<path
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="5"
|
||||
stroke-miterlimit="3.864"
|
||||
d="M-23.957 4.94c21.724 0 21.724-3.6 43.237-3.6 21.512 0 21.512 3.601 43.234 3.601m.001-.001c21.724 0 21.724-3.601 43.237-3.601 21.515 0 21.515 3.601 43.237 3.601m-172.946 8.649c21.724 0 21.724-3.603 43.237-3.603 21.512 0 21.512 3.603 43.234 3.603m.001 0c21.724 0 21.724-3.603 43.237-3.603 21.515 0 21.515 3.603 43.237 3.603m-172.946 8.646c21.724 0 21.724-3.603 43.237-3.603 21.512 0 21.512 3.603 43.234 3.603m.001 0c21.724 0 21.724-3.603 43.237-3.603 21.515 0 21.515 3.603 43.237 3.603m-172.946 8.647c21.724 0 21.724-3.6 43.237-3.6 21.512 0 21.512 3.6 43.234 3.6m.001 0c21.724 0 21.724-3.6 43.237-3.6 21.515 0 21.515 3.6 43.237 3.6m-172.946 8.649c21.724 0 21.724-3.604 43.237-3.604 21.512 0 21.512 3.604 43.234 3.604m.001 0c21.724 0 21.724-3.604 43.237-3.604 21.515 0 21.515 3.604 43.237 3.604m-172.946 8.647c21.724 0 21.724-3.604 43.237-3.604 21.512 0 21.512 3.604 43.234 3.604m.001 0c21.724 0 21.724-3.604 43.237-3.604 21.515 0 21.515 3.604 43.237 3.604m-172.946 8.645c21.724 0 21.724-3.6 43.237-3.6 21.512 0 21.512 3.6 43.234 3.6m.001 0c21.724 0 21.724-3.6 43.237-3.6 21.515 0 21.515 3.6 43.237 3.6m-172.946 8.65c21.724 0 21.724-3.603 43.237-3.603 21.512 0 21.512 3.603 43.234 3.603m.001 0c21.724 0 21.724-3.603 43.237-3.603 21.515 0 21.515 3.603 43.237 3.603m-172.946 8.646c21.724 0 21.724-3.603 43.237-3.603 21.512 0 21.512 3.603 43.234 3.603m.001 0c21.724 0 21.724-3.603 43.237-3.603 21.515 0 21.515 3.603 43.237 3.603m-172.946 8.647c21.724 0 21.724-3.604 43.237-3.604 21.512 0 21.512 3.604 43.234 3.604m.001 0c21.724 0 21.724-3.604 43.237-3.604 21.515 0 21.515 3.604 43.237 3.604m-172.946 8.646c21.724 0 21.724-3.604 43.237-3.604 21.512 0 21.512 3.604 43.234 3.604m.001 0c21.724 0 21.724-3.604 43.237-3.604 21.515 0 21.515 3.604 43.237 3.604m-172.946 8.649c21.724 0 21.724-3.604 43.237-3.604 21.512 0 21.512 3.604 43.234 3.604m.001 0c21.724 0 21.724-3.604 43.237-3.604 21.515 0 21.515 3.604 43.237 3.604m-172.946 8.646c21.724 0 21.724-3.603 43.237-3.603 21.512 0 21.512 3.603 43.234 3.603m.001 0c21.724 0 21.724-3.603 43.237-3.603 21.515 0 21.515 3.603 43.237 3.603m-172.946 8.647c21.724 0 21.724-3.604 43.237-3.604 21.512 0 21.512 3.604 43.234 3.604m.001 0c21.724 0 21.724-3.604 43.237-3.604 21.515 0 21.515 3.604 43.237 3.604"
|
||||
/>
|
||||
</svg>
|
||||
)
|
1
src/components/Article/CoverImage/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { CoverImage } from './CoverImage'
|
3
src/components/Article/CoverImage/types.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export type CoverImageProps = {
|
||||
class?: string
|
||||
}
|
|
@ -1,31 +1,41 @@
|
|||
import { createEffect, For, createMemo, onMount, Show, createSignal, onCleanup } from 'solid-js'
|
||||
import { Title } from '@solidjs/meta'
|
||||
import { clsx } from 'clsx'
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import MD from './MD'
|
||||
import type { Author, Shout } from '../../graphql/types.gen'
|
||||
import { useSession } from '../../context/session'
|
||||
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import { Link, Meta } from '@solidjs/meta'
|
||||
import { clsx } from 'clsx'
|
||||
import { createEffect, For, createMemo, onMount, Show, createSignal, onCleanup } from 'solid-js'
|
||||
import { isServer } from 'solid-js/web'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { useSession } from '../../context/session'
|
||||
import { MediaItem } from '../../pages/types'
|
||||
import { DEFAULT_HEADER_OFFSET, router, useRouter } from '../../stores/router'
|
||||
import { getDescription } from '../../utils/meta'
|
||||
import { TableOfContents } from '../TableOfContents'
|
||||
import { AudioPlayer } from './AudioPlayer'
|
||||
import { SharePopup } from './SharePopup'
|
||||
import { ShoutRatingControl } from './ShoutRatingControl'
|
||||
import { CommentsTree } from './CommentsTree'
|
||||
import stylesHeader from '../Nav/Header/Header.module.scss'
|
||||
import { AudioHeader } from './AudioHeader'
|
||||
import { Popover } from '../_shared/Popover'
|
||||
import { VideoPlayer } from '../_shared/VideoPlayer'
|
||||
import { showModal } from '../../stores/ui'
|
||||
import { getImageUrl, getOpenGraphImageUrl } from '../../utils/getImageUrl'
|
||||
import { getDescription, getKeywords } from '../../utils/meta'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { SolidSwiper } from '../_shared/SolidSwiper'
|
||||
import styles from './Article.module.scss'
|
||||
import { CardTopic } from '../Feed/CardTopic'
|
||||
import { createPopper } from '@popperjs/core'
|
||||
import { Image } from '../_shared/Image'
|
||||
import { InviteCoAuthorsModal } from '../_shared/InviteCoAuthorsModal'
|
||||
import { Lightbox } from '../_shared/Lightbox'
|
||||
import { Popover } from '../_shared/Popover'
|
||||
import { ShareModal } from '../_shared/ShareModal'
|
||||
import { ImageSwiper } from '../_shared/SolidSwiper'
|
||||
import { VideoPlayer } from '../_shared/VideoPlayer'
|
||||
import { AuthorBadge } from '../Author/AuthorBadge'
|
||||
import { getImageUrl } from '../../utils/getImageUrl'
|
||||
import { CardTopic } from '../Feed/CardTopic'
|
||||
import { FeedArticlePopup } from '../Feed/FeedArticlePopup'
|
||||
import { TableOfContents } from '../TableOfContents'
|
||||
|
||||
import { AudioHeader } from './AudioHeader'
|
||||
import { AudioPlayer } from './AudioPlayer'
|
||||
import { CommentsTree } from './CommentsTree'
|
||||
import { getShareUrl, SharePopup } from './SharePopup'
|
||||
import { ShoutRatingControl } from './ShoutRatingControl'
|
||||
|
||||
import styles from './Article.module.scss'
|
||||
import stylesHeader from '../Nav/Header/Header.module.scss'
|
||||
|
||||
type Props = {
|
||||
article: Shout
|
||||
|
@ -43,26 +53,30 @@ const scrollTo = (el: HTMLElement) => {
|
|||
window.scrollTo({
|
||||
top: top + window.scrollY - DEFAULT_HEADER_OFFSET,
|
||||
left: 0,
|
||||
behavior: 'smooth'
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
|
||||
const imgSrcRegExp = /<img[^>]+src\s*=\s*["']([^"']+)["']/gi
|
||||
|
||||
export const FullArticle = (props: Props) => {
|
||||
const [selectedImage, setSelectedImage] = createSignal('')
|
||||
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
||||
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
||||
|
||||
const { t, formatDate } = useLocalize()
|
||||
const {
|
||||
user,
|
||||
isAuthenticated,
|
||||
actions: { requireAuthentication }
|
||||
actions: { requireAuthentication },
|
||||
} = useSession()
|
||||
|
||||
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
||||
|
||||
const formattedDate = createMemo(() => formatDate(new Date(props.article.createdAt)))
|
||||
|
||||
const mainTopic = createMemo(
|
||||
() =>
|
||||
props.article.topics?.find((topic) => topic?.slug === props.article.mainTopic) ||
|
||||
props.article.topics[0]
|
||||
props.article.topics[0],
|
||||
)
|
||||
|
||||
const canEdit = () => props.article.authors?.some((a) => a.slug === user()?.slug)
|
||||
|
@ -87,8 +101,33 @@ export const FullArticle = (props: Props) => {
|
|||
}
|
||||
return props.article.body
|
||||
})
|
||||
const media = createMemo(() => {
|
||||
return JSON.parse(props.article.media || '[]')
|
||||
|
||||
const imageUrls = createMemo(() => {
|
||||
if (!body()) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
const result: string[] = []
|
||||
let match: RegExpMatchArray
|
||||
|
||||
while ((match = imgSrcRegExp.exec(body())) !== null) {
|
||||
result.push(match[1])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const imageElements = document.querySelectorAll<HTMLImageElement>('#shoutBody img')
|
||||
// eslint-disable-next-line unicorn/prefer-spread
|
||||
return Array.from(imageElements).map((img) => img.src)
|
||||
})
|
||||
|
||||
const media = createMemo<MediaItem[]>(() => {
|
||||
try {
|
||||
return JSON.parse(props.article.media)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
const commentsRef: {
|
||||
|
@ -99,7 +138,7 @@ export const FullArticle = (props: Props) => {
|
|||
scrollTo(commentsRef.current)
|
||||
}
|
||||
|
||||
const { searchParams, changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
||||
const { searchParams, changeSearchParams } = useRouter<ArticlePageSearchParams>()
|
||||
|
||||
createEffect(() => {
|
||||
if (props.scrollToComments) {
|
||||
|
@ -110,8 +149,8 @@ export const FullArticle = (props: Props) => {
|
|||
createEffect(() => {
|
||||
if (searchParams()?.scrollTo === 'comments' && commentsRef.current) {
|
||||
scrollToComments()
|
||||
changeSearchParam({
|
||||
scrollTo: null
|
||||
changeSearchParams({
|
||||
scrollTo: null,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -119,10 +158,10 @@ export const FullArticle = (props: Props) => {
|
|||
createEffect(() => {
|
||||
if (searchParams().commentId && isReactionsLoaded()) {
|
||||
const commentElement = document.querySelector<HTMLElement>(
|
||||
`[id='comment_${searchParams().commentId}']`
|
||||
`[id='comment_${searchParams().commentId}']`,
|
||||
)
|
||||
|
||||
changeSearchParam({ commentId: null })
|
||||
changeSearchParams({ commentId: null })
|
||||
|
||||
if (commentElement) {
|
||||
scrollTo(commentElement)
|
||||
|
@ -131,17 +170,21 @@ export const FullArticle = (props: Props) => {
|
|||
})
|
||||
|
||||
const {
|
||||
actions: { loadReactionsBy }
|
||||
actions: { loadReactionsBy },
|
||||
} = useReactions()
|
||||
|
||||
onMount(async () => {
|
||||
await loadReactionsBy({
|
||||
by: { shout: props.article.slug }
|
||||
by: { shout: props.article.slug },
|
||||
})
|
||||
|
||||
setIsReactionsLoaded(true)
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
document.title = props.article.title
|
||||
})
|
||||
|
||||
const clickHandlers = []
|
||||
const documentClickHandlers = []
|
||||
|
||||
|
@ -151,7 +194,7 @@ export const FullArticle = (props: Props) => {
|
|||
}
|
||||
|
||||
const tooltipElements: NodeListOf<HTMLElement> = document.querySelectorAll(
|
||||
'[data-toggle="tooltip"], footnote'
|
||||
'[data-toggle="tooltip"], footnote',
|
||||
)
|
||||
if (!tooltipElements) {
|
||||
return
|
||||
|
@ -168,7 +211,7 @@ export const FullArticle = (props: Props) => {
|
|||
document.body.appendChild(tooltip)
|
||||
|
||||
if (element.hasAttribute('href')) {
|
||||
element.setAttribute('href', 'javascript: void(0);')
|
||||
element.setAttribute('href', 'javascript: void(0)')
|
||||
}
|
||||
|
||||
const popperInstance = createPopper(element, tooltip, {
|
||||
|
@ -176,19 +219,19 @@ export const FullArticle = (props: Props) => {
|
|||
modifiers: [
|
||||
{
|
||||
name: 'eventListeners',
|
||||
options: { scroll: false }
|
||||
options: { scroll: false },
|
||||
},
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 8]
|
||||
}
|
||||
offset: [0, 8],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'flip',
|
||||
options: { fallbackPlacements: ['top'] }
|
||||
}
|
||||
]
|
||||
options: { fallbackPlacements: ['top'] },
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
tooltip.style.visibility = 'hidden'
|
||||
|
@ -229,12 +272,53 @@ export const FullArticle = (props: Props) => {
|
|||
})
|
||||
})
|
||||
|
||||
const openLightbox = (image) => {
|
||||
setSelectedImage(image)
|
||||
}
|
||||
const handleLightboxClose = () => {
|
||||
setSelectedImage()
|
||||
}
|
||||
|
||||
const handleArticleBodyClick = (event) => {
|
||||
if (event.target.tagName === 'IMG' && !event.target.dataset['disableLightbox']) {
|
||||
const src = event.target.src
|
||||
openLightbox(getImageUrl(src))
|
||||
}
|
||||
}
|
||||
|
||||
const cover = props.article.cover ?? 'production/image/logo_image.png'
|
||||
|
||||
const ogImage = getOpenGraphImageUrl(cover, {
|
||||
title: props.article.title,
|
||||
topic: mainTopic().title,
|
||||
author: props.article.authors[0].name,
|
||||
width: 1200,
|
||||
})
|
||||
|
||||
const description = getDescription(props.article.description || body())
|
||||
const ogTitle = props.article.title
|
||||
const keywords = getKeywords(props.article)
|
||||
const shareUrl = getShareUrl({ pathname: `/${props.article.slug}` })
|
||||
return (
|
||||
<>
|
||||
<Title>{props.article.title}</Title>
|
||||
<Meta name="descprition" content={description} />
|
||||
<Meta name="keywords" content={keywords} />
|
||||
<Meta name="og:type" content="article" />
|
||||
<Meta name="og:title" content={ogTitle} />
|
||||
<Meta name="og:image" content={ogImage} />
|
||||
<Meta name="og:description" content={description} />
|
||||
<Meta name="twitter:card" content="summary_large_image" />
|
||||
<Meta name="twitter:title" content={ogTitle} />
|
||||
<Meta name="twitter:description" content={description} />
|
||||
<Meta name="twitter:image" content={ogImage} />
|
||||
|
||||
<For each={imageUrls()}>{(imageUrl) => <Link rel="preload" as="image" href={imageUrl} />}</For>
|
||||
<div class="wide-container">
|
||||
<div class="row position-relative">
|
||||
<article class="col-md-16 col-lg-14 col-xl-12 offset-md-5">
|
||||
<article
|
||||
class={clsx('col-md-16 col-lg-14 col-xl-12 offset-md-5', styles.articleContent)}
|
||||
onClick={handleArticleBodyClick}
|
||||
>
|
||||
{/*TODO: Check styles.shoutTopic*/}
|
||||
<Show when={props.article.layout !== 'music'}>
|
||||
<div class={styles.shoutHeader}>
|
||||
|
@ -264,12 +348,7 @@ export const FullArticle = (props: Props) => {
|
|||
props.article.layout !== 'image'
|
||||
}
|
||||
>
|
||||
<div
|
||||
class={styles.shoutCover}
|
||||
style={{
|
||||
'background-image': `url('${getImageUrl(props.article.cover, { width: 1600 })}')`
|
||||
}}
|
||||
/>
|
||||
<Image width={800} alt={props.article.title} src={props.article.cover} />
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
|
@ -301,7 +380,7 @@ export const FullArticle = (props: Props) => {
|
|||
description={m.body}
|
||||
/>
|
||||
<Show when={m?.body}>
|
||||
<MD body={m.body} />
|
||||
<div innerHTML={m.body} />
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
|
@ -310,11 +389,7 @@ export const FullArticle = (props: Props) => {
|
|||
</Show>
|
||||
|
||||
<Show when={body()}>
|
||||
<div id="shoutBody" class={styles.shoutBody}>
|
||||
<Show when={!body().startsWith('<')} fallback={<div innerHTML={body()} />}>
|
||||
<MD body={body()} />
|
||||
</Show>
|
||||
</div>
|
||||
<div id="shoutBody" class={styles.shoutBody} innerHTML={body()} />
|
||||
</Show>
|
||||
</article>
|
||||
|
||||
|
@ -331,7 +406,7 @@ export const FullArticle = (props: Props) => {
|
|||
<div class="wide-container">
|
||||
<div class="row">
|
||||
<div class="col-md-20 offset-md-2">
|
||||
<SolidSwiper images={media()} />
|
||||
<ImageSwiper images={media()} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -346,35 +421,58 @@ export const FullArticle = (props: Props) => {
|
|||
<ShoutRatingControl shout={props.article} class={styles.ratingControl} />
|
||||
</div>
|
||||
|
||||
<Popover content={t('Comment')}>
|
||||
<Popover content={t('Comment')} disabled={isActionPopupActive()}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<div class={styles.shoutStatsItem} ref={triggerRef} onClick={scrollToComments}>
|
||||
<div class={clsx(styles.shoutStatsItem)} ref={triggerRef} onClick={scrollToComments}>
|
||||
<Icon name="comment" class={styles.icon} />
|
||||
<Icon name="comment-hover" class={clsx(styles.icon, styles.iconHover)} />
|
||||
{props.article.stat?.commented ?? ''}
|
||||
<Show
|
||||
when={props.article.stat?.commented}
|
||||
fallback={<span class={styles.commentsTextLabel}>{t('Add comment')}</span>}
|
||||
>
|
||||
{props.article.stat?.commented}
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
<Show when={props.article.stat?.viewed}>
|
||||
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemViews)}>
|
||||
<Icon name="eye" class={styles.icon} />
|
||||
<Icon name="eye" class={clsx(styles.icon, styles.iconHover)} />
|
||||
<span class={styles.shoutStatsItemCount}>{props.article.stat?.viewed}</span>
|
||||
<span class={styles.shoutStatsItemLabel}>
|
||||
{t('viewsWithCount', { count: props.article.stat?.viewed })}
|
||||
</span>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Popover content={t('Share')}>
|
||||
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
|
||||
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}>
|
||||
{formattedDate()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Popover content={t('Add to bookmarks')} disabled={isActionPopupActive()}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<div
|
||||
class={clsx(styles.shoutStatsItem, styles.shoutStatsItemBookmarks)}
|
||||
ref={triggerRef}
|
||||
onClick={handleBookmarkButtonClick}
|
||||
>
|
||||
<div class={styles.shoutStatsItemInner}>
|
||||
<Icon name="bookmark" class={styles.icon} />
|
||||
<Icon name="bookmark-hover" class={clsx(styles.icon, styles.iconHover)} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
<Popover content={t('Share')} disabled={isActionPopupActive()}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<div class={styles.shoutStatsItem} ref={triggerRef}>
|
||||
<SharePopup
|
||||
title={props.article.title}
|
||||
description={getDescription(props.article.body)}
|
||||
description={description}
|
||||
imageUrl={props.article.cover}
|
||||
shareUrl={shareUrl}
|
||||
containerCssClass={stylesHeader.control}
|
||||
onVisibilityChange={(isVisible) => setIsActionPopupActive(isVisible)}
|
||||
trigger={
|
||||
<div class={styles.shoutStatsItemInner}>
|
||||
<Icon name="share-outline" class={styles.icon} />
|
||||
|
@ -385,16 +483,7 @@ export const FullArticle = (props: Props) => {
|
|||
</div>
|
||||
)}
|
||||
</Popover>
|
||||
<Popover content={t('Add to bookmarks')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
<div class={styles.shoutStatsItem} ref={triggerRef} onClick={handleBookmarkButtonClick}>
|
||||
<div class={styles.shoutStatsItemInner}>
|
||||
<Icon name="bookmark" class={styles.icon} />
|
||||
<Icon name="bookmark-hover" class={clsx(styles.icon, styles.iconHover)} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Popover>
|
||||
|
||||
<Show when={canEdit()}>
|
||||
<Popover content={t('Edit')}>
|
||||
{(triggerRef: (el) => void) => (
|
||||
|
@ -410,20 +499,32 @@ export const FullArticle = (props: Props) => {
|
|||
)}
|
||||
</Popover>
|
||||
</Show>
|
||||
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
|
||||
<div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}>
|
||||
{formattedDate()}
|
||||
|
||||
<FeedArticlePopup
|
||||
isOwner={canEdit()}
|
||||
containerCssClass={clsx(stylesHeader.control, styles.articlePopupOpener)}
|
||||
onShareClick={() => showModal('share')}
|
||||
onInviteClick={() => showModal('inviteCoAuthors')}
|
||||
onVisibilityChange={(isVisible) => setIsActionPopupActive(isVisible)}
|
||||
trigger={
|
||||
<button>
|
||||
<Icon name="ellipsis" class={clsx(styles.icon)} />
|
||||
<Icon name="ellipsis" class={clsx(styles.icon, styles.iconHover)} />
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.help}>
|
||||
|
||||
<Show when={isAuthenticated() && !canEdit()}>
|
||||
<div class={styles.help}>
|
||||
<button class="button">{t('Cooperate')}</button>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={canEdit()}>
|
||||
<div class={styles.help}>
|
||||
<button class="button button--light">{t('Invite to collab')}</button>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show when={props.article.topics.length}>
|
||||
<div class={styles.topicsList}>
|
||||
|
@ -461,6 +562,16 @@ export const FullArticle = (props: Props) => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={selectedImage()}>
|
||||
<Lightbox image={selectedImage()} onClose={handleLightboxClose} />
|
||||
</Show>
|
||||
<InviteCoAuthorsModal title={t('Invite experts')} />
|
||||
<ShareModal
|
||||
title={props.article.title}
|
||||
description={description}
|
||||
imageUrl={props.article.cover}
|
||||
shareUrl={shareUrl}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import MD from 'markdown-it'
|
||||
import mdfig from 'markdown-it-implicit-figures'
|
||||
import mdmark from 'markdown-it-mark'
|
||||
import mdcustom from 'markdown-it-container'
|
||||
import mdlinks from 'markdown-it-replace-link'
|
||||
import { createMemo } from 'solid-js'
|
||||
|
||||
const mit = MD({
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true
|
||||
})
|
||||
mit.use(mdmark)
|
||||
mit.use(mdcustom)
|
||||
mit.use(mdfig, {
|
||||
dataType: false, // <figure data-type="image">
|
||||
figcaption: true // <figcaption>alternative text</figcaption>
|
||||
})
|
||||
mit.use(mdlinks)
|
||||
|
||||
export default (props: { body: string }) => {
|
||||
const body = createMemo(() => (props.body.startsWith('<') ? props.body : mit.render(props.body)))
|
||||
return <div innerHTML={body()} />
|
||||
}
|
|
@ -1,19 +1,16 @@
|
|||
import { Icon } from '../_shared/Icon'
|
||||
|
||||
import { createSocialShare, TWITTER, VK, FACEBOOK, TELEGRAM } from '@solid-primitives/share'
|
||||
import styles from '../_shared/Popup/Popup.module.scss'
|
||||
import type { PopupProps } from '../_shared/Popup'
|
||||
import { Popup } from '../_shared/Popup'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
|
||||
import { createEffect, createSignal } from 'solid-js'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
|
||||
import { Popup } from '../_shared/Popup'
|
||||
import { ShareLinks } from '../_shared/ShareLinks'
|
||||
|
||||
type SharePopupProps = {
|
||||
title: string
|
||||
shareUrl?: string
|
||||
shareUrl: string
|
||||
imageUrl: string
|
||||
description: string
|
||||
isVisible?: (value: boolean) => void
|
||||
onVisibilityChange?: (value: boolean) => void
|
||||
} & Omit<PopupProps, 'children'>
|
||||
|
||||
export const getShareUrl = (params: { pathname?: string } = {}) => {
|
||||
|
@ -23,63 +20,22 @@ export const getShareUrl = (params: { pathname?: string } = {}) => {
|
|||
}
|
||||
|
||||
export const SharePopup = (props: SharePopupProps) => {
|
||||
const { t } = useLocalize()
|
||||
const [isVisible, setIsVisible] = createSignal(false)
|
||||
const {
|
||||
actions: { showSnackbar }
|
||||
} = useSnackbar()
|
||||
|
||||
createEffect(() => {
|
||||
if (props.isVisible) {
|
||||
props.isVisible(isVisible())
|
||||
if (props.onVisibilityChange) {
|
||||
props.onVisibilityChange(isVisible())
|
||||
}
|
||||
})
|
||||
|
||||
const [share] = createSocialShare(() => ({
|
||||
title: props.title,
|
||||
url: props.shareUrl,
|
||||
description: props.description
|
||||
}))
|
||||
|
||||
const copyLink = async () => {
|
||||
await navigator.clipboard.writeText(props.shareUrl)
|
||||
showSnackbar({ body: t('Link copied') })
|
||||
}
|
||||
|
||||
return (
|
||||
<Popup {...props} variant="bordered" onVisibilityChange={(value) => setIsVisible(value)}>
|
||||
<ul class="nodash">
|
||||
<li>
|
||||
<button role="button" class={styles.shareControl} onClick={() => share(VK)}>
|
||||
<Icon name="vk-white" class={styles.icon} />
|
||||
VK
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button role="button" class={styles.shareControl} onClick={() => share(FACEBOOK)}>
|
||||
<Icon name="facebook-white" class={styles.icon} />
|
||||
Facebook
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button role="button" class={styles.shareControl} onClick={() => share(TWITTER)}>
|
||||
<Icon name="twitter-white" class={styles.icon} />
|
||||
Twitter
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button role="button" class={styles.shareControl} onClick={() => share(TELEGRAM)}>
|
||||
<Icon name="telegram-white" class={styles.icon} />
|
||||
Telegram
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button role="button" class={styles.shareControl} onClick={copyLink}>
|
||||
<Icon name="link-white" class={styles.icon} />
|
||||
{t('Copy link')}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<ShareLinks
|
||||
variant="inPopup"
|
||||
title={props.title}
|
||||
shareUrl={props.shareUrl}
|
||||
imageUrl={props.imageUrl}
|
||||
description={props.description}
|
||||
/>
|
||||
</Popup>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { clsx } from 'clsx'
|
||||
import { createMemo, Show } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { useSession } from '../../context/session'
|
||||
import { ReactionKind, Shout } from '../../graphql/types.gen'
|
||||
import { loadShout } from '../../stores/zine/articles'
|
||||
import { useSession } from '../../context/session'
|
||||
import { useReactions } from '../../context/reactions'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { Popup } from '../_shared/Popup'
|
||||
import { VotersList } from '../_shared/VotersList'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
|
||||
import styles from './ShoutRatingControl.module.scss'
|
||||
|
||||
interface ShoutRatingControlProps {
|
||||
|
@ -19,12 +21,12 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
const { t } = useLocalize()
|
||||
const {
|
||||
user,
|
||||
actions: { requireAuthentication }
|
||||
actions: { requireAuthentication },
|
||||
} = useSession()
|
||||
|
||||
const {
|
||||
reactionEntities,
|
||||
actions: { createReaction, deleteReaction, loadReactionsBy }
|
||||
actions: { createReaction, deleteReaction, loadReactionsBy },
|
||||
} = useReactions()
|
||||
|
||||
const checkReaction = (reactionKind: ReactionKind) =>
|
||||
|
@ -33,7 +35,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === user()?.slug &&
|
||||
r.shout.id === props.shout.id &&
|
||||
!r.replyTo
|
||||
!r.replyTo,
|
||||
)
|
||||
|
||||
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||
|
@ -45,8 +47,8 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
(r) =>
|
||||
[ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) &&
|
||||
r.shout.id === props.shout.id &&
|
||||
!r.replyTo
|
||||
)
|
||||
!r.replyTo,
|
||||
),
|
||||
)
|
||||
|
||||
const deleteShoutReaction = async (reactionKind: ReactionKind) => {
|
||||
|
@ -55,7 +57,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
r.kind === reactionKind &&
|
||||
r.createdBy.slug === user()?.slug &&
|
||||
r.shout.id === props.shout.id &&
|
||||
!r.replyTo
|
||||
!r.replyTo,
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
@ -69,13 +71,13 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
} else {
|
||||
await createReaction({
|
||||
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
||||
shout: props.shout.id
|
||||
shout: props.shout.id,
|
||||
})
|
||||
}
|
||||
|
||||
loadShout(props.shout.slug)
|
||||
loadReactionsBy({
|
||||
by: { shout: props.shout.slug }
|
||||
by: { shout: props.shout.slug },
|
||||
})
|
||||
}, 'vote')
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { createEffect, JSX, Show } from 'solid-js'
|
||||
|
||||
import { useSession } from '../../context/session'
|
||||
import { hideModal } from '../../stores/ui'
|
||||
import { useRouter } from '../../stores/router'
|
||||
import { RootSearchParams } from '../../pages/types'
|
||||
import { useRouter } from '../../stores/router'
|
||||
import { hideModal } from '../../stores/ui'
|
||||
import { AuthModalSearchParams } from '../Nav/AuthModal/types'
|
||||
|
||||
type Props = {
|
||||
|
@ -12,7 +13,7 @@ type Props = {
|
|||
|
||||
export const AuthGuard = (props: Props) => {
|
||||
const { isAuthenticated, isSessionLoaded } = useSession()
|
||||
const { changeSearchParam } = useRouter<RootSearchParams & AuthModalSearchParams>()
|
||||
const { changeSearchParams } = useRouter<RootSearchParams & AuthModalSearchParams>()
|
||||
|
||||
createEffect(() => {
|
||||
if (props.disabled) {
|
||||
|
@ -22,12 +23,12 @@ export const AuthGuard = (props: Props) => {
|
|||
if (isAuthenticated()) {
|
||||
hideModal()
|
||||
} else {
|
||||
changeSearchParam(
|
||||
changeSearchParams(
|
||||
{
|
||||
source: 'authguard',
|
||||
modal: 'auth'
|
||||
modal: 'auth',
|
||||
},
|
||||
true
|
||||
true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
.AuthorLink {
|
||||
.link {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
flex-flow: row nowrap;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
|
@ -33,6 +32,7 @@
|
|||
.link {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
|
@ -43,8 +43,22 @@
|
|||
.link {
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.authorLinkFloorImportant {
|
||||
.link {
|
||||
&:hover {
|
||||
background: #fff !important;
|
||||
|
||||
.name {
|
||||
background: none;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
import { clsx } from 'clsx'
|
||||
import styles from './AhtorLink.module.scss'
|
||||
|
||||
import { Author } from '../../../graphql/types.gen'
|
||||
import { Userpic } from '../Userpic'
|
||||
|
||||
import styles from './AhtorLink.module.scss'
|
||||
|
||||
type Props = {
|
||||
author: Author
|
||||
size?: 'XS' | 'M' | 'L'
|
||||
class?: string
|
||||
isFloorImportant?: boolean
|
||||
}
|
||||
|
||||
export const AuthorLink = (props: Props) => {
|
||||
return (
|
||||
<div class={clsx(styles.AuthorLink, props.class, styles[props.size ?? 'M'])}>
|
||||
<div
|
||||
class={clsx(styles.AuthorLink, props.class, styles[props.size ?? 'M'], {
|
||||
[styles.authorLinkFloorImportant]: props.isFloorImportant,
|
||||
})}
|
||||
>
|
||||
<a class={styles.link} href={`/author/${props.author.slug}`}>
|
||||
<Userpic size={props.size ?? 'M'} name={props.author.name} userpic={props.author.userpic} />
|
||||
<div class={styles.name}>{props.author.name}</div>
|
||||
|
|
|
@ -1,27 +1,40 @@
|
|||
.AuthorBadge {
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
margin-bottom: 2rem;
|
||||
gap: 1rem;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 3rem;
|
||||
&.nameOnly {
|
||||
align-items: center;
|
||||
|
||||
.info {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.basicInfo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex: 0 calc(100% - 5.2rem);
|
||||
gap: 1rem;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
flex: 0 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
@include font-size(1.4rem);
|
||||
|
||||
border: none;
|
||||
display: flex;
|
||||
flex: 0 calc(100% - 5.2rem);
|
||||
flex-direction: column;
|
||||
line-height: 1.3;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
flex: 1 100%;
|
||||
|
@ -43,6 +56,7 @@
|
|||
|
||||
.bio {
|
||||
color: var(--black-400);
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +67,10 @@
|
|||
margin-left: 5.2rem;
|
||||
gap: 1rem;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
@ -72,11 +90,26 @@
|
|||
|
||||
&.iconed {
|
||||
padding: 6px !important;
|
||||
min-width: 32px;
|
||||
min-width: 4rem;
|
||||
width: unset;
|
||||
|
||||
&:hover img {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.actionButtonLabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.actionButtonLabelHovered {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.actionButtonLabelHovered {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,46 @@
|
|||
import { clsx } from 'clsx'
|
||||
import styles from './AuthorBadge.module.scss'
|
||||
import { Userpic } from '../Userpic'
|
||||
import { Author, FollowingEntity } from '../../../graphql/types.gen'
|
||||
import { createMemo, createSignal, Match, Show, Switch } from 'solid-js'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Button } from '../../_shared/Button'
|
||||
import { useSession } from '../../../context/session'
|
||||
import { follow, unfollow } from '../../../stores/zine/common'
|
||||
import { CheckButton } from '../../_shared/CheckButton'
|
||||
import { openPage } from '@nanostores/router'
|
||||
import { clsx } from 'clsx'
|
||||
import { createEffect, createMemo, createSignal, Match, Show, Switch } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { useMediaQuery } from '../../../context/mediaQuery'
|
||||
import { useSession } from '../../../context/session'
|
||||
import { Author, FollowingEntity } from '../../../graphql/types.gen'
|
||||
import { router, useRouter } from '../../../stores/router'
|
||||
import { follow, unfollow } from '../../../stores/zine/common'
|
||||
import { Button } from '../../_shared/Button'
|
||||
import { CheckButton } from '../../_shared/CheckButton'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Userpic } from '../Userpic'
|
||||
|
||||
import styles from './AuthorBadge.module.scss'
|
||||
import stylesButton from '../../_shared/Button/Button.module.scss'
|
||||
|
||||
type Props = {
|
||||
author: Author
|
||||
minimizeSubscribeButton?: boolean
|
||||
showMessageButton?: boolean
|
||||
iconButtons?: boolean
|
||||
nameOnly?: boolean
|
||||
}
|
||||
export const AuthorBadge = (props: Props) => {
|
||||
const { mediaMatches } = useMediaQuery()
|
||||
const [isMobileView, setIsMobileView] = createSignal(false)
|
||||
const [isSubscribing, setIsSubscribing] = createSignal(false)
|
||||
|
||||
createEffect(() => {
|
||||
setIsMobileView(!mediaMatches.sm)
|
||||
})
|
||||
|
||||
const {
|
||||
session,
|
||||
subscriptions,
|
||||
actions: { loadSubscriptions, requireAuthentication }
|
||||
actions: { loadSubscriptions, requireAuthentication },
|
||||
} = useSession()
|
||||
const { changeSearchParam } = useRouter()
|
||||
const { changeSearchParams } = useRouter()
|
||||
const { t, formatDate } = useLocalize()
|
||||
const subscribed = createMemo(() =>
|
||||
subscriptions().authors.some((author) => author.slug === props.author.slug)
|
||||
subscriptions().authors.some((author) => author.slug === props.author.slug),
|
||||
)
|
||||
|
||||
const subscribe = async (really = true) => {
|
||||
|
@ -50,23 +62,18 @@ export const AuthorBadge = (props: Props) => {
|
|||
const initChat = () => {
|
||||
requireAuthentication(() => {
|
||||
openPage(router, `inbox`)
|
||||
changeSearchParam({
|
||||
initChat: props.author.id.toString()
|
||||
changeSearchParams({
|
||||
initChat: props.author.id.toString(),
|
||||
})
|
||||
}, 'discussions')
|
||||
}
|
||||
const subscribeValue = createMemo(() => {
|
||||
if (props.iconButtons) {
|
||||
return <Icon name="author-subscribe" />
|
||||
}
|
||||
return isSubscribing() ? t('...subscribing') : t('Subscribe')
|
||||
})
|
||||
|
||||
return (
|
||||
<div class={clsx(styles.AuthorBadge)}>
|
||||
<div class={clsx(styles.AuthorBadge, { [styles.nameOnly]: props.nameOnly })}>
|
||||
<div class={styles.basicInfo}>
|
||||
<Userpic
|
||||
hasLink={true}
|
||||
size={'M'}
|
||||
size={isMobileView() ? 'M' : 'L'}
|
||||
name={props.author.name}
|
||||
userpic={props.author.userpic}
|
||||
slug={props.author.slug}
|
||||
|
@ -75,6 +82,7 @@ export const AuthorBadge = (props: Props) => {
|
|||
<div class={styles.name}>
|
||||
<span>{props.author.name}</span>
|
||||
</div>
|
||||
<Show when={!props.nameOnly}>
|
||||
<Switch
|
||||
fallback={
|
||||
<div class={styles.bio}>
|
||||
|
@ -91,8 +99,10 @@ export const AuthorBadge = (props: Props) => {
|
|||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Show>
|
||||
</a>
|
||||
<Show when={props.author.slug !== session()?.user.slug}>
|
||||
</div>
|
||||
<Show when={props.author.slug !== session()?.user.slug && !props.nameOnly}>
|
||||
<div class={styles.actions}>
|
||||
<Show
|
||||
when={!props.minimizeSubscribeButton}
|
||||
|
@ -110,18 +120,49 @@ export const AuthorBadge = (props: Props) => {
|
|||
<Button
|
||||
variant={props.iconButtons ? 'secondary' : 'bordered'}
|
||||
size="S"
|
||||
value={subscribeValue()}
|
||||
value={
|
||||
<Show
|
||||
when={props.iconButtons}
|
||||
fallback={
|
||||
<Show when={isSubscribing()} fallback={t('Subscribe')}>
|
||||
{t('subscribing...')}
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<Icon name="author-subscribe" class={stylesButton.icon} />
|
||||
</Show>
|
||||
}
|
||||
onClick={() => handleSubscribe(true)}
|
||||
class={clsx(styles.actionButton, { [styles.iconed]: props.iconButtons })}
|
||||
isSubscribeButton={true}
|
||||
class={clsx(styles.actionButton, {
|
||||
[styles.iconed]: props.iconButtons,
|
||||
[stylesButton.subscribed]: subscribed(),
|
||||
})}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant={props.iconButtons ? 'secondary' : 'bordered'}
|
||||
size="S"
|
||||
value={props.iconButtons ? <Icon name="author-unsubscribe" /> : t('Following')}
|
||||
value={
|
||||
<Show
|
||||
when={props.iconButtons}
|
||||
fallback={
|
||||
<>
|
||||
<span class={styles.actionButtonLabel}>{t('Following')}</span>
|
||||
<span class={styles.actionButtonLabelHovered}>{t('Unfollow')}</span>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Icon name="author-unsubscribe" class={stylesButton.icon} />
|
||||
</Show>
|
||||
}
|
||||
onClick={() => handleSubscribe(false)}
|
||||
class={clsx(styles.actionButton, { [styles.iconed]: props.iconButtons })}
|
||||
isSubscribeButton={true}
|
||||
class={clsx(styles.actionButton, {
|
||||
[styles.iconed]: props.iconButtons,
|
||||
[stylesButton.subscribed]: subscribed(),
|
||||
})}
|
||||
/>
|
||||
</Show>
|
||||
</Show>
|
||||
|
|
|
@ -43,8 +43,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
.authorActionsLabel {
|
||||
@include media-breakpoint-down(sm) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.authorActionsLabelMobile {
|
||||
display: none;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.authorDetails {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
flex: 1 100%;
|
||||
|
@ -77,37 +92,10 @@
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.buttonSubscribe {
|
||||
aspect-ratio: auto;
|
||||
background-color: #000;
|
||||
border: 1px solid #000;
|
||||
.buttonWriteMessage {
|
||||
border-radius: 0.8rem;
|
||||
color: #fff;
|
||||
float: none;
|
||||
padding-bottom: 0.6rem;
|
||||
padding-top: 0.6rem;
|
||||
width: 10em;
|
||||
|
||||
.icon {
|
||||
margin-right: 0.5em;
|
||||
|
||||
img {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
|
||||
.icon img {
|
||||
filter: invert(0);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +162,7 @@
|
|||
.authorSubscribeSocial {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin: 2rem 0;
|
||||
margin: 0.5rem 0 2rem -0.4rem;
|
||||
|
||||
.socialLink {
|
||||
border: none;
|
||||
|
@ -243,7 +231,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
&[href*='telegram.com/'] {
|
||||
&[href*='telegram.com/'],
|
||||
&[href*='t.me/'] {
|
||||
&::before {
|
||||
background-image: url(/icons/user-link-telegram.svg);
|
||||
}
|
||||
|
@ -429,7 +418,6 @@
|
|||
@include media-breakpoint-down(sm) {
|
||||
flex: 1 100%;
|
||||
justify-content: center;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
|
@ -446,7 +434,6 @@
|
|||
flex-wrap: wrap;
|
||||
font-size: 1.4rem;
|
||||
margin-top: 1.5rem;
|
||||
gap: 1rem;
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
justify-content: center;
|
||||
|
@ -457,10 +444,18 @@
|
|||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
margin-right: 3rem;
|
||||
margin: 0 2% 1rem;
|
||||
vertical-align: top;
|
||||
border-bottom: unset !important;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.subscribersItem {
|
||||
position: relative;
|
||||
|
||||
|
|
|
@ -1,37 +1,40 @@
|
|||
import type { Author } from '../../../graphql/types.gen'
|
||||
import { Userpic } from '../Userpic'
|
||||
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
||||
import { translit } from '../../../utils/ru2en'
|
||||
import { follow, unfollow } from '../../../stores/zine/common'
|
||||
import { clsx } from 'clsx'
|
||||
import { useSession } from '../../../context/session'
|
||||
import { ShowOnlyOnClient } from '../../_shared/ShowOnlyOnClient'
|
||||
import { FollowingEntity, Topic } from '../../../graphql/types.gen'
|
||||
import { router, useRouter } from '../../../stores/router'
|
||||
|
||||
import { openPage, redirectPage } from '@nanostores/router'
|
||||
import { clsx } from 'clsx'
|
||||
import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Modal } from '../../Nav/Modal'
|
||||
import { useSession } from '../../../context/session'
|
||||
import { FollowingEntity, Topic } from '../../../graphql/types.gen'
|
||||
import { SubscriptionFilter } from '../../../pages/types'
|
||||
import { router, useRouter } from '../../../stores/router'
|
||||
import { follow, unfollow } from '../../../stores/zine/common'
|
||||
import { isAuthor } from '../../../utils/isAuthor'
|
||||
import { AuthorBadge } from '../AuthorBadge'
|
||||
import { TopicBadge } from '../../Topic/TopicBadge'
|
||||
import { translit } from '../../../utils/ru2en'
|
||||
import { Button } from '../../_shared/Button'
|
||||
import { ShowOnlyOnClient } from '../../_shared/ShowOnlyOnClient'
|
||||
import { getShareUrl, SharePopup } from '../../Article/SharePopup'
|
||||
import { Modal } from '../../Nav/Modal'
|
||||
import { TopicBadge } from '../../Topic/TopicBadge'
|
||||
import { AuthorBadge } from '../AuthorBadge'
|
||||
import { Userpic } from '../Userpic'
|
||||
|
||||
import styles from './AuthorCard.module.scss'
|
||||
import stylesButton from '../../_shared/Button/Button.module.scss'
|
||||
|
||||
type Props = {
|
||||
author: Author
|
||||
followers?: Author[]
|
||||
following?: Array<Author | Topic>
|
||||
}
|
||||
|
||||
export const AuthorCard = (props: Props) => {
|
||||
const { t, lang } = useLocalize()
|
||||
const {
|
||||
session,
|
||||
subscriptions,
|
||||
isSessionLoaded,
|
||||
actions: { loadSubscriptions, requireAuthentication }
|
||||
actions: { loadSubscriptions, requireAuthentication },
|
||||
} = useSession()
|
||||
|
||||
const [isSubscribing, setIsSubscribing] = createSignal(false)
|
||||
|
@ -39,7 +42,7 @@ export const AuthorCard = (props: Props) => {
|
|||
const [subscriptionFilter, setSubscriptionFilter] = createSignal<SubscriptionFilter>('all')
|
||||
|
||||
const subscribed = createMemo<boolean>(() =>
|
||||
subscriptions().authors.some((author) => author.slug === props.author.slug)
|
||||
subscriptions().authors.some((author) => author.slug === props.author.slug),
|
||||
)
|
||||
|
||||
const subscribe = async (really = true) => {
|
||||
|
@ -68,12 +71,12 @@ export const AuthorCard = (props: Props) => {
|
|||
})
|
||||
|
||||
// TODO: reimplement AuthorCard
|
||||
const { changeSearchParam } = useRouter()
|
||||
const { changeSearchParams } = useRouter()
|
||||
const initChat = () => {
|
||||
requireAuthentication(() => {
|
||||
openPage(router, `inbox`)
|
||||
changeSearchParam({
|
||||
initChat: props.author.id.toString()
|
||||
changeSearchParams({
|
||||
initChat: props.author.id.toString(),
|
||||
})
|
||||
}, 'discussions')
|
||||
}
|
||||
|
@ -96,13 +99,23 @@ export const AuthorCard = (props: Props) => {
|
|||
}
|
||||
})
|
||||
|
||||
const followButtonText = () => {
|
||||
const followButtonText = createMemo(() => {
|
||||
if (isSubscribing()) {
|
||||
return t('...subscribing')
|
||||
return t('subscribing...')
|
||||
}
|
||||
return t(subscribed() ? 'Unfollow' : 'Follow')
|
||||
|
||||
if (subscribed()) {
|
||||
return (
|
||||
<>
|
||||
<span class={stylesButton.buttonSubscribeLabel}>{t('Following')}</span>
|
||||
<span class={stylesButton.buttonSubscribeLabelHovered}>{t('Unfollow')}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return t('Follow')
|
||||
})
|
||||
|
||||
return (
|
||||
<div class={clsx(styles.author, 'row')}>
|
||||
<div class="col-md-5">
|
||||
|
@ -117,7 +130,9 @@ export const AuthorCard = (props: Props) => {
|
|||
<div class={clsx('col-md-15 col-xl-13', styles.authorDetails)}>
|
||||
<div class={styles.authorDetailsWrapper}>
|
||||
<div class={styles.authorName}>{name()}</div>
|
||||
<Show when={props.author.bio}>
|
||||
<div class={styles.authorAbout} innerHTML={props.author.bio} />
|
||||
</Show>
|
||||
<Show
|
||||
when={
|
||||
(props.followers && props.followers.length > 0) ||
|
||||
|
@ -205,13 +220,16 @@ export const AuthorCard = (props: Props) => {
|
|||
<Button
|
||||
onClick={handleSubscribe}
|
||||
value={followButtonText()}
|
||||
class={styles.buttonSubscribe}
|
||||
isSubscribeButton={true}
|
||||
class={clsx({
|
||||
[stylesButton.subscribed]: subscribed(),
|
||||
})}
|
||||
/>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
value={t('Message')}
|
||||
onClick={initChat}
|
||||
class={styles.buttonSubscribe}
|
||||
class={styles.buttonWriteMessage}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
@ -220,7 +238,12 @@ export const AuthorCard = (props: Props) => {
|
|||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => redirectPage(router, 'profileSettings')}
|
||||
value={t('Edit profile')}
|
||||
value={
|
||||
<>
|
||||
<span class={styles.authorActionsLabel}>{t('Edit profile')}</span>
|
||||
<span class={styles.authorActionsLabelMobile}>{t('Edit')}</span>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<SharePopup
|
||||
title={props.author.name}
|
||||
|
@ -234,7 +257,7 @@ export const AuthorCard = (props: Props) => {
|
|||
</Show>
|
||||
</ShowOnlyOnClient>
|
||||
<Show when={props.followers}>
|
||||
<Modal variant="medium" name="followers" maxHeight>
|
||||
<Modal variant="medium" isResponsive={true} name="followers" maxHeight>
|
||||
<>
|
||||
<h2>{t('Followers')}</h2>
|
||||
<div class={styles.listWrapper}>
|
||||
|
@ -250,7 +273,7 @@ export const AuthorCard = (props: Props) => {
|
|||
</Modal>
|
||||
</Show>
|
||||
<Show when={props.following}>
|
||||
<Modal variant="medium" name="following" maxHeight>
|
||||
<Modal variant="medium" isResponsive={true} name="following" maxHeight>
|
||||
<>
|
||||
<h2>{t('Subscriptions')}</h2>
|
||||
<ul class="view-switcher">
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import styles from './AuthorRatingControl.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import type { Author } from '../../graphql/types.gen'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import styles from './AuthorRatingControl.module.scss'
|
||||
|
||||
interface AuthorRatingControlProps {
|
||||
author: Author
|
||||
class?: string
|
||||
|
@ -20,7 +22,7 @@ export const AuthorRatingControl = (props: AuthorRatingControlProps) => {
|
|||
<div
|
||||
class={clsx(styles.rating, props.class, {
|
||||
[styles.isUpvoted]: isUpvoted,
|
||||
[styles.isDownvoted]: isDownvoted
|
||||
[styles.isDownvoted]: isDownvoted,
|
||||
})}
|
||||
>
|
||||
<button
|
||||
|
|
|
@ -75,6 +75,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.L {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
min-width: 40px;
|
||||
|
||||
.letters {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&.XL {
|
||||
aspect-ratio: 1/1;
|
||||
margin: 0 auto 1rem;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { createMemo, Show } from 'solid-js'
|
||||
import styles from './Userpic.module.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import { createMemo, Show } from 'solid-js'
|
||||
|
||||
import { ConditionalWrapper } from '../../_shared/ConditionalWrapper'
|
||||
import { Loading } from '../../_shared/Loading'
|
||||
import { Image } from '../../_shared/Image'
|
||||
import { Loading } from '../../_shared/Loading'
|
||||
|
||||
import styles from './Userpic.module.scss'
|
||||
|
||||
type Props = {
|
||||
name: string
|
||||
|
@ -46,7 +48,7 @@ export const Userpic = (props: Props) => {
|
|||
return (
|
||||
<div
|
||||
class={clsx(styles.Userpic, props.class, styles[props.size ?? 'M'], {
|
||||
['cursorPointer']: props.onClick
|
||||
['cursorPointer']: props.onClick,
|
||||
})}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
|
@ -55,7 +57,7 @@ export const Userpic = (props: Props) => {
|
|||
condition={props.hasLink}
|
||||
wrapper={(children) => <a href={`/author/${props.slug}`}>{children}</a>}
|
||||
>
|
||||
<Show when={props.userpic} fallback={<div class={styles.letters}>{letters()}</div>}>
|
||||
<Show keyed={true} when={props.userpic} fallback={<div class={styles.letters}>{letters()}</div>}>
|
||||
<Image src={props.userpic} width={avatarSize()} height={avatarSize()} alt={props.name} />
|
||||
</Show>
|
||||
</ConditionalWrapper>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import styles from './Banner.module.scss'
|
||||
|
||||
import { showModal } from '../../stores/ui'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { showModal } from '../../stores/ui'
|
||||
import { Image } from '../_shared/Image'
|
||||
|
||||
import styles from './Banner.module.scss'
|
||||
|
||||
export default () => {
|
||||
const { t } = useLocalize()
|
||||
|
@ -21,7 +23,11 @@ export default () => {
|
|||
</p>
|
||||
</div>
|
||||
<div class={clsx(styles.discoursBannerImage, 'col-lg-12 offset-lg-2')}>
|
||||
<img src="/discours-banner.jpg" alt={t('Discours')} />
|
||||
<Image
|
||||
src="https://images.discours.io/unsafe/production/image/discours-banner.jpg"
|
||||
alt={t('Discours')}
|
||||
width={600}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.donate-form input,
|
||||
.donate-form label,
|
||||
.donate-form .btn {
|
||||
.donateForm input,
|
||||
.donateForm label,
|
||||
.donateForm .btn {
|
||||
font-family: Muller, Arial, Helvetica, sans-serif;
|
||||
border: solid 1px #595959;
|
||||
border-radius: 3px;
|
||||
|
@ -10,7 +10,7 @@
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.donate-form input {
|
||||
.donateForm input {
|
||||
&::-webkit-outer-spin-button,
|
||||
&::-webkit-inner-spin-button {
|
||||
appearance: none;
|
||||
|
@ -48,12 +48,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
.donateForm .btn {
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
padding: 5px 10px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
transform: none !important;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
&:last-of-type {
|
||||
|
@ -62,7 +63,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.btnGroup {
|
||||
input {
|
||||
&:first-child + .btn {
|
||||
border-top-right-radius: 0;
|
||||
|
@ -75,12 +76,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.payment-type {
|
||||
.paymentType {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.donate-buttons-container {
|
||||
.donateButtonsContainer {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
justify-content: space-between;
|
||||
|
@ -111,59 +113,34 @@
|
|||
}
|
||||
}
|
||||
|
||||
.donate-input {
|
||||
.donateInput {
|
||||
@include media-breakpoint-down(sm) {
|
||||
flex: 1 100%;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
border: 1px solid #000;
|
||||
.sendBtn {
|
||||
border: 2px solid #000;
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
color: #fff !important;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
line-height: 1.8;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: #fff !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-choose {
|
||||
.paymentChoose {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.form-group:not(:first-child) {
|
||||
.formGroup:not(:first-child) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.discours-help .modalwrap__inner {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
.payment-form {
|
||||
padding: 0 !important;
|
||||
|
||||
.button {
|
||||
display: block;
|
||||
padding-bottom: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.delimiter-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.delimiter {
|
||||
left: 100%;
|
||||
line-height: 1;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translate(-50%, calc(-50% - 0.8rem));
|
||||
}
|
||||
*/
|
|
@ -1,8 +1,11 @@
|
|||
import '../../styles/help.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import { createSignal, onMount } from 'solid-js'
|
||||
import { showModal } from '../../stores/ui'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
import { showModal } from '../../stores/ui'
|
||||
|
||||
import styles from './Donate.module.scss'
|
||||
|
||||
export const Donate = () => {
|
||||
const { t } = useLocalize()
|
||||
|
@ -11,7 +14,7 @@ export const Donate = () => {
|
|||
const cpOptions = {
|
||||
publicId: 'pk_0a37bab30ffc6b77b2f93d65f2aed',
|
||||
description: t('Help discours to grow'),
|
||||
currency: 'RUB'
|
||||
currency: 'RUB',
|
||||
}
|
||||
|
||||
let amountSwitchElement: HTMLDivElement | undefined
|
||||
|
@ -22,13 +25,13 @@ export const Donate = () => {
|
|||
const [period, setPeriod] = createSignal(monthly)
|
||||
const [amount, setAmount] = createSignal(0)
|
||||
const {
|
||||
actions: { showSnackbar }
|
||||
actions: { showSnackbar },
|
||||
} = useSnackbar()
|
||||
|
||||
const initiated = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const {
|
||||
cp: { CloudPayments }
|
||||
cp: { CloudPayments },
|
||||
} = window as any // Checkout(cpOptions)
|
||||
setWidget(new CloudPayments())
|
||||
console.log('[donate] payments initiated')
|
||||
|
@ -42,8 +45,8 @@ export const Donate = () => {
|
|||
amount: amount() || 0, //сумма
|
||||
vat: 20, //ставка НДС
|
||||
method: 0, // тег-1214 признак способа расчета - признак способа расчета
|
||||
object: 0 // тег-1212 признак предмета расчета - признак предмета товара, работы, услуги, платежа, выплаты, иного предмета расчета
|
||||
}
|
||||
object: 0, // тег-1212 признак предмета расчета - признак предмета товара, работы, услуги, платежа, выплаты, иного предмета расчета
|
||||
},
|
||||
],
|
||||
// taxationSystem: 0, //система налогообложения; необязательный, если у вас одна система налогообложения
|
||||
// email: 'user@example.com', //e-mail покупателя, если нужно отправить письмо с чеком
|
||||
|
@ -53,8 +56,8 @@ export const Donate = () => {
|
|||
electronic: amount(), // Сумма оплаты электронными деньгами
|
||||
advancePayment: 0, // Сумма из предоплаты (зачетом аванса) (2 знака после запятой)
|
||||
credit: 0, // Сумма постоплатой(в кредит) (2 знака после запятой)
|
||||
provision: 0 // Сумма оплаты встречным предоставлением (сертификаты, др. мат.ценности) (2 знака после запятой)
|
||||
}
|
||||
provision: 0, // Сумма оплаты встречным предоставлением (сертификаты, др. мат.ценности) (2 знака после запятой)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -93,10 +96,10 @@ export const Donate = () => {
|
|||
recurrent: {
|
||||
interval: period(), // local solid's signal
|
||||
period: 1, // internal widget's
|
||||
CustomerReciept: customerReciept() // чек для регулярных платежей
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomerReciept: customerReciept(), // чек для регулярных платежей
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
(opts) => {
|
||||
// success
|
||||
|
@ -111,34 +114,34 @@ export const Donate = () => {
|
|||
|
||||
showSnackbar({
|
||||
type: 'error',
|
||||
body: reason
|
||||
body: reason,
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<form class="discours-form donate-form" action="" method="post">
|
||||
<form class={styles.donateForm} action="" method="post">
|
||||
<input type="hidden" name="shopId" value="156465" />
|
||||
<input value="148805" name="scid" type="hidden" />
|
||||
<input value="0" name="customerNumber" type="hidden" />
|
||||
|
||||
<div class="form-group">
|
||||
<div class="donate-buttons-container" ref={amountSwitchElement}>
|
||||
<div class={styles.formGroup}>
|
||||
<div class={styles.donateButtonsContainer} ref={amountSwitchElement}>
|
||||
<input type="radio" name="amount" id="fix250" value="250" />
|
||||
<label for="fix250" class="btn donate-value-radio">
|
||||
<label for="fix250" class={styles.btn}>
|
||||
250 ₽
|
||||
</label>
|
||||
<input type="radio" name="amount" id="fix500" value="500" checked />
|
||||
<label for="fix500" class="btn donate-value-radio">
|
||||
<label for="fix500" class={styles.btn}>
|
||||
500 ₽
|
||||
</label>
|
||||
<input type="radio" name="amount" id="fix1000" value="1000" />
|
||||
<label for="fix1000" class="btn donate-value-radio">
|
||||
<label for="fix1000" class={styles.btn}>
|
||||
1000 ₽
|
||||
</label>
|
||||
<input
|
||||
class="form-control donate-input"
|
||||
class={styles.donateInput}
|
||||
required
|
||||
ref={customAmountElement}
|
||||
type="number"
|
||||
|
@ -148,8 +151,8 @@ export const Donate = () => {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="payment-type" classList={{ showing: showingPayment() }}>
|
||||
<div class="btn-group payment-choose" data-toggle="buttons">
|
||||
<div class={styles.formGroup} id="payment-type" classList={{ showing: showingPayment() }}>
|
||||
<div class={clsx(styles.btnGroup, styles.paymentChoose)} data-toggle="buttons">
|
||||
<input
|
||||
type="radio"
|
||||
autocomplete="off"
|
||||
|
@ -158,7 +161,11 @@ export const Donate = () => {
|
|||
onClick={() => setPeriod(once)}
|
||||
checked={period() === once}
|
||||
/>
|
||||
<label for="once" class="btn payment-type" classList={{ active: period() === once }}>
|
||||
<label
|
||||
for="once"
|
||||
class={clsx(styles.btn, styles.paymentType)}
|
||||
classList={{ active: period() === once }}
|
||||
>
|
||||
{t('One time')}
|
||||
</label>
|
||||
<input
|
||||
|
@ -169,16 +176,20 @@ export const Donate = () => {
|
|||
onClick={() => setPeriod(monthly)}
|
||||
checked={period() === monthly}
|
||||
/>
|
||||
<label for="monthly" class="btn payment-type" classList={{ active: period() === monthly }}>
|
||||
<label
|
||||
for="monthly"
|
||||
class={clsx(styles.btn, styles.paymentType)}
|
||||
classList={{ active: period() === monthly }}
|
||||
>
|
||||
{t('Every month')}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<a href={''} class="btn send-btn donate" onClick={show}>
|
||||
<div class={styles.formGroup}>
|
||||
<button type="button" class={clsx(styles.btn, styles.sendBtn)} onClick={show}>
|
||||
{t('Help discours to grow')}
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { hideModal } from '../../stores/ui'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { hideModal } from '../../stores/ui'
|
||||
import { Button } from '../_shared/Button'
|
||||
|
||||
export const Feedback = () => {
|
||||
|
@ -14,9 +14,9 @@ export const Feedback = () => {
|
|||
method,
|
||||
headers: {
|
||||
accept: 'application/json',
|
||||
'content-type': 'application/json; charset=utf-8'
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
},
|
||||
body: JSON.stringify({ contact: contactElement?.value, message: msgElement?.textContent })
|
||||
body: JSON.stringify({ contact: contactElement?.value, message: msgElement?.textContent }),
|
||||
})
|
||||
hideModal()
|
||||
}
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
|
||||
a {
|
||||
color: rgb(255 255 255 / 64%);
|
||||
transition: color 0.3s, background-color 0.3s;
|
||||
transition:
|
||||
color 0.3s,
|
||||
background-color 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
|
@ -66,6 +68,7 @@
|
|||
}
|
||||
|
||||
.footerCopyrightSocial {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
.icon {
|
||||
|
@ -94,6 +97,15 @@
|
|||
margin-left: 0.3em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
a:link {
|
||||
border: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.socialItemvk {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { clsx } from 'clsx'
|
||||
import { createMemo, For } from 'solid-js'
|
||||
import styles from './Footer.module.scss'
|
||||
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { Subscribe } from '../_shared/Subscribe'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import styles from './Footer.module.scss'
|
||||
|
||||
export const Footer = () => {
|
||||
const { t, lang } = useLocalize()
|
||||
|
@ -16,26 +17,26 @@ export const Footer = () => {
|
|||
header: 'About the project',
|
||||
items: [
|
||||
{
|
||||
title: 'Manifest',
|
||||
slug: '/about/manifest'
|
||||
title: 'Discours Manifest',
|
||||
slug: '/about/manifest',
|
||||
},
|
||||
{
|
||||
title: 'How it works',
|
||||
slug: '/about/guide'
|
||||
slug: '/about/guide',
|
||||
},
|
||||
{
|
||||
title: 'Dogma',
|
||||
slug: '/about/dogma'
|
||||
slug: '/about/dogma',
|
||||
},
|
||||
{
|
||||
title: 'Principles',
|
||||
slug: '/about/principles'
|
||||
slug: '/about/principles',
|
||||
},
|
||||
{
|
||||
title: 'How to write an article',
|
||||
slug: '/how-to-write-a-good-article'
|
||||
}
|
||||
]
|
||||
slug: '/how-to-write-a-good-article',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -43,25 +44,21 @@ export const Footer = () => {
|
|||
items: [
|
||||
{
|
||||
title: 'Suggest an idea',
|
||||
slug: '/connect'
|
||||
slug: '/connect',
|
||||
},
|
||||
{
|
||||
title: 'Become an author',
|
||||
slug: '/create'
|
||||
slug: '/create',
|
||||
},
|
||||
{
|
||||
title: 'Support us',
|
||||
slug: '/about/help'
|
||||
},
|
||||
{
|
||||
title: 'Feedback',
|
||||
slug: '/#feedback'
|
||||
title: 'Support Discours',
|
||||
slug: '/about/help',
|
||||
},
|
||||
{
|
||||
title: 'Work with us',
|
||||
slug: 'https://docs.google.com/forms/d/e/1FAIpQLSeNNvIzKlXElJtkPkYiXl-jQjlvsL9u4-kpnoRjz1O8Wo40xQ/viewform'
|
||||
}
|
||||
]
|
||||
slug: 'https://docs.google.com/forms/d/e/1FAIpQLSeNNvIzKlXElJtkPkYiXl-jQjlvsL9u4-kpnoRjz1O8Wo40xQ/viewform',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -69,46 +66,46 @@ export const Footer = () => {
|
|||
items: [
|
||||
{
|
||||
title: 'Authors',
|
||||
slug: '/authors'
|
||||
slug: '/authors',
|
||||
},
|
||||
{
|
||||
title: 'Communities',
|
||||
slug: '/community'
|
||||
slug: '/community',
|
||||
},
|
||||
{
|
||||
title: 'Partners',
|
||||
slug: '/about/partners'
|
||||
slug: '/about/partners',
|
||||
},
|
||||
{
|
||||
title: 'Special projects',
|
||||
slug: '/about/projects'
|
||||
slug: '/about/projects',
|
||||
},
|
||||
{
|
||||
title: changeLangTitle(),
|
||||
slug: changeLangLink(),
|
||||
rel: 'external'
|
||||
}
|
||||
]
|
||||
}
|
||||
rel: 'external',
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const SOCIAL = [
|
||||
{
|
||||
name: 'facebook',
|
||||
href: 'https://facebook.com/discoursio'
|
||||
href: 'https://facebook.com/discoursio',
|
||||
},
|
||||
{
|
||||
name: 'vk',
|
||||
href: 'https://vk.com/discoursio'
|
||||
href: 'https://vk.com/discoursio',
|
||||
},
|
||||
{
|
||||
name: 'twitter',
|
||||
href: 'https://twitter.com/discours_io'
|
||||
href: 'https://twitter.com/discours_io',
|
||||
},
|
||||
{
|
||||
name: 'telegram',
|
||||
href: 'https://t.me/discoursio'
|
||||
}
|
||||
href: 'https://t.me/discoursio',
|
||||
},
|
||||
]
|
||||
return (
|
||||
<footer class={styles.discoursFooter}>
|
||||
|
@ -143,7 +140,7 @@ export const Footer = () => {
|
|||
<div class={clsx(styles.footerCopyright, 'row')}>
|
||||
<div class="col-md-18 col-lg-20">
|
||||
{t(
|
||||
'Independant magazine with an open horizontal cooperation about culture, science and society'
|
||||
'Independant magazine with an open horizontal cooperation about culture, science and society',
|
||||
)}
|
||||
. {t('Discours')} © 2015–{new Date().getFullYear()}{' '}
|
||||
<a href="/about/terms-of-use">{t('Terms of use')}</a>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
.aboutDiscours {
|
||||
@include font-size(1.6rem);
|
||||
|
||||
background: #fef2f2;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6.4rem;
|
||||
|
@ -8,6 +9,7 @@
|
|||
|
||||
h4 {
|
||||
@include font-size(4rem);
|
||||
|
||||
font-weight: bold;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 2rem;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import styles from './Hero.module.scss'
|
||||
|
||||
import { showModal } from '../../stores/ui'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useRouter } from '../../stores/router'
|
||||
import { showModal } from '../../stores/ui'
|
||||
import { AuthModalSearchParams } from '../Nav/AuthModal/types'
|
||||
|
||||
import styles from './Hero.module.scss'
|
||||
|
||||
export default () => {
|
||||
const { t } = useLocalize()
|
||||
const { changeSearchParam } = useRouter<AuthModalSearchParams>()
|
||||
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
|
||||
|
||||
return (
|
||||
<div class={styles.aboutDiscours}>
|
||||
|
@ -17,7 +17,7 @@ export default () => {
|
|||
<h4 innerHTML={t('Horizontal collaborative journalistic platform')} />
|
||||
<p
|
||||
innerHTML={t(
|
||||
'Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects'
|
||||
'Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects',
|
||||
)}
|
||||
/>
|
||||
<div class={styles.aboutDiscoursActions}>
|
||||
|
@ -28,8 +28,8 @@ export default () => {
|
|||
class="button"
|
||||
onClick={() => {
|
||||
showModal('auth')
|
||||
changeSearchParam({
|
||||
mode: 'register'
|
||||
changeSearchParams({
|
||||
mode: 'register',
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { clsx } from 'clsx'
|
||||
import styles from './Draft.module.scss'
|
||||
import type { Shout } from '../../graphql/types.gen'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useConfirm } from '../../context/confirm'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import { clsx } from 'clsx'
|
||||
|
||||
import { useConfirm } from '../../context/confirm'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
import { router } from '../../stores/router'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
|
||||
import styles from './Draft.module.scss'
|
||||
|
||||
type Props = {
|
||||
class?: string
|
||||
|
@ -18,11 +21,11 @@ type Props = {
|
|||
export const Draft = (props: Props) => {
|
||||
const { t, formatDate } = useLocalize()
|
||||
const {
|
||||
actions: { showConfirm }
|
||||
actions: { showConfirm },
|
||||
} = useConfirm()
|
||||
|
||||
const {
|
||||
actions: { showSnackbar }
|
||||
actions: { showSnackbar },
|
||||
} = useSnackbar()
|
||||
|
||||
const handlePublishLinkClick = (e) => {
|
||||
|
@ -37,7 +40,7 @@ export const Draft = (props: Props) => {
|
|||
confirmBody: t('Are you sure you want to delete this draft?'),
|
||||
confirmButtonLabel: t('Delete'),
|
||||
confirmButtonVariant: 'danger',
|
||||
declineButtonVariant: 'primary'
|
||||
declineButtonVariant: 'primary',
|
||||
})
|
||||
if (isConfirmed) {
|
||||
props.onDelete(props.shout)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
.draggable {
|
||||
margin: 8px 0;
|
||||
padding: 8px 0;
|
||||
|
||||
&:hover {
|
||||
background: var(--placeholder-color-semi);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { Buffer } from 'buffer'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
import styles from './AudioUploader.module.scss'
|
||||
import { DropArea } from '../../_shared/DropArea'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Show } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { MediaItem } from '../../../pages/types'
|
||||
import { composeMediaItems } from '../../../utils/composeMediaItems'
|
||||
import { DropArea } from '../../_shared/DropArea'
|
||||
import { AudioPlayer } from '../../Article/AudioPlayer'
|
||||
import { Buffer } from 'buffer'
|
||||
|
||||
import styles from './AudioUploader.module.scss'
|
||||
|
||||
window.Buffer = Buffer
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
z-index: 2;
|
||||
font-weight: 500;
|
||||
transition: 0.6s ease-in-out;
|
||||
background: rgba(white, 0.3);
|
||||
background: rgb(255 255 255 / 30%);
|
||||
backdrop-filter: blur(4px);
|
||||
border: 1px solid var(--secondary-color);
|
||||
left: 100%;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { clsx } from 'clsx'
|
||||
import styles from './AutoSaveNotice.module.scss'
|
||||
import { Loading } from '../../_shared/Loading'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Loading } from '../../_shared/Loading'
|
||||
|
||||
import styles from './AutoSaveNotice.module.scss'
|
||||
|
||||
type Props = {
|
||||
active: boolean
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import type { Editor } from '@tiptap/core'
|
||||
import styles from './BubbleMenu.module.scss'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
|
||||
import styles from './BubbleMenu.module.scss'
|
||||
|
||||
type Props = {
|
||||
editor: Editor
|
||||
ref: (el: HTMLElement) => void
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import type { Editor } from '@tiptap/core'
|
||||
import styles from './BubbleMenu.module.scss'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
import { UploadModalContent } from '../UploadModalContent'
|
||||
import { Modal } from '../../Nav/Modal'
|
||||
import { UploadedFile } from '../../../pages/types'
|
||||
import { renderUploadedImage } from '../../../utils/renderUploadedImage'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
import { Modal } from '../../Nav/Modal'
|
||||
import { UploadModalContent } from '../UploadModalContent'
|
||||
|
||||
import styles from './BubbleMenu.module.scss'
|
||||
|
||||
type Props = {
|
||||
editor: Editor
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { createSignal, Show, For } from 'solid-js'
|
||||
import type { Editor } from '@tiptap/core'
|
||||
import styles from './BubbleMenu.module.scss'
|
||||
|
||||
import { clsx } from 'clsx'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { createSignal, Show, For } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import styles from './BubbleMenu.module.scss'
|
||||
|
||||
type Props = {
|
||||
editor: Editor
|
||||
|
|
|
@ -1,52 +1,56 @@
|
|||
import { createEffect, createSignal, onCleanup } from 'solid-js'
|
||||
import { createTiptapEditor, useEditorHTML } from 'solid-tiptap'
|
||||
import uniqolor from 'uniqolor'
|
||||
import * as Y from 'yjs'
|
||||
import type { Doc } from 'yjs/dist/src/utils/Doc'
|
||||
|
||||
import { HocuspocusProvider } from '@hocuspocus/provider'
|
||||
import { isTextSelection } from '@tiptap/core'
|
||||
import { Bold } from '@tiptap/extension-bold'
|
||||
import { BubbleMenu } from '@tiptap/extension-bubble-menu'
|
||||
import { Dropcursor } from '@tiptap/extension-dropcursor'
|
||||
import { Italic } from '@tiptap/extension-italic'
|
||||
import { Strike } from '@tiptap/extension-strike'
|
||||
import { HorizontalRule } from '@tiptap/extension-horizontal-rule'
|
||||
import { Underline } from '@tiptap/extension-underline'
|
||||
import { FloatingMenu } from '@tiptap/extension-floating-menu'
|
||||
import { BulletList } from '@tiptap/extension-bullet-list'
|
||||
import { OrderedList } from '@tiptap/extension-ordered-list'
|
||||
import { ListItem } from '@tiptap/extension-list-item'
|
||||
import { CharacterCount } from '@tiptap/extension-character-count'
|
||||
import { Placeholder } from '@tiptap/extension-placeholder'
|
||||
import { Collaboration } from '@tiptap/extension-collaboration'
|
||||
import { CollaborationCursor } from '@tiptap/extension-collaboration-cursor'
|
||||
import { Document } from '@tiptap/extension-document'
|
||||
import { Dropcursor } from '@tiptap/extension-dropcursor'
|
||||
import { FloatingMenu } from '@tiptap/extension-floating-menu'
|
||||
import Focus from '@tiptap/extension-focus'
|
||||
import { Gapcursor } from '@tiptap/extension-gapcursor'
|
||||
import { HardBreak } from '@tiptap/extension-hard-break'
|
||||
import { Heading } from '@tiptap/extension-heading'
|
||||
import { Highlight } from '@tiptap/extension-highlight'
|
||||
import { Link } from '@tiptap/extension-link'
|
||||
import { Document } from '@tiptap/extension-document'
|
||||
import { Text } from '@tiptap/extension-text'
|
||||
import { CollaborationCursor } from '@tiptap/extension-collaboration-cursor'
|
||||
import { isTextSelection } from '@tiptap/core'
|
||||
import { Paragraph } from '@tiptap/extension-paragraph'
|
||||
import Focus from '@tiptap/extension-focus'
|
||||
import { Collaboration } from '@tiptap/extension-collaboration'
|
||||
import { HocuspocusProvider } from '@hocuspocus/provider'
|
||||
import { CustomBlockquote } from './extensions/CustomBlockquote'
|
||||
import { Figure } from './extensions/Figure'
|
||||
import { Figcaption } from './extensions/Figcaption'
|
||||
import { Embed } from './extensions/Embed'
|
||||
import { useSession } from '../../context/session'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useEditorContext } from '../../context/editor'
|
||||
import { TrailingNode } from './extensions/TrailingNode'
|
||||
import Article from './extensions/Article'
|
||||
import { TextBubbleMenu } from './TextBubbleMenu'
|
||||
import { FigureBubbleMenu, BlockquoteBubbleMenu, IncutBubbleMenu } from './BubbleMenu'
|
||||
import { EditorFloatingMenu } from './EditorFloatingMenu'
|
||||
import './Prosemirror.scss'
|
||||
import { HorizontalRule } from '@tiptap/extension-horizontal-rule'
|
||||
import { Image } from '@tiptap/extension-image'
|
||||
import { Footnote } from './extensions/Footnote'
|
||||
import { Italic } from '@tiptap/extension-italic'
|
||||
import { Link } from '@tiptap/extension-link'
|
||||
import { ListItem } from '@tiptap/extension-list-item'
|
||||
import { OrderedList } from '@tiptap/extension-ordered-list'
|
||||
import { Paragraph } from '@tiptap/extension-paragraph'
|
||||
import { Placeholder } from '@tiptap/extension-placeholder'
|
||||
import { Strike } from '@tiptap/extension-strike'
|
||||
import { Text } from '@tiptap/extension-text'
|
||||
import { Underline } from '@tiptap/extension-underline'
|
||||
import { createEffect, createSignal, onCleanup } from 'solid-js'
|
||||
import { createTiptapEditor, useEditorHTML } from 'solid-tiptap'
|
||||
import uniqolor from 'uniqolor'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
import { useEditorContext } from '../../context/editor'
|
||||
import { useLocalize } from '../../context/localize'
|
||||
import { useSession } from '../../context/session'
|
||||
import { useSnackbar } from '../../context/snackbar'
|
||||
import { handleImageUpload } from '../../utils/handleImageUpload'
|
||||
|
||||
import { FigureBubbleMenu, BlockquoteBubbleMenu, IncutBubbleMenu } from './BubbleMenu'
|
||||
import { EditorFloatingMenu } from './EditorFloatingMenu'
|
||||
import Article from './extensions/Article'
|
||||
import { CustomBlockquote } from './extensions/CustomBlockquote'
|
||||
import { Figcaption } from './extensions/Figcaption'
|
||||
import { Figure } from './extensions/Figure'
|
||||
import { Footnote } from './extensions/Footnote'
|
||||
import { Iframe } from './extensions/Iframe'
|
||||
import { TrailingNode } from './extensions/TrailingNode'
|
||||
import { TextBubbleMenu } from './TextBubbleMenu'
|
||||
|
||||
import './Prosemirror.scss'
|
||||
|
||||
type Props = {
|
||||
shoutId: number
|
||||
initialContent?: string
|
||||
|
@ -61,7 +65,7 @@ const allowedImageTypes = new Set([
|
|||
'image/png',
|
||||
'image/tiff',
|
||||
'image/webp',
|
||||
'image/x-icon'
|
||||
'image/x-icon',
|
||||
])
|
||||
|
||||
const yDocs: Record<string, Doc> = {}
|
||||
|
@ -75,7 +79,7 @@ export const Editor = (props: Props) => {
|
|||
const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false)
|
||||
|
||||
const {
|
||||
actions: { showSnackbar }
|
||||
actions: { showSnackbar },
|
||||
} = useSnackbar()
|
||||
|
||||
const docName = `shout-${props.shoutId}`
|
||||
|
@ -88,49 +92,44 @@ export const Editor = (props: Props) => {
|
|||
providers[docName] = new HocuspocusProvider({
|
||||
url: 'wss://hocuspocus.discours.io',
|
||||
name: docName,
|
||||
document: yDocs[docName]
|
||||
document: yDocs[docName],
|
||||
})
|
||||
}
|
||||
|
||||
const editorElRef: {
|
||||
current: HTMLDivElement
|
||||
} = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
|
||||
const textBubbleMenuRef: {
|
||||
current: HTMLDivElement
|
||||
} = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
|
||||
const incutBubbleMenuRef: {
|
||||
current: HTMLElement
|
||||
} = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
const figureBubbleMenuRef: {
|
||||
current: HTMLElement
|
||||
} = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
const blockquoteBubbleMenuRef: {
|
||||
current: HTMLElement
|
||||
} = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
|
||||
const floatingMenuRef: {
|
||||
current: HTMLDivElement
|
||||
} = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
|
||||
const ImageFigure = Figure.extend({
|
||||
name: 'capturedImage',
|
||||
content: 'figcaption image'
|
||||
})
|
||||
|
||||
const handleClipboardPaste = async () => {
|
||||
try {
|
||||
const clipboardItems = await navigator.clipboard.read()
|
||||
|
@ -149,7 +148,7 @@ export const Editor = (props: Props) => {
|
|||
source: blob.toString(),
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
file
|
||||
file,
|
||||
}
|
||||
|
||||
showSnackbar({ body: t('Uploading image') })
|
||||
|
@ -159,24 +158,18 @@ export const Editor = (props: Props) => {
|
|||
.chain()
|
||||
.focus()
|
||||
.insertContent({
|
||||
type: 'capturedImage',
|
||||
type: 'figure',
|
||||
attrs: { 'data-type': 'image' },
|
||||
content: [
|
||||
{
|
||||
type: 'figcaption',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: result.originalFilename
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'image',
|
||||
attrs: {
|
||||
src: result.url
|
||||
}
|
||||
}
|
||||
]
|
||||
attrs: { src: result.url },
|
||||
},
|
||||
{
|
||||
type: 'figcaption',
|
||||
content: [{ type: 'text', text: result.originalFilename }],
|
||||
},
|
||||
],
|
||||
})
|
||||
.run()
|
||||
} catch (error) {
|
||||
|
@ -190,7 +183,7 @@ export const Editor = (props: Props) => {
|
|||
element: editorElRef.current,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'articleEditor'
|
||||
class: 'articleEditor',
|
||||
},
|
||||
transformPastedHTML(html) {
|
||||
return html.replaceAll(/<img.*?>/g, '')
|
||||
|
@ -198,7 +191,7 @@ export const Editor = (props: Props) => {
|
|||
handlePaste: () => {
|
||||
handleClipboardPaste()
|
||||
return false
|
||||
}
|
||||
},
|
||||
},
|
||||
extensions: [
|
||||
Document,
|
||||
|
@ -211,31 +204,31 @@ export const Editor = (props: Props) => {
|
|||
Strike,
|
||||
HorizontalRule.configure({
|
||||
HTMLAttributes: {
|
||||
class: 'horizontalRule'
|
||||
}
|
||||
class: 'horizontalRule',
|
||||
},
|
||||
}),
|
||||
Underline,
|
||||
Link.configure({
|
||||
openOnClick: false
|
||||
openOnClick: false,
|
||||
}),
|
||||
Heading.configure({
|
||||
levels: [2, 3, 4]
|
||||
levels: [2, 3, 4],
|
||||
}),
|
||||
BulletList,
|
||||
OrderedList,
|
||||
ListItem,
|
||||
Collaboration.configure({
|
||||
document: yDocs[docName]
|
||||
document: yDocs[docName],
|
||||
}),
|
||||
CollaborationCursor.configure({
|
||||
provider: providers[docName],
|
||||
user: {
|
||||
name: user().name,
|
||||
color: uniqolor(user().slug).color
|
||||
}
|
||||
color: uniqolor(user().slug).color,
|
||||
},
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: t('Add a link or click plus to embed media')
|
||||
placeholder: t('Add a link or click plus to embed media'),
|
||||
}),
|
||||
Focus,
|
||||
Gapcursor,
|
||||
|
@ -243,14 +236,14 @@ export const Editor = (props: Props) => {
|
|||
Highlight.configure({
|
||||
multicolor: true,
|
||||
HTMLAttributes: {
|
||||
class: 'highlight'
|
||||
}
|
||||
class: 'highlight',
|
||||
},
|
||||
}),
|
||||
ImageFigure,
|
||||
Image,
|
||||
Iframe,
|
||||
Figure,
|
||||
Figcaption,
|
||||
Footnote,
|
||||
Embed,
|
||||
CharacterCount.configure(), // https://github.com/ueberdosis/tiptap/issues/2589#issuecomment-1093084689
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'textBubbleMenu',
|
||||
|
@ -261,14 +254,19 @@ export const Editor = (props: Props) => {
|
|||
const isEmptyTextBlock = doc.textBetween(from, to).length === 0 && isTextSelection(selection)
|
||||
setIsCommonMarkup(e.isActive('figcaption'))
|
||||
const result =
|
||||
(view.hasFocus() && !empty && !isEmptyTextBlock && !e.isActive('image')) ||
|
||||
e.isActive('footnote')
|
||||
(view.hasFocus() &&
|
||||
!empty &&
|
||||
!isEmptyTextBlock &&
|
||||
!e.isActive('image') &&
|
||||
!e.isActive('figure')) ||
|
||||
e.isActive('footnote') ||
|
||||
(e.isActive('figcaption') && !empty)
|
||||
setShouldShowTextBubbleMenu(result)
|
||||
return result
|
||||
},
|
||||
tippyOptions: {
|
||||
sticky: true
|
||||
}
|
||||
sticky: true,
|
||||
},
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'blockquoteBubbleMenu',
|
||||
|
@ -286,8 +284,8 @@ export const Editor = (props: Props) => {
|
|||
if (selectedElement) {
|
||||
return selectedElement.getBoundingClientRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'incutBubbleMenu',
|
||||
|
@ -305,31 +303,31 @@ export const Editor = (props: Props) => {
|
|||
if (selectedElement) {
|
||||
return selectedElement.getBoundingClientRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'imageBubbleMenu',
|
||||
element: figureBubbleMenuRef.current,
|
||||
shouldShow: ({ editor: e, view }) => {
|
||||
return view.hasFocus() && e.isActive('image')
|
||||
}
|
||||
},
|
||||
}),
|
||||
FloatingMenu.configure({
|
||||
tippyOptions: {
|
||||
placement: 'left'
|
||||
placement: 'left',
|
||||
},
|
||||
element: floatingMenuRef.current
|
||||
element: floatingMenuRef.current,
|
||||
}),
|
||||
TrailingNode,
|
||||
Article
|
||||
Article,
|
||||
],
|
||||
enablePasteRules: [Link],
|
||||
content: initialContent ?? null
|
||||
content: initialContent ?? null,
|
||||
}))
|
||||
|
||||
const {
|
||||
actions: { countWords, setEditor }
|
||||
actions: { countWords, setEditor },
|
||||
} = useEditorContext()
|
||||
|
||||
setEditor(editor)
|
||||
|
@ -341,7 +339,7 @@ export const Editor = (props: Props) => {
|
|||
if (html()) {
|
||||
countWords({
|
||||
characters: editor().storage.characterCount.characters(),
|
||||
words: editor().storage.characterCount.words()
|
||||
words: editor().storage.characterCount.words(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import { createEffect, createSignal, Show } from 'solid-js'
|
||||
import type { Editor, JSONContent } from '@tiptap/core'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { InlineForm } from '../InlineForm'
|
||||
import styles from './EditorFloatingMenu.module.scss'
|
||||
import HTMLParser from 'html-to-json-parser'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Modal } from '../../Nav/Modal'
|
||||
import { Menu } from './Menu'
|
||||
import type { MenuItem } from './Menu/Menu'
|
||||
import { showModal } from '../../../stores/ui'
|
||||
import { UploadModalContent } from '../UploadModalContent'
|
||||
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
||||
import type { Editor } from '@tiptap/core'
|
||||
|
||||
import { createEffect, createSignal, Show } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { UploadedFile } from '../../../pages/types'
|
||||
import { showModal } from '../../../stores/ui'
|
||||
import { renderUploadedImage } from '../../../utils/renderUploadedImage'
|
||||
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Modal } from '../../Nav/Modal'
|
||||
import { InlineForm } from '../InlineForm'
|
||||
import { UploadModalContent } from '../UploadModalContent'
|
||||
|
||||
import { Menu } from './Menu'
|
||||
|
||||
import styles from './EditorFloatingMenu.module.scss'
|
||||
|
||||
type FloatingMenuProps = {
|
||||
editor: Editor
|
||||
|
@ -20,10 +23,17 @@ type FloatingMenuProps = {
|
|||
}
|
||||
|
||||
const embedData = async (data) => {
|
||||
const result = (await HTMLParser(data, false)) as JSONContent
|
||||
if ('type' in result && result.type === 'iframe') {
|
||||
return result.attributes
|
||||
const element = document.createRange().createContextualFragment(data)
|
||||
const { attributes } = element.firstChild as HTMLIFrameElement
|
||||
|
||||
const result: { src: string; width?: string; height?: string } = { src: '' }
|
||||
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
const attribute = attributes[i]
|
||||
result[attribute.name] = attribute.value
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
||||
|
@ -35,12 +45,33 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
|||
const handleEmbedFormSubmit = async (value: string) => {
|
||||
// TODO: add support instagram embed (blockquote)
|
||||
const emb = await embedData(value)
|
||||
props.editor.chain().focus().setIframe(emb).run()
|
||||
props.editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContent({
|
||||
type: 'figure',
|
||||
attrs: { 'data-type': 'iframe' },
|
||||
content: [
|
||||
{
|
||||
type: 'iframe',
|
||||
attrs: {
|
||||
src: emb.src,
|
||||
width: emb.width,
|
||||
height: emb.height,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'figcaption',
|
||||
content: [{ type: 'text', text: t('Description') }],
|
||||
},
|
||||
],
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
const validateEmbed = async (value) => {
|
||||
const iframeData = (await HTMLParser(value, false)) as JSONContent
|
||||
if (iframeData.type !== 'iframe') {
|
||||
const element = document.createRange().createContextualFragment(value)
|
||||
if (element.firstChild?.nodeName !== 'IFRAME') {
|
||||
return t('Error')
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +93,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
|||
const closeUploadModalHandler = () => {
|
||||
setSelectedMenuItem()
|
||||
setMenuOpen(false)
|
||||
setSelectedMenuItem()
|
||||
}
|
||||
|
||||
useOutsideClickHandler({
|
||||
|
@ -73,8 +105,9 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
|||
|
||||
if (menuOpen()) {
|
||||
setMenuOpen(false)
|
||||
setSelectedMenuItem()
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const handleUpload = (image: UploadedFile) => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import styles from './Menu.module.scss'
|
||||
import { useLocalize } from '../../../../context/localize'
|
||||
import { Icon } from '../../../_shared/Icon'
|
||||
import { Popover } from '../../../_shared/Popover'
|
||||
import { useLocalize } from '../../../../context/localize'
|
||||
|
||||
import styles from './Menu.module.scss'
|
||||
|
||||
export type MenuItem = 'image' | 'embed' | 'horizontal-rule'
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
.InlineForm {
|
||||
border: 1px solid;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
padding: 6px 11px;
|
||||
padding: 8px 11px;
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
|
@ -24,6 +25,10 @@
|
|||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.linkError {
|
||||
|
@ -39,7 +44,9 @@
|
|||
border: 1px solid #e9e9ee;
|
||||
border-radius: 2px;
|
||||
opacity: 0;
|
||||
transition: height 0.3s ease-in-out, opacity 0.3s ease-in-out;
|
||||
transition:
|
||||
height 0.3s ease-in-out,
|
||||
opacity 0.3s ease-in-out;
|
||||
|
||||
&.visible {
|
||||
height: 32px;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import styles from './InlineForm.module.scss'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { createSignal, onMount } from 'solid-js'
|
||||
import { clsx } from 'clsx'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
import { createSignal, onMount } from 'solid-js'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { Popover } from '../../_shared/Popover'
|
||||
|
||||
import styles from './InlineForm.module.scss'
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Editor } from '@tiptap/core'
|
||||
import { createEditorTransaction } from 'solid-tiptap'
|
||||
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { validateUrl } from '../../../utils/validateUrl'
|
||||
import { InlineForm } from '../InlineForm'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { createEditorTransaction } from 'solid-tiptap'
|
||||
|
||||
type Props = {
|
||||
editor: Editor
|
||||
|
@ -24,7 +25,7 @@ export const InsertLinkForm = (props: Props) => {
|
|||
() => props.editor,
|
||||
(ed) => {
|
||||
return (ed && ed.getAttributes('link').href) || ''
|
||||
}
|
||||
},
|
||||
)
|
||||
const handleClearLinkForm = () => {
|
||||
if (currentUrl()) {
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.LinkBubbleMenu {
|
||||
background: var(--editor-bubble-menu-background);
|
||||
box-shadow: 0 4px 10px rgba(#000, 0.25);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import type { Editor } from '@tiptap/core'
|
||||
|
||||
import { InsertLinkForm } from '../InsertLinkForm'
|
||||
|
||||
import styles from './LinkBubbleMenu.module.scss'
|
||||
|
||||
type Props = {
|
||||
editor: Editor
|
||||
ref: (el: HTMLDivElement) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export const LinkBubbleMenuModule = (props: Props) => {
|
||||
return (
|
||||
<div ref={props.ref} class={styles.LinkBubbleMenu}>
|
||||
<InsertLinkForm editor={props.editor} onClose={props.onClose} />
|
||||
</div>
|
||||
)
|
||||
}
|
1
src/components/Editor/LinkBubbleMenu/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { LinkBubbleMenuModule } from './LinkBubbleMenu.module'
|
|
@ -1,17 +1,20 @@
|
|||
import { clsx } from 'clsx'
|
||||
import { Button } from '../../_shared/Button'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import styles from './Panel.module.scss'
|
||||
import { useEditorContext } from '../../../context/editor'
|
||||
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
||||
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
|
||||
import { getPagePath } from '@nanostores/router'
|
||||
import { router } from '../../../stores/router'
|
||||
import { clsx } from 'clsx'
|
||||
import { createSignal, Show } from 'solid-js'
|
||||
import { useEditorHTML } from 'solid-tiptap'
|
||||
import Typograf from 'typograf'
|
||||
import { createSignal, Show } from 'solid-js'
|
||||
|
||||
import { useEditorContext } from '../../../context/editor'
|
||||
import { useLocalize } from '../../../context/localize'
|
||||
import { router } from '../../../stores/router'
|
||||
import { showModal } from '../../../stores/ui'
|
||||
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
|
||||
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
|
||||
import { Button } from '../../_shared/Button'
|
||||
import { DarkModeToggle } from '../../_shared/DarkModeToggle'
|
||||
import { Icon } from '../../_shared/Icon'
|
||||
|
||||
import styles from './Panel.module.scss'
|
||||
|
||||
const typograf = new Typograf({ locale: ['ru', 'en-US'] })
|
||||
|
||||
|
@ -26,11 +29,11 @@ export const Panel = (props: Props) => {
|
|||
wordCounter,
|
||||
editorRef,
|
||||
form,
|
||||
actions: { toggleEditorPanel, saveShout, publishShout }
|
||||
actions: { toggleEditorPanel, saveShout, publishShout },
|
||||
} = useEditorContext()
|
||||
|
||||
const containerRef: { current: HTMLElement } = {
|
||||
current: null
|
||||
current: null,
|
||||
}
|
||||
|
||||
const [isShortcutsVisible, setIsShortcutsVisible] = createSignal(false)
|
||||
|
@ -39,7 +42,7 @@ export const Panel = (props: Props) => {
|
|||
useOutsideClickHandler({
|
||||
containerRef,
|
||||
predicate: () => isEditorPanelVisible(),
|
||||
handler: () => toggleEditorPanel()
|
||||
handler: () => toggleEditorPanel(),
|
||||
})
|
||||
|
||||
useEscKeyDownHandler(() => {
|
||||
|
@ -89,7 +92,9 @@ export const Panel = (props: Props) => {
|
|||
|
||||
<section>
|
||||
<p>
|
||||
<a class={styles.link}>{t('Invite co-authors')}</a>
|
||||
<span class={styles.link} onClick={() => showModal('inviteCoAuthors')}>
|
||||
{t('Invite co-authors')}
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
|
|
|
@ -265,10 +265,29 @@ mark.highlight {
|
|||
}
|
||||
}
|
||||
|
||||
figure[data-type='capturedImage'] {
|
||||
flex-direction: column-reverse;
|
||||
.ProseMirror-hideselection figure[data-type='figure'] {
|
||||
& > figcaption {
|
||||
--selection-color: rgb(0 0 0 / 60%);
|
||||
}
|
||||
}
|
||||
|
||||
figure[data-type='figure'] {
|
||||
width: 100% !important;
|
||||
|
||||
.iframe-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
iframe {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-type-no-unknown */
|
||||
footnote {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
|
@ -276,7 +295,7 @@ footnote {
|
|||
width: 0.8rem;
|
||||
height: 1em;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
|
|