Compare commits
27 Commits
dev
...
feature/em
Author | SHA1 | Date | |
---|---|---|---|
d518d5c2bc | |||
8fbcde234e | |||
93f6a1b080 | |||
3a71161da9 | |||
02067ace1f | |||
79518e07f2 | |||
424537c513 | |||
0b4fa8bfa3 | |||
d7680ea396 | |||
1a393c75c5 | |||
7f85c543ed | |||
52b2a6d16c | |||
59db2c598d | |||
21e0f4f5da | |||
39e2e37a26 | |||
5e2cec5b5d | |||
cf37edaeca | |||
4055f2c3fc | |||
f0584b8aff | |||
8bf1dab381 | |||
3c8807da21 | |||
d65aea5fb0 | |||
90c4d93872 | |||
d9fe833d2e | |||
86ee656a3a | |||
0118cf42c6 | |||
6d3f7ceffe |
|
@ -5,6 +5,7 @@ on: [push]
|
|||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref != 'refs/heads/feature/email-templates'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
@ -35,37 +36,42 @@ jobs:
|
|||
if: github.event_name == 'push' && github.ref == 'refs/heads/feature/email-templates'
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: "Email confirmation template"
|
||||
- name: Run templates build
|
||||
run: npm run templates
|
||||
|
||||
- name: "authorizer_email_confirmation template"
|
||||
uses: gyto/mailgun-template-action@v2
|
||||
with:
|
||||
html-file: "./templates/authorizer_email_confirmation.html"
|
||||
html-file: "./templates/dist/authorizer_email_confirmation.html"
|
||||
mailgun-api-key: ${{ secrets.MAILGUN_API_KEY }}
|
||||
mailgun-domain: "discours.io"
|
||||
mailgun-template: "authorizer_email_confirmation"
|
||||
|
||||
- name: "Password reset template"
|
||||
- name: "authorizer_password_reset template"
|
||||
uses: gyto/mailgun-template-action@v2
|
||||
with:
|
||||
html-file: "./templates/authorizer_password_reset.html"
|
||||
html-file: "./templates/dist/authorizer_password_reset.html"
|
||||
mailgun-api-key: ${{ secrets.MAILGUN_API_KEY }}
|
||||
mailgun-domain: "discours.io"
|
||||
mailgun-template: "authorizer_password_reset"
|
||||
|
||||
- name: "First publication notification"
|
||||
- name: "email_first_publication template deploy"
|
||||
uses: gyto/mailgun-template-action@v2
|
||||
with:
|
||||
html-file: "./templates/first_publication_notification.html"
|
||||
html-file: "./templates/dist/authorizer_first_publication.html"
|
||||
mailgun-api-key: ${{ secrets.MAILGUN_API_KEY }}
|
||||
mailgun-domain: "discours.io"
|
||||
mailgun-template: "first_publication_notification"
|
||||
mailgun-template: "email_first_publication"
|
||||
|
||||
- name: "New comment notification template"
|
||||
- name: "new_comment_notification template"
|
||||
uses: gyto/mailgun-template-action@v2
|
||||
with:
|
||||
html-file: "./templates/new_comment_notification.html"
|
||||
html-file: "./templates/dist/authorizer_new_comment.html"
|
||||
mailgun-api-key: ${{ secrets.MAILGUN_API_KEY }}
|
||||
mailgun-domain: "discours.io"
|
||||
mailgun-template: "new_comment_notification"
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -22,3 +22,4 @@ bun.lockb
|
|||
/blob-report/
|
||||
/playwright/.cache/
|
||||
/plawright-report/
|
||||
/templates/dist/*
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { renderPage } from 'vike/server'
|
||||
|
||||
export const config = {
|
||||
runtime: 'edge',
|
||||
runtime: 'edge'
|
||||
}
|
||||
export default async function handler(request) {
|
||||
const { url, cookies } = request
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"$schema": "https://biomejs.dev/schemas/1.5.3/schema.json",
|
||||
"files": {
|
||||
"include": ["*.tsx", "*.ts", "*.js", "*.json"],
|
||||
"ignore": ["./dist", "./node_modules", ".husky", "docs", "gen", "*.d.ts"]
|
||||
"ignore": ["./dist", "./node_modules", ".husky", "docs", "gen", "templates"]
|
||||
},
|
||||
"vcs": {
|
||||
"defaultBranch": "dev",
|
||||
|
@ -22,7 +22,7 @@
|
|||
"formatter": {
|
||||
"semicolons": "asNeeded",
|
||||
"quoteStyle": "single",
|
||||
"trailingComma": "all",
|
||||
"trailingComma": "none",
|
||||
"enabled": true,
|
||||
"jsxQuoteStyle": "double",
|
||||
"arrowParentheses": "always"
|
||||
|
|
20
package-lock.json
generated
20
package-lock.json
generated
|
@ -93,7 +93,7 @@
|
|||
"patch-package": "^8.0.0",
|
||||
"prosemirror-history": "1.3.2",
|
||||
"prosemirror-trailing-node": "2.0.7",
|
||||
"prosemirror-view": "1.32.7",
|
||||
"prosemirror-view": "1.33.1",
|
||||
"rollup": "4.11.0",
|
||||
"sass": "1.69.5",
|
||||
"solid-js": "1.8.15",
|
||||
|
@ -5858,9 +5858,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.672",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.672.tgz",
|
||||
"integrity": "sha512-YYCy+goe3UqZqa3MOQCI5Mx/6HdBLzXL/mkbGCEWL3sP3Z1BP9zqAzeD3YEmLZlespYGFtyM8tRp5i2vfaUGCA==",
|
||||
"version": "1.4.673",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.673.tgz",
|
||||
"integrity": "sha512-zjqzx4N7xGdl5468G+vcgzDhaHkaYgVcf9MqgexcTqsl2UHSCmOj/Bi3HAprg4BZCpC7HyD8a6nZl6QAZf72gw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
|
@ -10142,9 +10142,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.32.7",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.32.7.tgz",
|
||||
"integrity": "sha512-pvxiOoD4shW41X5bYDjRQk3DSG4fMqxh36yPMt7VYgU3dWRmqFzWJM/R6zeo1KtC8nyk717ZbQND3CC9VNeptw==",
|
||||
"version": "1.33.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.33.1.tgz",
|
||||
"integrity": "sha512-62qkYgSJIkwIMMCpuGuPzc52DiK1Iod6TWoIMxP4ja6BTD4yO8kCUL64PZ/WhH/dJ9fW0CDO39FhH1EMyhUFEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.16.0",
|
||||
|
@ -12163,9 +12163,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz",
|
||||
"integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
|
|
11
package.json
11
package.json
|
@ -12,20 +12,21 @@
|
|||
"dev": "vite",
|
||||
"e2e": "npx playwright test --project=chromium",
|
||||
"fix": "npm run check:code:fix && stylelint **/*.{scss,css} --fix",
|
||||
"format": "npx @biomejs/biome format src/. --write",
|
||||
"format": "npx @biomejs/biome format . --write",
|
||||
"hygen": "HYGEN_TMPLS=gen hygen",
|
||||
"postinstall": "npm run codegen && npx patch-package",
|
||||
"check:code": "npx @biomejs/biome check src --log-kind=compact --verbose",
|
||||
"check:code:fix": "npx @biomejs/biome check src --log-kind=compact --verbose --apply-unsafe",
|
||||
"lint": "npm run lint:code && stylelint **/*.{scss,css}",
|
||||
"lint:code": "npx @biomejs/biome lint src --log-kind=compact --verbose",
|
||||
"lint:code:fix": "npx @biomejs/biome lint src --apply-unsafe --log-kind=compact --verbose",
|
||||
"lint:code": "npx @biomejs/biome lint . --log-kind=compact --verbose",
|
||||
"lint:code:fix": "npx @biomejs/biome lint . --apply-unsafe --log-kind=compact --verbose",
|
||||
"lint:styles": "stylelint **/*.{scss,css}",
|
||||
"lint:styles:fix": "stylelint **/*.{scss,css} --fix",
|
||||
"preview": "vite preview",
|
||||
"start": "vite",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:watch": "tsc --noEmit --watch"
|
||||
"typecheck:watch": "tsc --noEmit --watch",
|
||||
"templates": "node ./templates/compile.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": "4.0.0",
|
||||
|
@ -111,7 +112,7 @@
|
|||
"patch-package": "^8.0.0",
|
||||
"prosemirror-history": "1.3.2",
|
||||
"prosemirror-trailing-node": "2.0.7",
|
||||
"prosemirror-view": "1.32.7",
|
||||
"prosemirror-view": "1.33.1",
|
||||
"rollup": "4.11.0",
|
||||
"sass": "1.69.5",
|
||||
"solid-js": "1.8.15",
|
||||
|
|
|
@ -27,25 +27,25 @@ export default defineConfig({
|
|||
// baseURL: 'http://127.0.0.1:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
trace: 'on-first-retry'
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
use: { ...devices['Desktop Chrome'] }
|
||||
},
|
||||
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
use: { ...devices['Desktop Firefox'] }
|
||||
},
|
||||
|
||||
{
|
||||
name: 'webkit',
|
||||
use: { ...devices['Desktop Safari'] },
|
||||
},
|
||||
use: { ...devices['Desktop Safari'] }
|
||||
}
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
|
@ -66,7 +66,7 @@ export default defineConfig({
|
|||
// name: 'Google Chrome',
|
||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
]
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
|
|
|
@ -84,7 +84,7 @@ const pagesMap: Record<keyof typeof ROUTES, Component<PageProps>> = {
|
|||
profileSettings: ProfileSettingsPage,
|
||||
profileSecurity: ProfileSecurityPage,
|
||||
profileSubscriptions: ProfileSubscriptionsPage,
|
||||
fourOuFour: FourOuFourPage,
|
||||
fourOuFour: FourOuFourPage
|
||||
}
|
||||
|
||||
type Props = PageProps & { is404: boolean }
|
||||
|
|
|
@ -38,8 +38,8 @@ export const AudioPlayer = (props: Props) => {
|
|||
() => {
|
||||
setCurrentTrackDuration(0)
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
{ defer: true }
|
||||
)
|
||||
)
|
||||
|
||||
const handlePlayMedia = async (trackIndex: number) => {
|
||||
|
@ -134,7 +134,7 @@ export const AudioPlayer = (props: Props) => {
|
|||
<div
|
||||
class={styles.progressFilled}
|
||||
style={{
|
||||
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`,
|
||||
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -43,7 +43,7 @@ export const PlayerPlaylist = (props: Props) => {
|
|||
gtag('event', 'select_item', {
|
||||
item_list_id: props.articleSlug,
|
||||
item_list_name: getMediaTitle(mi, index),
|
||||
items: props.media.map((it, ix) => getMediaTitle(it, ix)),
|
||||
items: props.media.map((it, ix) => getMediaTitle(it, ix))
|
||||
})
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -46,7 +46,7 @@ export const Comment = (props: Props) => {
|
|||
const canEdit = createMemo(
|
||||
() =>
|
||||
Boolean(author()?.id) &&
|
||||
(props.comment?.created_by?.id === author().id || session()?.user?.roles.includes('editor')),
|
||||
(props.comment?.created_by?.id === author().id || session()?.user?.roles.includes('editor'))
|
||||
)
|
||||
|
||||
const comment = createMemo(() => props.comment)
|
||||
|
@ -59,7 +59,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) {
|
||||
|
@ -80,7 +80,7 @@ export const Comment = (props: Props) => {
|
|||
kind: ReactionKind.Comment,
|
||||
reply_to: props.comment.id,
|
||||
body: value,
|
||||
shout: props.comment.shout.id,
|
||||
shout: props.comment.shout.id
|
||||
})
|
||||
setClearEditor(true)
|
||||
setIsReplyVisible(false)
|
||||
|
@ -102,7 +102,7 @@ export const Comment = (props: Props) => {
|
|||
id: props.comment.id,
|
||||
kind: ReactionKind.Comment,
|
||||
body: value,
|
||||
shout: props.comment.shout.id,
|
||||
shout: props.comment.shout.id
|
||||
})
|
||||
setEditMode(false)
|
||||
setLoading(false)
|
||||
|
@ -126,7 +126,7 @@ export const Comment = (props: Props) => {
|
|||
name={comment().created_by.name}
|
||||
userpic={comment().created_by.pic}
|
||||
class={clsx({
|
||||
[styles.compactUserpic]: props.compact,
|
||||
[styles.compactUserpic]: props.compact
|
||||
})}
|
||||
/>
|
||||
<small>
|
||||
|
|
|
@ -30,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.created_at * 1000)}</time>
|
||||
|
|
|
@ -28,7 +28,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
r.kind === reactionKind &&
|
||||
r.created_by.slug === author()?.slug &&
|
||||
r.shout.id === props.comment.shout.id &&
|
||||
r.reply_to === props.comment.id,
|
||||
r.reply_to === props.comment.id
|
||||
)
|
||||
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||
const isDownvoted = createMemo(() => checkReaction(ReactionKind.Dislike))
|
||||
|
@ -39,8 +39,8 @@ export const CommentRatingControl = (props: Props) => {
|
|||
(r) =>
|
||||
[ReactionKind.Like, ReactionKind.Dislike].includes(r.kind) &&
|
||||
r.shout.id === props.comment.shout.id &&
|
||||
r.reply_to === props.comment.id,
|
||||
),
|
||||
r.reply_to === props.comment.id
|
||||
)
|
||||
)
|
||||
|
||||
const deleteCommentReaction = async (reactionKind: ReactionKind) => {
|
||||
|
@ -49,7 +49,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
r.kind === reactionKind &&
|
||||
r.created_by.slug === author()?.slug &&
|
||||
r.shout.id === props.comment.shout.id &&
|
||||
r.reply_to === props.comment.id,
|
||||
r.reply_to === props.comment.id
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
await createReaction({
|
||||
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
|
||||
shout: props.comment.shout.id,
|
||||
reply_to: props.comment.id,
|
||||
reply_to: props.comment.id
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
|
@ -73,7 +73,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 }
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
disabled={!(canVote() && author())}
|
||||
onClick={() => handleRatingChange(true)}
|
||||
class={clsx(styles.commentRatingControl, styles.commentRatingControlUp, {
|
||||
[styles.voted]: isUpvoted(),
|
||||
[styles.voted]: isUpvoted()
|
||||
})}
|
||||
/>
|
||||
<Popup
|
||||
|
@ -92,7 +92,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}
|
||||
|
@ -110,7 +110,7 @@ export const CommentRatingControl = (props: Props) => {
|
|||
disabled={!(canVote() && author())}
|
||||
onClick={() => handleRatingChange(false)}
|
||||
class={clsx(styles.commentRatingControl, styles.commentRatingControlDown, {
|
||||
[styles.voted]: isDownvoted(),
|
||||
[styles.voted]: isDownvoted()
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -52,7 +52,7 @@ export const CommentsTree = (props: Props) => {
|
|||
const { reactionEntities, createReaction } = useReactions()
|
||||
|
||||
const comments = createMemo(() =>
|
||||
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT'),
|
||||
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
|
||||
)
|
||||
|
||||
const sortedComments = createMemo(() => {
|
||||
|
@ -93,7 +93,7 @@ export const CommentsTree = (props: Props) => {
|
|||
await createReaction({
|
||||
kind: ReactionKind.Comment,
|
||||
body: value,
|
||||
shout: props.shoutId,
|
||||
shout: props.shoutId
|
||||
})
|
||||
setClearEditor(true)
|
||||
} catch (error) {
|
||||
|
@ -151,7 +151,7 @@ export const CommentsTree = (props: Props) => {
|
|||
<Comment
|
||||
sortedComments={sortedComments()}
|
||||
isArticleAuthor={Boolean(
|
||||
props.articleAuthors.some((a) => a?.slug === reaction.created_by.slug),
|
||||
props.articleAuthors.some((a) => a?.slug === reaction.created_by.slug)
|
||||
)}
|
||||
comment={reaction}
|
||||
clickedReply={(id) => setClickedReplyId(id)}
|
||||
|
|
|
@ -26,7 +26,7 @@ const coverImages = [
|
|||
CoverImage9,
|
||||
CoverImage10,
|
||||
CoverImage11,
|
||||
CoverImage12,
|
||||
CoverImage12
|
||||
]
|
||||
|
||||
let counter = 0
|
||||
|
|
|
@ -61,7 +61,7 @@ const scrollTo = (el: HTMLElement) => {
|
|||
window.scrollTo({
|
||||
top: top - DEFAULT_HEADER_OFFSET,
|
||||
left: 0,
|
||||
behavior: 'smooth',
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ export const FullArticle = (props: Props) => {
|
|||
Boolean(author()?.id) &&
|
||||
(props.article?.authors?.some((a) => Boolean(a) && a?.id === author().id) ||
|
||||
props.article?.created_by?.id === author().id ||
|
||||
session()?.user?.roles.includes('editor')),
|
||||
session()?.user?.roles.includes('editor'))
|
||||
)
|
||||
|
||||
const mainTopic = createMemo(() => {
|
||||
|
@ -167,7 +167,7 @@ export const FullArticle = (props: Props) => {
|
|||
createEffect(() => {
|
||||
if (searchParams().commentId && isReactionsLoaded()) {
|
||||
const commentElement = document.querySelector<HTMLElement>(
|
||||
`[id='comment_${searchParams().commentId}']`,
|
||||
`[id='comment_${searchParams().commentId}']`
|
||||
)
|
||||
|
||||
if (commentElement) {
|
||||
|
@ -185,7 +185,7 @@ export const FullArticle = (props: Props) => {
|
|||
}
|
||||
|
||||
const tooltipElements: NodeListOf<HTMLElement> = document.querySelectorAll(
|
||||
'[data-toggle="tooltip"], footnote',
|
||||
'[data-toggle="tooltip"], footnote'
|
||||
)
|
||||
if (!tooltipElements) {
|
||||
return
|
||||
|
@ -210,19 +210,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'
|
||||
|
@ -307,8 +307,8 @@ export const FullArticle = (props: Props) => {
|
|||
() => props.article,
|
||||
() => {
|
||||
updateIframeSizes()
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
onMount(async () => {
|
||||
|
@ -326,7 +326,7 @@ export const FullArticle = (props: Props) => {
|
|||
title: props.article.title,
|
||||
topic: mainTopic()?.title || '',
|
||||
author: props.article?.authors[0]?.name || '',
|
||||
width: 1200,
|
||||
width: 1200
|
||||
})
|
||||
|
||||
const description = getDescription(props.article.description || body())
|
||||
|
|
|
@ -29,7 +29,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
r.kind === reactionKind &&
|
||||
r.created_by.id === author()?.id &&
|
||||
r.shout.id === props.shout.id &&
|
||||
!r.reply_to,
|
||||
!r.reply_to
|
||||
)
|
||||
|
||||
const isUpvoted = createMemo(() => checkReaction(ReactionKind.Like))
|
||||
|
@ -37,8 +37,8 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
|
||||
const shoutRatingReactions = createMemo(() =>
|
||||
Object.values(reactionEntities).filter(
|
||||
(r) => ['LIKE', 'DISLIKE'].includes(r.kind) && r.shout.id === props.shout.id && !r.reply_to,
|
||||
),
|
||||
(r) => ['LIKE', 'DISLIKE'].includes(r.kind) && r.shout.id === props.shout.id && !r.reply_to
|
||||
)
|
||||
)
|
||||
|
||||
const deleteShoutReaction = async (reactionKind: ReactionKind) => {
|
||||
|
@ -47,7 +47,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
|
|||
r.kind === reactionKind &&
|
||||
r.created_by.id === author()?.id &&
|
||||
r.shout.id === props.shout.id &&
|
||||
!r.reply_to,
|
||||
!r.reply_to
|
||||
)
|
||||
return deleteReaction(reactionToDelete.id)
|
||||
}
|
||||
|
@ -62,13 +62,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 }
|
||||
})
|
||||
|
||||
setIsLoading(false)
|
||||
|
|
|
@ -26,9 +26,9 @@ export const AuthGuard = (props: Props) => {
|
|||
changeSearchParams(
|
||||
{
|
||||
source: 'authguard',
|
||||
m: 'auth',
|
||||
m: 'auth'
|
||||
},
|
||||
true,
|
||||
true
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -50,7 +50,7 @@ export const AuthorBadge = (props: Props) => {
|
|||
requireAuthentication(() => {
|
||||
openPage(router, 'inbox')
|
||||
changeSearchParams({
|
||||
initChat: props.author.id.toString(),
|
||||
initChat: props.author.id.toString()
|
||||
})
|
||||
}, 'discussions')
|
||||
}
|
||||
|
@ -71,9 +71,9 @@ export const AuthorBadge = (props: Props) => {
|
|||
on(
|
||||
() => props.isFollowed,
|
||||
() => {
|
||||
setIsFollowed(props.isFollowed?.value)
|
||||
},
|
||||
),
|
||||
setIsFollowed(props.isFollowed.value)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const handleFollowClick = () => {
|
||||
|
@ -110,7 +110,7 @@ export const AuthorBadge = (props: Props) => {
|
|||
fallback={
|
||||
<div class={styles.bio}>
|
||||
{t('Registered since {date}', {
|
||||
date: formatDate(new Date(props.author.created_at * 1000)),
|
||||
date: formatDate(new Date(props.author.created_at * 1000))
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ export const AuthorBadge = (props: Props) => {
|
|||
isSubscribeButton={true}
|
||||
class={clsx(styles.actionButton, {
|
||||
[styles.iconed]: props.iconButtons,
|
||||
[stylesButton.subscribed]: isFollowed(),
|
||||
[stylesButton.subscribed]: isFollowed()
|
||||
})}
|
||||
/>
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ export const AuthorBadge = (props: Props) => {
|
|||
isSubscribeButton={true}
|
||||
class={clsx(styles.actionButton, {
|
||||
[styles.iconed]: props.iconButtons,
|
||||
[stylesButton.subscribed]: isFollowed(),
|
||||
[stylesButton.subscribed]: isFollowed()
|
||||
})}
|
||||
/>
|
||||
</Show>
|
||||
|
|
|
@ -63,7 +63,7 @@ export const AuthorCard = (props: Props) => {
|
|||
requireAuthentication(() => {
|
||||
openPage(router, 'inbox')
|
||||
changeSearchParams({
|
||||
initChat: props.author.id.toString(),
|
||||
initChat: props.author.id.toString()
|
||||
})
|
||||
}, 'discussions')
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ export const AuthorCard = (props: Props) => {
|
|||
value={followButtonText()}
|
||||
isSubscribeButton={true}
|
||||
class={clsx({
|
||||
[stylesButton.subscribed]: isFollowed(),
|
||||
[stylesButton.subscribed]: isFollowed()
|
||||
})}
|
||||
/>
|
||||
</Show>
|
||||
|
@ -254,7 +254,7 @@ export const AuthorCard = (props: Props) => {
|
|||
author={follower}
|
||||
isFollowed={{
|
||||
loaded: Boolean(authorSubs()),
|
||||
value: isOwnerSubscribed(follower.id),
|
||||
value: isOwnerSubscribed(follower.id)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -303,7 +303,7 @@ export const AuthorCard = (props: Props) => {
|
|||
<AuthorBadge
|
||||
isFollowed={{
|
||||
loaded: Boolean(authorSubs()),
|
||||
value: isOwnerSubscribed(subscription.id),
|
||||
value: isOwnerSubscribed(subscription.id)
|
||||
}}
|
||||
author={subscription}
|
||||
/>
|
||||
|
@ -311,7 +311,7 @@ export const AuthorCard = (props: Props) => {
|
|||
<TopicBadge
|
||||
isFollowed={{
|
||||
loaded: Boolean(authorSubs()),
|
||||
value: isOwnerSubscribed(subscription.id),
|
||||
value: isOwnerSubscribed(subscription.id)
|
||||
}}
|
||||
topic={subscription}
|
||||
/>
|
||||
|
|
|
@ -27,7 +27,7 @@ export const AuthorLink = (props: Props) => {
|
|||
return (
|
||||
<div
|
||||
class={clsx(styles.AuthorLink, props.class, styles[props.size ?? 'M'], {
|
||||
[styles.authorLinkFloorImportant]: props.isFloorImportant,
|
||||
[styles.authorLinkFloorImportant]: props.isFloorImportant
|
||||
})}
|
||||
>
|
||||
<a class={styles.link} href={`/author/${props.author.slug}`}>
|
||||
|
|
|
@ -30,7 +30,7 @@ export const AuthorRatingControl = (props: AuthorRatingControlProps) => {
|
|||
<div
|
||||
class={clsx(styles.rating, props.class, {
|
||||
[styles.isUpvoted]: isUpvoted,
|
||||
[styles.isDownvoted]: isDownvoted,
|
||||
[styles.isDownvoted]: isDownvoted
|
||||
})}
|
||||
>
|
||||
<button
|
||||
|
|
|
@ -16,7 +16,7 @@ export const AuthorShoutsRating = (props: AuthorShoutsRating) => {
|
|||
<div
|
||||
class={clsx(styles.rating, props.class, {
|
||||
[styles.isUpvoted]: isUpvoted(),
|
||||
[styles.isDownvoted]: !isUpvoted(),
|
||||
[styles.isDownvoted]: !isUpvoted()
|
||||
})}
|
||||
>
|
||||
<span class={styles.ratingValue}>{props.author?.stat?.rating_shouts}</span>
|
||||
|
|
|
@ -48,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}
|
||||
>
|
||||
|
|
|
@ -28,7 +28,7 @@ export const AuthorsList = (props: Props) => {
|
|||
const result = await apiClient.loadAuthorsBy({
|
||||
by: { order: queryType },
|
||||
limit: PAGE_SIZE,
|
||||
offset: offset,
|
||||
offset: offset
|
||||
})
|
||||
|
||||
if (queryType === 'shouts') {
|
||||
|
@ -44,7 +44,7 @@ export const AuthorsList = (props: Props) => {
|
|||
const queryType = props.query
|
||||
const nextPage = currentPage()[queryType] + 1
|
||||
fetchAuthors(queryType, nextPage).then(() =>
|
||||
setCurrentPage({ ...currentPage(), [queryType]: nextPage }),
|
||||
setCurrentPage({ ...currentPage(), [queryType]: nextPage })
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ export const AuthorsList = (props: Props) => {
|
|||
author={author}
|
||||
isFollowed={{
|
||||
loaded: !loading(),
|
||||
value: isOwnerSubscribed(author.id),
|
||||
value: isOwnerSubscribed(author.id)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,7 @@ export const Donate = () => {
|
|||
const cpOptions = {
|
||||
publicId: 'pk_0a37bab30ffc6b77b2f93d65f2aed',
|
||||
description: t('Help discours to grow'),
|
||||
currency: 'RUB',
|
||||
currency: 'RUB'
|
||||
}
|
||||
|
||||
let amountSwitchElement: HTMLDivElement | undefined
|
||||
|
@ -45,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 покупателя, если нужно отправить письмо с чеком
|
||||
|
@ -56,8 +56,8 @@ export const Donate = () => {
|
|||
electronic: amount(), // Сумма оплаты электронными деньгами
|
||||
advancePayment: 0, // Сумма из предоплаты (зачетом аванса) (2 знака после запятой)
|
||||
credit: 0, // Сумма постоплатой(в кредит) (2 знака после запятой)
|
||||
provision: 0, // Сумма оплаты встречным предоставлением (сертификаты, др. мат.ценности) (2 знака после запятой)
|
||||
},
|
||||
provision: 0 // Сумма оплаты встречным предоставлением (сертификаты, др. мат.ценности) (2 знака после запятой)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
@ -98,10 +98,10 @@ export const Donate = () => {
|
|||
recurrent: {
|
||||
interval: period(), // local solid's signal
|
||||
period: 1, // internal widget's
|
||||
CustomerReciept: customerReciept(), // чек для регулярных платежей
|
||||
},
|
||||
},
|
||||
},
|
||||
CustomerReciept: customerReciept() // чек для регулярных платежей
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(opts) => {
|
||||
// success
|
||||
|
@ -116,9 +116,9 @@ export const Donate = () => {
|
|||
|
||||
showSnackbar({
|
||||
type: 'error',
|
||||
body: reason,
|
||||
body: reason
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -18,25 +18,25 @@ export const Footer = () => {
|
|||
items: [
|
||||
{
|
||||
title: 'Discours Manifest',
|
||||
slug: '/about/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'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -44,21 +44,21 @@ export const Footer = () => {
|
|||
items: [
|
||||
{
|
||||
title: 'Suggest an idea',
|
||||
slug: '/connect',
|
||||
slug: '/connect'
|
||||
},
|
||||
{
|
||||
title: 'Become an author',
|
||||
slug: '/create',
|
||||
slug: '/create'
|
||||
},
|
||||
{
|
||||
title: 'Support Discours',
|
||||
slug: '/about/help',
|
||||
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'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -66,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}>
|
||||
|
@ -140,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>
|
||||
|
|
|
@ -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}>
|
||||
|
@ -29,7 +29,7 @@ export default () => {
|
|||
onClick={() => {
|
||||
showModal('auth')
|
||||
changeSearchParams({
|
||||
mode: 'register',
|
||||
mode: 'register'
|
||||
})
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -39,7 +39,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)
|
||||
|
|
|
@ -67,7 +67,7 @@ const allowedImageTypes = new Set([
|
|||
'image/png',
|
||||
'image/tiff',
|
||||
'image/webp',
|
||||
'image/x-icon',
|
||||
'image/x-icon'
|
||||
])
|
||||
|
||||
const yDocs: Record<string, Doc> = {}
|
||||
|
@ -93,42 +93,42 @@ export const Editor = (props: Props) => {
|
|||
url: 'wss://hocuspocus.discours.io',
|
||||
name: docName,
|
||||
document: yDocs[docName],
|
||||
token: session()?.access_token || '',
|
||||
token: session()?.access_token || ''
|
||||
})
|
||||
}
|
||||
|
||||
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 handleClipboardPaste = async () => {
|
||||
|
@ -149,7 +149,7 @@ export const Editor = (props: Props) => {
|
|||
source: blob.toString(),
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
file,
|
||||
file
|
||||
}
|
||||
|
||||
showSnackbar({ body: t('Uploading image') })
|
||||
|
@ -164,13 +164,13 @@ export const Editor = (props: Props) => {
|
|||
content: [
|
||||
{
|
||||
type: 'image',
|
||||
attrs: { src: result.url },
|
||||
attrs: { src: result.url }
|
||||
},
|
||||
{
|
||||
type: 'figcaption',
|
||||
content: [{ type: 'text', text: result.originalFilename }],
|
||||
},
|
||||
],
|
||||
content: [{ type: 'text', text: result.originalFilename }]
|
||||
}
|
||||
]
|
||||
})
|
||||
.run()
|
||||
} catch (error) {
|
||||
|
@ -184,7 +184,7 @@ export const Editor = (props: Props) => {
|
|||
element: editorElRef.current,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: 'articleEditor',
|
||||
class: 'articleEditor'
|
||||
},
|
||||
transformPastedHTML(html) {
|
||||
return html.replaceAll(/<img.*?>/g, '')
|
||||
|
@ -192,7 +192,7 @@ export const Editor = (props: Props) => {
|
|||
handlePaste: () => {
|
||||
handleClipboardPaste()
|
||||
return false
|
||||
},
|
||||
}
|
||||
},
|
||||
extensions: [
|
||||
Document,
|
||||
|
@ -207,34 +207,34 @@ export const Editor = (props: Props) => {
|
|||
Strike,
|
||||
HorizontalRule.configure({
|
||||
HTMLAttributes: {
|
||||
class: 'horizontalRule',
|
||||
},
|
||||
class: 'horizontalRule'
|
||||
}
|
||||
}),
|
||||
Underline,
|
||||
Link.extend({
|
||||
inclusive: false,
|
||||
inclusive: false
|
||||
}).configure({
|
||||
autolink: true,
|
||||
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: author().name,
|
||||
color: uniqolor(author().slug).color,
|
||||
},
|
||||
color: uniqolor(author().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,
|
||||
|
@ -242,8 +242,8 @@ export const Editor = (props: Props) => {
|
|||
Highlight.configure({
|
||||
multicolor: true,
|
||||
HTMLAttributes: {
|
||||
class: 'highlight',
|
||||
},
|
||||
class: 'highlight'
|
||||
}
|
||||
}),
|
||||
Image,
|
||||
Iframe,
|
||||
|
@ -275,8 +275,8 @@ export const Editor = (props: Props) => {
|
|||
return result
|
||||
},
|
||||
tippyOptions: {
|
||||
sticky: true,
|
||||
},
|
||||
sticky: true
|
||||
}
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'blockquoteBubbleMenu',
|
||||
|
@ -294,8 +294,8 @@ export const Editor = (props: Props) => {
|
|||
if (selectedElement) {
|
||||
return selectedElement.getBoundingClientRect()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'incutBubbleMenu',
|
||||
|
@ -313,27 +313,27 @@ 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 { countWords, setEditor } = useEditorContext()
|
||||
|
@ -346,7 +346,7 @@ export const Editor = (props: Props) => {
|
|||
if (html()) {
|
||||
countWords({
|
||||
characters: editor().storage.characterCount.characters(),
|
||||
words: editor().storage.characterCount.words(),
|
||||
words: editor().storage.characterCount.words()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -57,14 +57,14 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
|||
attrs: {
|
||||
src: emb.src,
|
||||
width: emb.width,
|
||||
height: emb.height,
|
||||
},
|
||||
height: emb.height
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'figcaption',
|
||||
content: [{ type: 'text', text: t('Description') }],
|
||||
},
|
||||
],
|
||||
content: [{ type: 'text', text: t('Description') }]
|
||||
}
|
||||
]
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
|
|||
setMenuOpen(false)
|
||||
setSelectedMenuItem()
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const handleUpload = (image: UploadedFile) => {
|
||||
|
|
|
@ -25,7 +25,7 @@ export const InsertLinkForm = (props: Props) => {
|
|||
() => props.editor,
|
||||
(ed) => {
|
||||
return ed?.getAttributes('link').href || ''
|
||||
},
|
||||
}
|
||||
)
|
||||
const handleClearLinkForm = () => {
|
||||
if (currentUrl()) {
|
||||
|
|
|
@ -35,7 +35,7 @@ export const Panel = (props: Props) => {
|
|||
useOutsideClickHandler({
|
||||
containerRef,
|
||||
predicate: () => isEditorPanelVisible(),
|
||||
handler: () => toggleEditorPanel(),
|
||||
handler: () => toggleEditorPanel()
|
||||
})
|
||||
|
||||
useEscKeyDownHandler(() => {
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
createTiptapEditor,
|
||||
useEditorHTML,
|
||||
useEditorIsEmpty,
|
||||
useEditorIsFocused,
|
||||
useEditorIsFocused
|
||||
} from 'solid-tiptap'
|
||||
|
||||
import { useEditorContext } from '../../context/editor'
|
||||
|
@ -73,32 +73,32 @@ const SimplifiedEditor = (props: Props) => {
|
|||
const wrapperEditorElRef: {
|
||||
current: HTMLElement
|
||||
} = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const editorElRef: {
|
||||
current: HTMLElement
|
||||
} = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const textBubbleMenuRef: {
|
||||
current: HTMLDivElement
|
||||
} = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const linkBubbleMenuRef: {
|
||||
current: HTMLDivElement
|
||||
} = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const { setEditor } = useEditorContext()
|
||||
|
||||
const ImageFigure = Figure.extend({
|
||||
name: 'capturedImage',
|
||||
content: 'figcaption image',
|
||||
content: 'figcaption image'
|
||||
})
|
||||
|
||||
const content = props.initialContent
|
||||
|
@ -106,8 +106,8 @@ const SimplifiedEditor = (props: Props) => {
|
|||
element: editorElRef.current,
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: styles.simplifiedEditorField,
|
||||
},
|
||||
class: styles.simplifiedEditorField
|
||||
}
|
||||
},
|
||||
extensions: [
|
||||
Document,
|
||||
|
@ -116,18 +116,18 @@ const SimplifiedEditor = (props: Props) => {
|
|||
Bold,
|
||||
Italic,
|
||||
Link.extend({
|
||||
inclusive: false,
|
||||
inclusive: false
|
||||
}).configure({
|
||||
autolink: true,
|
||||
openOnClick: false,
|
||||
openOnClick: false
|
||||
}),
|
||||
CharacterCount.configure({
|
||||
limit: maxLength,
|
||||
limit: maxLength
|
||||
}),
|
||||
Blockquote.configure({
|
||||
HTMLAttributes: {
|
||||
class: styles.blockQuote,
|
||||
},
|
||||
class: styles.blockQuote
|
||||
}
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'textBubbleMenu',
|
||||
|
@ -137,7 +137,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
const { selection } = state
|
||||
const { empty } = selection
|
||||
return view.hasFocus() && !empty
|
||||
},
|
||||
}
|
||||
}),
|
||||
BubbleMenu.configure({
|
||||
pluginKey: 'linkBubbleMenu',
|
||||
|
@ -148,19 +148,19 @@ const SimplifiedEditor = (props: Props) => {
|
|||
return !empty && shouldShowLinkBubbleMenu()
|
||||
},
|
||||
tippyOptions: {
|
||||
placement: 'bottom',
|
||||
},
|
||||
placement: 'bottom'
|
||||
}
|
||||
}),
|
||||
ImageFigure,
|
||||
Image,
|
||||
Figcaption,
|
||||
Placeholder.configure({
|
||||
emptyNodeClass: styles.emptyNode,
|
||||
placeholder: props.placeholder,
|
||||
}),
|
||||
placeholder: props.placeholder
|
||||
})
|
||||
],
|
||||
autofocus: props.autoFocus,
|
||||
content: content ?? null,
|
||||
content: content ?? null
|
||||
}))
|
||||
|
||||
setEditor(editor)
|
||||
|
@ -172,7 +172,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
() => editor(),
|
||||
(ed) => {
|
||||
return ed?.isActive(name)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const html = useEditorHTML(() => editor())
|
||||
|
@ -191,13 +191,13 @@ const SimplifiedEditor = (props: Props) => {
|
|||
content: [
|
||||
{
|
||||
type: 'image',
|
||||
attrs: { src: image.url },
|
||||
attrs: { src: image.url }
|
||||
},
|
||||
{
|
||||
type: 'figcaption',
|
||||
content: [{ type: 'text', text: image.originalFilename }],
|
||||
},
|
||||
],
|
||||
content: [{ type: 'text', text: image.originalFilename }]
|
||||
}
|
||||
]
|
||||
})
|
||||
.run()
|
||||
hideModal()
|
||||
|
@ -260,7 +260,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
|
||||
const maxHeightStyle = {
|
||||
overflow: 'auto',
|
||||
'max-height': `${props.maxHeight}px`,
|
||||
'max-height': `${props.maxHeight}px`
|
||||
}
|
||||
|
||||
const handleShowLinkBubble = () => {
|
||||
|
@ -282,7 +282,7 @@ const SimplifiedEditor = (props: Props) => {
|
|||
[styles.minimal]: props.variant === 'minimal',
|
||||
[styles.bordered]: props.variant === 'bordered',
|
||||
[styles.isFocused]: isFocused() || !isEmpty(),
|
||||
[styles.labelVisible]: props.label && counter() > 0,
|
||||
[styles.labelVisible]: props.label && counter() > 0
|
||||
})}
|
||||
>
|
||||
<Show when={props.maxLength && editor()}>
|
||||
|
|
|
@ -26,7 +26,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
const isActive = (name: string, attributes?: unknown) =>
|
||||
createEditorTransaction(
|
||||
() => props.editor,
|
||||
(editor) => editor?.isActive(name, attributes),
|
||||
(editor) => editor?.isActive(name, attributes)
|
||||
)
|
||||
|
||||
const [textSizeBubbleOpen, setTextSizeBubbleOpen] = createSignal(false)
|
||||
|
@ -86,7 +86,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
}
|
||||
const value = ed.getAttributes('footnote').value
|
||||
setFootNote(value)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const handleAddFootnote = (footnote) => {
|
||||
|
@ -168,7 +168,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
<button
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: textSizeBubbleOpen(),
|
||||
[styles.bubbleMenuButtonActive]: textSizeBubbleOpen()
|
||||
})}
|
||||
onClick={toggleTextSizePopup}
|
||||
>
|
||||
|
@ -185,7 +185,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isH1(),
|
||||
[styles.bubbleMenuButtonActive]: isH1()
|
||||
})}
|
||||
onClick={() => {
|
||||
props.editor.chain().focus().toggleHeading({ level: 2 }).run()
|
||||
|
@ -202,7 +202,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isH2(),
|
||||
[styles.bubbleMenuButtonActive]: isH2()
|
||||
})}
|
||||
onClick={() => {
|
||||
props.editor.chain().focus().toggleHeading({ level: 3 }).run()
|
||||
|
@ -219,7 +219,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isH3(),
|
||||
[styles.bubbleMenuButtonActive]: isH3()
|
||||
})}
|
||||
onClick={() => {
|
||||
props.editor.chain().focus().toggleHeading({ level: 4 }).run()
|
||||
|
@ -239,7 +239,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isQuote(),
|
||||
[styles.bubbleMenuButtonActive]: isQuote()
|
||||
})}
|
||||
onClick={handleSetPunchline}
|
||||
>
|
||||
|
@ -253,7 +253,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isPunchLine(),
|
||||
[styles.bubbleMenuButtonActive]: isPunchLine()
|
||||
})}
|
||||
onClick={handleSetQuote}
|
||||
>
|
||||
|
@ -270,7 +270,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isIncut(),
|
||||
[styles.bubbleMenuButtonActive]: isIncut()
|
||||
})}
|
||||
onClick={() => {
|
||||
props.editor.chain().focus().toggleArticle().run()
|
||||
|
@ -294,7 +294,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isBold(),
|
||||
[styles.bubbleMenuButtonActive]: isBold()
|
||||
})}
|
||||
onClick={() => props.editor.chain().focus().toggleBold().run()}
|
||||
>
|
||||
|
@ -308,7 +308,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isItalic(),
|
||||
[styles.bubbleMenuButtonActive]: isItalic()
|
||||
})}
|
||||
onClick={() => props.editor.chain().focus().toggleItalic().run()}
|
||||
>
|
||||
|
@ -324,7 +324,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isHighlight(),
|
||||
[styles.bubbleMenuButtonActive]: isHighlight()
|
||||
})}
|
||||
onClick={() => props.editor.chain().focus().toggleHighlight({ color: '#f6e3a1' }).run()}
|
||||
>
|
||||
|
@ -341,7 +341,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
type="button"
|
||||
onClick={handleOpenLinkForm}
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isLink(),
|
||||
[styles.bubbleMenuButtonActive]: isLink()
|
||||
})}
|
||||
>
|
||||
<Icon name="editor-link" />
|
||||
|
@ -356,7 +356,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isFootnote(),
|
||||
[styles.bubbleMenuButtonActive]: isFootnote()
|
||||
})}
|
||||
onClick={handleOpenFootnoteEditor}
|
||||
>
|
||||
|
@ -369,7 +369,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
<button
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: listBubbleOpen(),
|
||||
[styles.bubbleMenuButtonActive]: listBubbleOpen()
|
||||
})}
|
||||
onClick={toggleListPopup}
|
||||
>
|
||||
|
@ -386,7 +386,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isBulletList(),
|
||||
[styles.bubbleMenuButtonActive]: isBulletList()
|
||||
})}
|
||||
onClick={() => {
|
||||
props.editor.chain().focus().toggleBulletList().run()
|
||||
|
@ -403,7 +403,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
|
|||
ref={triggerRef}
|
||||
type="button"
|
||||
class={clsx(styles.bubbleMenuButton, {
|
||||
[styles.bubbleMenuButtonActive]: isOrderedList(),
|
||||
[styles.bubbleMenuButtonActive]: isOrderedList()
|
||||
})}
|
||||
onClick={() => {
|
||||
props.editor.chain().focus().toggleOrderedList().run()
|
||||
|
|
|
@ -37,7 +37,7 @@ export const TopicSelect = (props: TopicSelectProps) => {
|
|||
disable: (topic) => {
|
||||
return props.selectedTopics.some((selectedTopic) => selectedTopic.slug === topic.slug)
|
||||
},
|
||||
createable: createValue,
|
||||
createable: createValue
|
||||
})
|
||||
|
||||
const handleChange = (selectedTopics: Topic[]) => {
|
||||
|
@ -61,7 +61,7 @@ export const TopicSelect = (props: TopicSelectProps) => {
|
|||
return (
|
||||
<div
|
||||
class={clsx(styles.selectedItem, {
|
||||
[styles.mainTopic]: isMainTopic,
|
||||
[styles.mainTopic]: isMainTopic
|
||||
})}
|
||||
onClick={() => handleSelectedItemClick(item)}
|
||||
>
|
||||
|
|
|
@ -48,7 +48,7 @@ export const UploadModalContent = (props: Props) => {
|
|||
source: blob.toString(),
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
file: file,
|
||||
file: file
|
||||
}
|
||||
await runUpload(fileToUpload)
|
||||
} catch (error) {
|
||||
|
@ -72,7 +72,7 @@ export const UploadModalContent = (props: Props) => {
|
|||
} else {
|
||||
setDragError(t('Image format not supported'))
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
const handleDrag = (event: MouseEvent) => {
|
||||
if (event.type === 'dragenter' || event.type === 'dragover') {
|
||||
|
|
|
@ -29,7 +29,7 @@ export const VideoUploader = (props: Props) => {
|
|||
const urlInput: {
|
||||
current: HTMLInputElement
|
||||
} = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const { setRef: dropzoneRef, files: droppedFiles } = createDropzone({
|
||||
|
@ -40,13 +40,13 @@ export const VideoUploader = (props: Props) => {
|
|||
} else if (droppedFiles()[0].file.type.startsWith('video/')) {
|
||||
await showSnackbar({
|
||||
body: t(
|
||||
'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.'
|
||||
)
|
||||
})
|
||||
} else {
|
||||
setError(t('Video format not supported'))
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
const handleDrag = (event) => {
|
||||
if (event.type === 'dragenter' || event.type === 'dragover') {
|
||||
|
@ -85,8 +85,8 @@ export const VideoUploader = (props: Props) => {
|
|||
onClick={() =>
|
||||
showSnackbar({
|
||||
body: t(
|
||||
'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.'
|
||||
)
|
||||
})
|
||||
}
|
||||
ref={dropzoneRef}
|
||||
|
|
|
@ -14,8 +14,8 @@ export default Node.create({
|
|||
name: 'article',
|
||||
defaultOptions: {
|
||||
HTMLAttributes: {
|
||||
'data-type': 'incut',
|
||||
},
|
||||
'data-type': 'incut'
|
||||
}
|
||||
},
|
||||
group: 'block',
|
||||
content: 'block+',
|
||||
|
@ -23,8 +23,8 @@ export default Node.create({
|
|||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'article',
|
||||
},
|
||||
tag: 'article'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -35,11 +35,11 @@ export default Node.create({
|
|||
addAttributes() {
|
||||
return {
|
||||
'data-float': {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
'data-bg': {
|
||||
default: null,
|
||||
},
|
||||
default: null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,7 +60,7 @@ export default Node.create({
|
|||
(value) =>
|
||||
({ commands }) => {
|
||||
return commands.updateAttributes(this.name, { 'data-bg': value })
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -14,18 +14,18 @@ declare module '@tiptap/core' {
|
|||
export const CustomBlockquote = Blockquote.extend({
|
||||
name: 'blockquote',
|
||||
defaultOptions: {
|
||||
HTMLAttributes: {},
|
||||
HTMLAttributes: {}
|
||||
},
|
||||
group: 'block',
|
||||
content: 'block+',
|
||||
addAttributes() {
|
||||
return {
|
||||
'data-float': {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
'data-type': {
|
||||
default: null,
|
||||
},
|
||||
default: null
|
||||
}
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
|
@ -41,7 +41,7 @@ export const CustomBlockquote = Blockquote.extend({
|
|||
(value) =>
|
||||
({ commands }) => {
|
||||
return commands.updateAttributes(this.name, { 'data-float': value })
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -16,20 +16,20 @@ export const CustomImage = Image.extend({
|
|||
addAttributes() {
|
||||
return {
|
||||
src: {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
alt: {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
width: {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
height: {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
'data-float': {
|
||||
default: null,
|
||||
},
|
||||
default: null
|
||||
}
|
||||
}
|
||||
},
|
||||
addCommands() {
|
||||
|
@ -39,14 +39,14 @@ export const CustomImage = Image.extend({
|
|||
({ commands }) => {
|
||||
return commands.insertContent({
|
||||
type: this.name,
|
||||
attrs: options,
|
||||
attrs: options
|
||||
})
|
||||
},
|
||||
setImageFloat:
|
||||
(value) =>
|
||||
({ commands }) => {
|
||||
return commands.updateAttributes(this.name, { 'data-float': value })
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,7 +12,7 @@ export const Figcaption = Node.create({
|
|||
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {},
|
||||
HTMLAttributes: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -25,8 +25,8 @@ export const Figcaption = Node.create({
|
|||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'figcaption',
|
||||
},
|
||||
tag: 'figcaption'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -39,7 +39,7 @@ export const Figcaption = Node.create({
|
|||
(value) =>
|
||||
({ commands }) => {
|
||||
return commands.focus(value)
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,7 +12,7 @@ export const Figure = Node.create({
|
|||
name: 'figure',
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {},
|
||||
HTMLAttributes: {}
|
||||
}
|
||||
},
|
||||
group: 'block',
|
||||
|
@ -24,7 +24,7 @@ export const Figure = Node.create({
|
|||
addAttributes() {
|
||||
return {
|
||||
'data-float': null,
|
||||
'data-type': { default: null },
|
||||
'data-type': { default: null }
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -45,8 +45,8 @@ export const Figure = Node.create({
|
|||
dataType = 'iframe'
|
||||
}
|
||||
return { 'data-type': dataType }
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
|
@ -69,10 +69,10 @@ export const Figure = Node.create({
|
|||
event.preventDefault()
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -82,7 +82,7 @@ export const Figure = Node.create({
|
|||
(value) =>
|
||||
({ commands }) => {
|
||||
return commands.updateAttributes(this.name, { 'data-float': value })
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -14,7 +14,7 @@ export const Footnote = Node.create({
|
|||
name: 'footnote',
|
||||
addOptions() {
|
||||
return {
|
||||
HTMLAttributes: {},
|
||||
HTMLAttributes: {}
|
||||
}
|
||||
},
|
||||
group: 'inline',
|
||||
|
@ -29,18 +29,18 @@ export const Footnote = Node.create({
|
|||
parseHTML: (element) => element.dataset.value || null,
|
||||
renderHTML: (attributes) => {
|
||||
return {
|
||||
'data-value': attributes.value,
|
||||
'data-value': attributes.value
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'footnote',
|
||||
},
|
||||
tag: 'footnote'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -92,7 +92,7 @@ export const Footnote = Node.create({
|
|||
}
|
||||
|
||||
return false
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -24,33 +24,33 @@ export const Iframe = Node.create<IframeOptions>({
|
|||
return {
|
||||
allowFullscreen: true,
|
||||
HTMLAttributes: {
|
||||
class: 'iframe-wrapper',
|
||||
},
|
||||
class: 'iframe-wrapper'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
src: {
|
||||
default: null,
|
||||
default: null
|
||||
},
|
||||
frameborder: {
|
||||
default: 0,
|
||||
default: 0
|
||||
},
|
||||
allowfullscreen: {
|
||||
default: this.options.allowFullscreen,
|
||||
parseHTML: () => this.options.allowFullscreen,
|
||||
parseHTML: () => this.options.allowFullscreen
|
||||
},
|
||||
width: { default: null },
|
||||
height: { default: null },
|
||||
height: { default: null }
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'iframe',
|
||||
},
|
||||
tag: 'iframe'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -69,7 +69,7 @@ export const Iframe = Node.create<IframeOptions>({
|
|||
tr.replaceRangeWith(selection.from, selection.to, node)
|
||||
}
|
||||
return true
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -12,8 +12,8 @@ export const Span = Mark.create({
|
|||
return { class: dom.getAttribute('class') }
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
|
@ -24,8 +24,8 @@ export const Span = Mark.create({
|
|||
addAttributes() {
|
||||
return {
|
||||
class: {
|
||||
default: null,
|
||||
},
|
||||
default: null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -43,7 +43,7 @@ export const ToggleTextWrap = Extension.create({
|
|||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
|||
addOptions() {
|
||||
return {
|
||||
node: 'paragraph',
|
||||
notAfter: ['paragraph'],
|
||||
notAfter: ['paragraph']
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -61,9 +61,9 @@ export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
|||
const lastNode = tr.doc.lastChild
|
||||
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes })
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
|
@ -57,11 +57,11 @@ const desktopCoverImageWidths: Record<ArticleCardProps['desktopCoverSize'], numb
|
|||
XS: 300,
|
||||
S: 400,
|
||||
M: 600,
|
||||
L: 800,
|
||||
L: 800
|
||||
}
|
||||
|
||||
const getTitleAndSubtitle = (
|
||||
article: Shout,
|
||||
article: Shout
|
||||
): {
|
||||
title: string
|
||||
subtitle: string
|
||||
|
@ -101,7 +101,7 @@ const LAYOUT_ASPECT = {
|
|||
music: styles.aspectRatio1x1,
|
||||
literature: styles.aspectRatio16x9,
|
||||
video: styles.aspectRatio16x9,
|
||||
image: styles.aspectRatio4x3,
|
||||
image: styles.aspectRatio4x3
|
||||
}
|
||||
|
||||
export const ArticleCard = (props: ArticleCardProps) => {
|
||||
|
@ -117,7 +117,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
const { title, subtitle } = getTitleAndSubtitle(props.article)
|
||||
|
||||
const formattedDate = createMemo<string>(() =>
|
||||
props.article.published_at ? formatDate(new Date(props.article.published_at * 1000)) : '',
|
||||
props.article.published_at ? formatDate(new Date(props.article.published_at * 1000)) : ''
|
||||
)
|
||||
|
||||
const canEdit = createMemo(
|
||||
|
@ -125,14 +125,14 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
Boolean(author()?.id) &&
|
||||
(props.article?.authors?.some((a) => Boolean(a) && a?.id === author().id) ||
|
||||
props.article?.created_by?.id === author().id ||
|
||||
session()?.user?.roles.includes('editor')),
|
||||
session()?.user?.roles.includes('editor'))
|
||||
)
|
||||
|
||||
const scrollToComments = (event) => {
|
||||
event.preventDefault()
|
||||
openPage(router, 'article', { slug: props.article.slug })
|
||||
changeSearchParams({
|
||||
scrollTo: 'comments',
|
||||
scrollTo: 'comments'
|
||||
})
|
||||
}
|
||||
return (
|
||||
|
@ -150,14 +150,14 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
[styles.shoutCardSingle]: props.settings?.isSingle,
|
||||
[styles.shoutCardBeside]: props.settings?.isBeside,
|
||||
[styles.shoutCardNoImage]: !props.article.cover,
|
||||
[aspectRatio()]: props.withAspectRatio,
|
||||
[aspectRatio()]: props.withAspectRatio
|
||||
})}
|
||||
>
|
||||
<Show when={!(props.settings?.noimage || props.settings?.isFeedMode)}>
|
||||
<div class={styles.shoutCardCoverContainer}>
|
||||
<div
|
||||
class={clsx(styles.shoutCardCover, {
|
||||
[styles.loading]: props.article.cover && isCoverImageLoading(),
|
||||
[styles.loading]: props.article.cover && isCoverImageLoading()
|
||||
})}
|
||||
>
|
||||
<Show
|
||||
|
@ -207,7 +207,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
|
|||
|
||||
<div
|
||||
class={clsx(styles.shoutCardTitlesContainer, {
|
||||
[styles.shoutCardTitlesContainerFeedMode]: props.settings?.isFeedMode,
|
||||
[styles.shoutCardTitlesContainerFeedMode]: props.settings?.isFeedMode
|
||||
})}
|
||||
>
|
||||
<a href={getPagePath(router, 'article', { slug: props.article.slug })}>
|
||||
|
|
|
@ -43,7 +43,7 @@ export const Beside = (props: Props) => {
|
|||
'col-lg-8',
|
||||
styles[
|
||||
`besideRatingColumn${props.wrapper.charAt(0).toUpperCase() + props.wrapper.slice(1)}`
|
||||
],
|
||||
]
|
||||
)}
|
||||
>
|
||||
<Show when={!!props.title}>
|
||||
|
@ -67,7 +67,7 @@ export const Beside = (props: Props) => {
|
|||
</Show>
|
||||
<ul
|
||||
class={clsx(styles.besideColumn, {
|
||||
[styles.besideColumnTopViewed]: props.wrapper === 'top-article',
|
||||
[styles.besideColumnTopViewed]: props.wrapper === 'top-article'
|
||||
})}
|
||||
>
|
||||
<For each={[...props.values]}>
|
||||
|
@ -89,7 +89,7 @@ export const Beside = (props: Props) => {
|
|||
<AuthorBadge
|
||||
author={value as Author}
|
||||
isFollowed={{
|
||||
value: isOwnerSubscribed(value.id),
|
||||
value: isOwnerSubscribed(value.id)
|
||||
}}
|
||||
/>
|
||||
</Show>
|
||||
|
|
|
@ -18,7 +18,7 @@ export const CardTopic = (props: CardTopicProps) => {
|
|||
<div
|
||||
class={clsx(styles.shoutTopic, props.class, {
|
||||
[styles.shoutTopicFloorImportant]: props.isFloorImportant,
|
||||
[styles.shoutTopicFeedMode]: props.isFeedMode,
|
||||
[styles.shoutTopicFeedMode]: props.isFeedMode
|
||||
})}
|
||||
>
|
||||
<a href={getPagePath(router, 'topic', { slug: props.slug })}>{props.title}</a>
|
||||
|
|
|
@ -26,7 +26,7 @@ export default (props: GroupProps) => {
|
|||
nosubtitle: false,
|
||||
noicon: true,
|
||||
isBigTitle: true,
|
||||
nodate: true,
|
||||
nodate: true
|
||||
}}
|
||||
desktopCoverSize="M"
|
||||
/>
|
||||
|
@ -60,7 +60,7 @@ export default (props: GroupProps) => {
|
|||
noimage: true,
|
||||
isBigTitle: true,
|
||||
isCompact: true,
|
||||
nodate: true,
|
||||
nodate: true
|
||||
}}
|
||||
desktopCoverSize="XS"
|
||||
/>
|
||||
|
@ -77,7 +77,7 @@ export default (props: GroupProps) => {
|
|||
noimage: true,
|
||||
isBigTitle: true,
|
||||
isCompact: true,
|
||||
nodate: true,
|
||||
nodate: true
|
||||
}}
|
||||
desktopCoverSize="XS"
|
||||
/>
|
||||
|
|
|
@ -21,7 +21,7 @@ export const Row1 = (props: {
|
|||
isSingle: true,
|
||||
nodate: props.nodate,
|
||||
noAuthorLink: props.noAuthorLink,
|
||||
noauthor: props.noauthor,
|
||||
noauthor: props.noauthor
|
||||
}}
|
||||
desktopCoverSize="L"
|
||||
/>
|
||||
|
|
|
@ -36,7 +36,7 @@ export const Row2 = (props: {
|
|||
isWithCover: props.isEqual || className === 'col-md-16',
|
||||
nodate: props.isEqual || props.nodate,
|
||||
noAuthorLink: props.noAuthorLink,
|
||||
noauthor: props.noauthor,
|
||||
noauthor: props.noauthor
|
||||
}}
|
||||
desktopCoverSize={desktopCoverSize}
|
||||
/>
|
||||
|
|
|
@ -28,7 +28,7 @@ export const Row3 = (props: {
|
|||
settings={{
|
||||
nodate: props.nodate,
|
||||
noAuthorLink: props.noAuthorLink,
|
||||
noauthor: props.noauthor,
|
||||
noauthor: props.noauthor
|
||||
}}
|
||||
desktopCoverSize="S"
|
||||
/>
|
||||
|
|
|
@ -18,7 +18,7 @@ export default (props: { articles: Shout[] }) => (
|
|||
isWithCover: true,
|
||||
isBigTitle: true,
|
||||
isVertical: true,
|
||||
nodate: true,
|
||||
nodate: true
|
||||
}}
|
||||
desktopCoverSize="S"
|
||||
/>
|
||||
|
|
|
@ -35,7 +35,7 @@ export const Sidebar = () => {
|
|||
<a
|
||||
href={getPagePath(router, 'feed')}
|
||||
class={clsx({
|
||||
[styles.selected]: page().route === 'feed',
|
||||
[styles.selected]: page().route === 'feed'
|
||||
})}
|
||||
>
|
||||
<span class={styles.sidebarItemName}>
|
||||
|
@ -48,7 +48,7 @@ export const Sidebar = () => {
|
|||
<a
|
||||
href={getPagePath(router, 'feedMy')}
|
||||
class={clsx({
|
||||
[styles.selected]: page().route === 'feedMy',
|
||||
[styles.selected]: page().route === 'feedMy'
|
||||
})}
|
||||
>
|
||||
<span class={styles.sidebarItemName}>
|
||||
|
@ -61,7 +61,7 @@ export const Sidebar = () => {
|
|||
<a
|
||||
href={getPagePath(router, 'feedCollaborations')}
|
||||
class={clsx({
|
||||
[styles.selected]: page().route === 'feedCollaborations',
|
||||
[styles.selected]: page().route === 'feedCollaborations'
|
||||
})}
|
||||
>
|
||||
<span class={styles.sidebarItemName}>
|
||||
|
@ -74,7 +74,7 @@ export const Sidebar = () => {
|
|||
<a
|
||||
href={getPagePath(router, 'feedDiscussions')}
|
||||
class={clsx({
|
||||
[styles.selected]: page().route === 'feedDiscussions',
|
||||
[styles.selected]: page().route === 'feedDiscussions'
|
||||
})}
|
||||
>
|
||||
<span class={styles.sidebarItemName}>
|
||||
|
@ -87,7 +87,7 @@ export const Sidebar = () => {
|
|||
<a
|
||||
href={getPagePath(router, 'feedBookmarks')}
|
||||
class={clsx({
|
||||
[styles.selected]: page().route === 'feedBookmarks',
|
||||
[styles.selected]: page().route === 'feedBookmarks'
|
||||
})}
|
||||
>
|
||||
<span class={styles.sidebarItemName}>
|
||||
|
@ -100,7 +100,7 @@ export const Sidebar = () => {
|
|||
<a
|
||||
href={getPagePath(router, 'feedNotifications')}
|
||||
class={clsx({
|
||||
[styles.selected]: page().route === 'feedNotifications',
|
||||
[styles.selected]: page().route === 'feedNotifications'
|
||||
})}
|
||||
>
|
||||
<span class={styles.sidebarItemName}>
|
||||
|
|
|
@ -51,7 +51,7 @@ const CreateModalContent = (props: Props) => {
|
|||
const handleClick = (user) => {
|
||||
setCollectionToInvite((userCollection) => {
|
||||
return userCollection.map((clickedUser) =>
|
||||
user.id === clickedUser.id ? { ...clickedUser, selected: !clickedUser.selected } : clickedUser,
|
||||
user.id === clickedUser.id ? { ...clickedUser, selected: !clickedUser.selected } : clickedUser
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ const colors = [
|
|||
'#668cff',
|
||||
'#c34cfe',
|
||||
'#e699ff',
|
||||
'#6633ff',
|
||||
'#6633ff'
|
||||
]
|
||||
|
||||
const getById = (letter: string) =>
|
||||
|
@ -44,7 +44,7 @@ const DialogAvatar = (props: Props) => {
|
|||
class={clsx(styles.DialogAvatar, props.class, {
|
||||
[styles.online]: props.online,
|
||||
[styles.bordered]: props.bordered,
|
||||
[styles.small]: props.size === 'small',
|
||||
[styles.small]: props.size === 'small'
|
||||
})}
|
||||
style={{ 'background-color': `${randomBg()}` }}
|
||||
>
|
||||
|
|
|
@ -27,7 +27,7 @@ type DialogProps = {
|
|||
const DialogCard = (props: DialogProps) => {
|
||||
const { t, formatTime } = useLocalize()
|
||||
const companions = createMemo(() =>
|
||||
props.members?.filter((member: ChatMember) => member.id !== props.ownId),
|
||||
props.members?.filter((member: ChatMember) => member.id !== props.ownId)
|
||||
)
|
||||
|
||||
const names = createMemo<string>(() => (companions() || []).map((companion) => companion.name).join(', '))
|
||||
|
@ -37,7 +37,7 @@ const DialogCard = (props: DialogProps) => {
|
|||
<div
|
||||
class={clsx(styles.DialogCard, {
|
||||
[styles.opened]: props.isOpened,
|
||||
[styles.hovered]: !props.isChatHeader,
|
||||
[styles.hovered]: !props.isChatHeader
|
||||
})}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const MessageActionsPopup = (props: MessageActionsPopupProps) => {
|
|||
{ name: t('Pin'), action: 'pin' },
|
||||
{ name: t('Forward'), action: 'forward' },
|
||||
{ name: t('Select'), action: 'select' },
|
||||
{ name: t('Delete'), action: 'delete' },
|
||||
{ name: t('Delete'), action: 'delete' }
|
||||
]
|
||||
createEffect(() => {
|
||||
if (props.actionSelect) props.actionSelect(selectedAction())
|
||||
|
|
|
@ -19,7 +19,7 @@ const QuotedMessage = (props: QuotedMessage) => {
|
|||
class={clsx(styles.QuotedMessage, {
|
||||
[styles.reply]: props.variant === 'reply',
|
||||
[styles.inline]: props.variant === 'inline',
|
||||
[styles.own]: props.isOwn,
|
||||
[styles.own]: props.isOwn
|
||||
})}
|
||||
>
|
||||
<Show when={props.variant === 'reply'}>
|
||||
|
|
|
@ -16,7 +16,7 @@ export const AuthModalHeader = (props: Props) => {
|
|||
const { source } = searchParams()
|
||||
|
||||
const generateModalTextsFromSource = (
|
||||
modalType: 'login' | 'register',
|
||||
modalType: 'login' | 'register'
|
||||
): { title: string; description: string } => {
|
||||
const title = modalType === 'login' ? 'Welcome to Discours' : 'Create account'
|
||||
|
||||
|
@ -24,53 +24,53 @@ export const AuthModalHeader = (props: Props) => {
|
|||
case 'create': {
|
||||
return {
|
||||
title: t(`${title} to publish articles`),
|
||||
description: '',
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
case 'bookmark': {
|
||||
return {
|
||||
title: t(`${title} to add to your bookmarks`),
|
||||
description: t(
|
||||
'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'
|
||||
)
|
||||
}
|
||||
}
|
||||
case 'discussions': {
|
||||
return {
|
||||
title: t(`${title} to participate in discussions`),
|
||||
description: t(
|
||||
"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"
|
||||
)
|
||||
}
|
||||
}
|
||||
case 'follow': {
|
||||
return {
|
||||
title: t(`${title} to subscribe`),
|
||||
description: t(
|
||||
'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'
|
||||
)
|
||||
}
|
||||
}
|
||||
case 'subscribe': {
|
||||
return {
|
||||
title: t(`${title} to subscribe to new publications`),
|
||||
description: t(
|
||||
'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'
|
||||
)
|
||||
}
|
||||
}
|
||||
case 'vote': {
|
||||
return {
|
||||
title: t(`${title} to vote`),
|
||||
description: t(
|
||||
'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'
|
||||
)
|
||||
}
|
||||
}
|
||||
default: {
|
||||
return {
|
||||
title: t(title),
|
||||
description: '',
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ export const ChangePasswordForm = () => {
|
|||
<h4>{t('Enter a new password')}</h4>
|
||||
<div class={styles.authSubtitle}>
|
||||
{t(
|
||||
'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'
|
||||
)}
|
||||
</div>
|
||||
<Show when={validationErrors()}>
|
||||
|
@ -83,7 +83,7 @@ export const ChangePasswordForm = () => {
|
|||
class={styles.authLink}
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
mode: 'login',
|
||||
mode: 'login'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -100,7 +100,7 @@ export const LoginForm = () => {
|
|||
if (errors.some((error) => error.message.includes('bad user credentials'))) {
|
||||
setValidationErrors((prev) => ({
|
||||
...prev,
|
||||
password: t('Something went wrong, check email and password'),
|
||||
password: t('Something went wrong, check email and password')
|
||||
}))
|
||||
} else {
|
||||
setSubmitError(t('Error'))
|
||||
|
@ -136,7 +136,7 @@ export const LoginForm = () => {
|
|||
</Show>
|
||||
<div
|
||||
class={clsx('pretty-form__item', {
|
||||
'pretty-form__item--error': validationErrors().email,
|
||||
'pretty-form__item--error': validationErrors().email
|
||||
})}
|
||||
>
|
||||
<input
|
||||
|
@ -171,7 +171,7 @@ export const LoginForm = () => {
|
|||
class="link"
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
mode: 'send-reset-link',
|
||||
mode: 'send-reset-link'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
@ -188,7 +188,7 @@ export const LoginForm = () => {
|
|||
class={styles.authLink}
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
mode: 'register',
|
||||
mode: 'register'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -55,15 +55,15 @@ export const PasswordField = (props: Props) => {
|
|||
() => {
|
||||
props.errorMessage?.(error())
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
{ defer: true }
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<div class={clsx(styles.PassportField, props.class)}>
|
||||
<div
|
||||
class={clsx('pretty-form__item', {
|
||||
'pretty-form__item--error': error() && props.variant !== 'login',
|
||||
'pretty-form__item--error': error() && props.variant !== 'login'
|
||||
})}
|
||||
>
|
||||
<input
|
||||
|
|
|
@ -99,7 +99,7 @@ export const RegisterForm = () => {
|
|||
email: cleanEmail,
|
||||
password: password(),
|
||||
confirm_password: password(),
|
||||
redirect_uri: window.location.origin,
|
||||
redirect_uri: window.location.origin
|
||||
}
|
||||
const { errors } = await signUp(opts)
|
||||
if (errors) return
|
||||
|
@ -114,7 +114,7 @@ export const RegisterForm = () => {
|
|||
const handleResendLink = async (_ev) => {
|
||||
const response: GenericResponse = await resendVerifyEmail({
|
||||
email: email(),
|
||||
identifier: 'basic_signup',
|
||||
identifier: 'basic_signup'
|
||||
})
|
||||
setIsSuccess(response?.message === 'Verification email has been sent. Please check your inbox')
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ export const RegisterForm = () => {
|
|||
{t('resend confirmation link')}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
)
|
||||
}))
|
||||
break
|
||||
|
||||
|
@ -144,7 +144,7 @@ export const RegisterForm = () => {
|
|||
{t('enter')}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
)
|
||||
}))
|
||||
break
|
||||
case 'registered':
|
||||
|
@ -157,7 +157,7 @@ export const RegisterForm = () => {
|
|||
{t('Set the new password').toLocaleLowerCase()}
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
)
|
||||
}))
|
||||
break
|
||||
default:
|
||||
|
@ -187,7 +187,7 @@ export const RegisterForm = () => {
|
|||
</Show>
|
||||
<div
|
||||
class={clsx('pretty-form__item', {
|
||||
'pretty-form__item--error': validationErrors().fullName,
|
||||
'pretty-form__item--error': validationErrors().fullName
|
||||
})}
|
||||
>
|
||||
<input
|
||||
|
@ -206,7 +206,7 @@ export const RegisterForm = () => {
|
|||
|
||||
<div
|
||||
class={clsx('pretty-form__item', {
|
||||
'pretty-form__item--error': validationErrors().email && !emailStatus(),
|
||||
'pretty-form__item--error': validationErrors().email && !emailStatus()
|
||||
})}
|
||||
>
|
||||
<input
|
||||
|
@ -250,7 +250,7 @@ export const RegisterForm = () => {
|
|||
class={styles.authLink}
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
mode: 'login',
|
||||
mode: 'login'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -58,7 +58,7 @@ export const SendResetLinkForm = () => {
|
|||
try {
|
||||
const { data, errors } = await forgotPassword({
|
||||
email: email(),
|
||||
redirect_uri: window.location.origin,
|
||||
redirect_uri: window.location.origin
|
||||
})
|
||||
console.debug('[SendResetLinkForm] authorizer response:', data)
|
||||
if (errors?.some((error) => error.message.includes('bad user credentials'))) {
|
||||
|
@ -83,7 +83,7 @@ export const SendResetLinkForm = () => {
|
|||
<div class={styles.authSubtitle}>{t(message()) || t('Please give us your email address')}</div>
|
||||
<div
|
||||
class={clsx('pretty-form__item', {
|
||||
'pretty-form__item--error': validationErrors().email,
|
||||
'pretty-form__item--error': validationErrors().email
|
||||
})}
|
||||
>
|
||||
<input
|
||||
|
@ -105,7 +105,7 @@ export const SendResetLinkForm = () => {
|
|||
class={'link'}
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
mode: 'login',
|
||||
mode: 'login'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
@ -132,7 +132,7 @@ export const SendResetLinkForm = () => {
|
|||
class={styles.authLink}
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
mode: 'login',
|
||||
mode: 'login'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -22,7 +22,7 @@ const AUTH_MODAL_MODES: Record<AuthModalMode, Component> = {
|
|||
register: RegisterForm,
|
||||
'send-reset-link': SendResetLinkForm,
|
||||
'confirm-email': EmailConfirm,
|
||||
'change-password': ChangePasswordForm,
|
||||
'change-password': ChangePasswordForm
|
||||
}
|
||||
|
||||
export const AuthModal = () => {
|
||||
|
@ -46,7 +46,7 @@ export const AuthModal = () => {
|
|||
ref={(el) => (rootRef.current = el)}
|
||||
class={clsx(styles.view, {
|
||||
row: !source,
|
||||
[styles.signUp]: mode() === 'register' || mode() === 'confirm-email',
|
||||
[styles.signUp]: mode() === 'register' || mode() === 'confirm-email'
|
||||
})}
|
||||
>
|
||||
<Show when={!source}>
|
||||
|
@ -59,7 +59,7 @@ export const AuthModal = () => {
|
|||
<h4>{t('Join the global community of authors!')}</h4>
|
||||
<p class={styles.authBenefits}>
|
||||
{t(
|
||||
'Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine',
|
||||
'Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine'
|
||||
)}
|
||||
.
|
||||
{t('New stories every day and even more!')}
|
||||
|
@ -82,7 +82,7 @@ export const AuthModal = () => {
|
|||
</Show>
|
||||
<div
|
||||
class={clsx(styles.auth, {
|
||||
'col-md-12': !source,
|
||||
'col-md-12': !source
|
||||
})}
|
||||
>
|
||||
<Dynamic component={AUTH_MODAL_MODES[mode()]} />
|
||||
|
|
|
@ -165,7 +165,7 @@ export const Header = (props: Props) => {
|
|||
[styles.headerScrolledTop]: !getIsScrollingBottom() && getIsScrolled(),
|
||||
[styles.headerScrolledBottom]:
|
||||
(getIsScrollingBottom() && getIsScrolled() && !isProfilePopupVisible()) || isSharePopupVisible(),
|
||||
[styles.headerWithTitle]: Boolean(props.title),
|
||||
[styles.headerWithTitle]: Boolean(props.title)
|
||||
}}
|
||||
>
|
||||
<Modal
|
||||
|
@ -320,7 +320,7 @@ export const Header = (props: Props) => {
|
|||
<p
|
||||
class={styles.mobileDescription}
|
||||
innerHTML={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'
|
||||
)}
|
||||
/>
|
||||
<div class={styles.mobileCopyright}>
|
||||
|
|
|
@ -53,7 +53,7 @@ export const HeaderAuth = (props: Props) => {
|
|||
const isSaveButtonVisible = createMemo(() => isAuthenticated() && isEditorPage())
|
||||
const isCreatePostButtonVisible = createMemo(() => isAuthenticated() && !isEditorPage())
|
||||
const isAuthenticatedControlsVisible = createMemo(
|
||||
() => isAuthenticated() && session()?.user?.email_verified,
|
||||
() => isAuthenticated() && session()?.user?.email_verified
|
||||
)
|
||||
|
||||
const handleBurgerButtonClick = () => {
|
||||
|
@ -147,7 +147,7 @@ export const HeaderAuth = (props: Props) => {
|
|||
{renderIconedButton({
|
||||
value: t('Save'),
|
||||
icon: 'save',
|
||||
action: handleSaveButtonClick,
|
||||
action: handleSaveButtonClick
|
||||
})}
|
||||
</div>
|
||||
|
||||
|
@ -155,7 +155,7 @@ export const HeaderAuth = (props: Props) => {
|
|||
{renderIconedButton({
|
||||
value: t('Publish'),
|
||||
icon: 'publish',
|
||||
action: () => publishShout(form),
|
||||
action: () => publishShout(form)
|
||||
})}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ export const Modal = (props: Props) => {
|
|||
<Show when={visible()}>
|
||||
<div
|
||||
class={clsx(styles.backdrop, [styles[`modal-${props.name}`]], {
|
||||
[styles.isMobile]: isMobileView(),
|
||||
[styles.isMobile]: isMobileView()
|
||||
})}
|
||||
onClick={handleHide}
|
||||
>
|
||||
|
@ -66,7 +66,7 @@ export const Modal = (props: Props) => {
|
|||
[styles.narrow]: props.variant === 'narrow',
|
||||
'col-auto col-md-20 offset-md-2 col-lg-14 offset-lg-5': props.variant === 'medium',
|
||||
[styles.noPadding]: props.noPadding,
|
||||
[styles.maxHeight]: props.maxHeight,
|
||||
[styles.maxHeight]: props.maxHeight
|
||||
})}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
|
|
|
@ -23,7 +23,7 @@ import styles from './SearchModal.module.scss'
|
|||
const getSearchCoincidences = ({ str, intersection }: { str: string; intersection: string }) =>
|
||||
`<span>${str.replaceAll(
|
||||
new RegExp(intersection, 'gi'),
|
||||
(casePreservedMatch) => `<span class="blackModeIntersection">${casePreservedMatch}</span>`,
|
||||
(casePreservedMatch) => `<span class="blackModeIntersection">${casePreservedMatch}</span>`
|
||||
)}</span>`
|
||||
|
||||
const prepareSearchResults = (list: Shout[], searchValue: string) =>
|
||||
|
@ -33,15 +33,15 @@ const prepareSearchResults = (list: Shout[], searchValue: string) =>
|
|||
title: article.title
|
||||
? getSearchCoincidences({
|
||||
str: article.title,
|
||||
intersection: searchValue,
|
||||
intersection: searchValue
|
||||
})
|
||||
: '',
|
||||
subtitle: article.subtitle
|
||||
? getSearchCoincidences({
|
||||
str: article.subtitle,
|
||||
intersection: searchValue,
|
||||
intersection: searchValue
|
||||
})
|
||||
: '',
|
||||
: ''
|
||||
}))
|
||||
|
||||
export const SearchModal = () => {
|
||||
|
@ -59,7 +59,7 @@ export const SearchModal = () => {
|
|||
const { hasMore, newShouts } = await loadShoutsSearch({
|
||||
limit: FEED_PAGE_SIZE,
|
||||
text: inputValue(),
|
||||
offset: offset(),
|
||||
offset: offset()
|
||||
})
|
||||
setIsLoading(false)
|
||||
setOffset(newShouts.length)
|
||||
|
@ -68,8 +68,8 @@ export const SearchModal = () => {
|
|||
},
|
||||
{
|
||||
ssrLoadFrom: 'initial',
|
||||
initialValue: null,
|
||||
},
|
||||
initialValue: null
|
||||
}
|
||||
)
|
||||
|
||||
let searchEl: HTMLInputElement
|
||||
|
@ -123,7 +123,7 @@ export const SearchModal = () => {
|
|||
<p
|
||||
class={styles.searchDescription}
|
||||
innerHTML={t(
|
||||
'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'
|
||||
)}
|
||||
/>
|
||||
|
||||
|
@ -137,7 +137,7 @@ export const SearchModal = () => {
|
|||
settings={{
|
||||
isFloorImportant: true,
|
||||
isSingle: true,
|
||||
nodate: true,
|
||||
nodate: true
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -15,7 +15,7 @@ export const Snackbar = () => {
|
|||
<div
|
||||
class={clsx(styles.snackbar, {
|
||||
[styles.error]: snackbarMessage()?.type === 'error',
|
||||
[styles.success]: snackbarMessage()?.type === 'success',
|
||||
[styles.success]: snackbarMessage()?.type === 'success'
|
||||
})}
|
||||
>
|
||||
<ShowOnlyOnClient>
|
||||
|
|
|
@ -55,20 +55,20 @@ export const NotificationsPanel = (props: Props) => {
|
|||
loadedNotificationsCount,
|
||||
totalNotificationsCount,
|
||||
loadNotificationsGrouped,
|
||||
markSeenAll,
|
||||
markSeenAll
|
||||
} = useNotifications()
|
||||
const handleHide = () => {
|
||||
props.onClose()
|
||||
}
|
||||
|
||||
const panelRef: { current: HTMLDivElement } = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
useOutsideClickHandler({
|
||||
containerRef: panelRef,
|
||||
predicate: () => props.isOpen,
|
||||
handler: () => handleHide(),
|
||||
handler: () => handleHide()
|
||||
})
|
||||
|
||||
let windowScrollTop = 0
|
||||
|
@ -101,13 +101,13 @@ export const NotificationsPanel = (props: Props) => {
|
|||
|
||||
const yesterdayNotifications = createMemo(() => {
|
||||
return sortedNotifications().filter((notification) =>
|
||||
isYesterday(new Date(notification.updated_at * 1000)),
|
||||
isYesterday(new Date(notification.updated_at * 1000))
|
||||
)
|
||||
})
|
||||
|
||||
const earlierNotifications = createMemo(() => {
|
||||
return sortedNotifications().filter((notification) =>
|
||||
isEarlier(new Date(notification.updated_at * 1000)),
|
||||
isEarlier(new Date(notification.updated_at * 1000))
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -158,14 +158,14 @@ export const NotificationsPanel = (props: Props) => {
|
|||
await loadNextPage()
|
||||
setIsLoading(false)
|
||||
}
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
class={clsx(styles.container, {
|
||||
[styles.isOpened]: props.isOpen,
|
||||
[styles.isOpened]: props.isOpen
|
||||
})}
|
||||
>
|
||||
<div ref={(el) => (panelRef.current = el)} class={styles.panel}>
|
||||
|
|
|
@ -100,7 +100,7 @@ export const ProfileSettings = () => {
|
|||
const isConfirmed = await showConfirm({
|
||||
confirmBody: t('Do you really want to reset all changes?'),
|
||||
confirmButtonVariant: 'primary',
|
||||
declineButtonVariant: 'secondary',
|
||||
declineButtonVariant: 'secondary'
|
||||
})
|
||||
if (isConfirmed) {
|
||||
setForm(clone(prevForm))
|
||||
|
@ -140,7 +140,7 @@ export const ProfileSettings = () => {
|
|||
const handleBeforeUnload = (event) => {
|
||||
if (!deepEqual(form, prevForm)) {
|
||||
event.returnValue = t(
|
||||
'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?'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -191,8 +191,8 @@ export const ProfileSettings = () => {
|
|||
style={{
|
||||
'background-image': `url(${getImageUrl(form.pic, {
|
||||
width: 180,
|
||||
height: 180,
|
||||
})})`,
|
||||
height: 180
|
||||
})})`
|
||||
}}
|
||||
/>
|
||||
<div class={styles.controls}>
|
||||
|
@ -235,7 +235,7 @@ export const ProfileSettings = () => {
|
|||
<h4>{t('Name')}</h4>
|
||||
<p class="description">
|
||||
{t(
|
||||
'Your name will appear on your profile page and as your signature in publications, comments and responses.',
|
||||
'Your name will appear on your profile page and as your signature in publications, comments and responses.'
|
||||
)}
|
||||
</p>
|
||||
<div class="pretty-form__item">
|
||||
|
|
|
@ -25,7 +25,7 @@ const scrollToHeader = (element) => {
|
|||
top:
|
||||
element.getBoundingClientRect().top -
|
||||
document.body.getBoundingClientRect().top -
|
||||
DEFAULT_HEADER_OFFSET,
|
||||
DEFAULT_HEADER_OFFSET
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,8 @@ export const TableOfContents = (props: Props) => {
|
|||
setHeadings(
|
||||
// eslint-disable-next-line unicorn/prefer-spread
|
||||
Array.from(
|
||||
document.querySelector(props.parentSelector).querySelectorAll<HTMLElement>('h1, h2, h3, h4'),
|
||||
),
|
||||
document.querySelector(props.parentSelector).querySelectorAll<HTMLElement>('h1, h2, h3, h4')
|
||||
)
|
||||
)
|
||||
setAreHeadingsLoaded(true)
|
||||
}
|
||||
|
@ -62,8 +62,8 @@ export const TableOfContents = (props: Props) => {
|
|||
createEffect(
|
||||
on(
|
||||
() => props.body,
|
||||
() => debouncedUpdateHeadings(),
|
||||
),
|
||||
() => debouncedUpdateHeadings()
|
||||
)
|
||||
)
|
||||
|
||||
onMount(() => {
|
||||
|
@ -79,7 +79,7 @@ export const TableOfContents = (props: Props) => {
|
|||
>
|
||||
<div
|
||||
class={clsx(styles.TableOfContentsFixedWrapper, {
|
||||
[styles.TableOfContentsFixedWrapperLefted]: props.variant === 'editor',
|
||||
[styles.TableOfContentsFixedWrapperLefted]: props.variant === 'editor'
|
||||
})}
|
||||
>
|
||||
<div class={styles.TableOfContentsContainer}>
|
||||
|
@ -96,7 +96,7 @@ export const TableOfContents = (props: Props) => {
|
|||
class={clsx(styles.TableOfContentsHeadingsItem, {
|
||||
[styles.TableOfContentsHeadingsItemH3]: h.nodeName === 'H3',
|
||||
[styles.TableOfContentsHeadingsItemH4]: h.nodeName === 'H4',
|
||||
[styles.active]: index() === activeHeaderIndex(),
|
||||
[styles.active]: index() === activeHeaderIndex()
|
||||
})}
|
||||
innerHTML={h.textContent}
|
||||
onClick={(e) => {
|
||||
|
@ -115,9 +115,9 @@ export const TableOfContents = (props: Props) => {
|
|||
class={clsx(
|
||||
styles.TableOfContentsPrimaryButton,
|
||||
{
|
||||
[styles.TableOfContentsPrimaryButtonLefted]: props.variant === 'editor' && !isVisible(),
|
||||
[styles.TableOfContentsPrimaryButtonLefted]: props.variant === 'editor' && !isVisible()
|
||||
},
|
||||
'd-none d-xl-block',
|
||||
'd-none d-xl-block'
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
|
@ -153,9 +153,9 @@ export const TableOfContents = (props: Props) => {
|
|||
class={clsx(
|
||||
styles.TableOfContentsPrimaryButton,
|
||||
{
|
||||
[styles.TableOfContentsPrimaryButtonLefted]: props.variant === 'editor' && !isVisible(),
|
||||
[styles.TableOfContentsPrimaryButtonLefted]: props.variant === 'editor' && !isVisible()
|
||||
},
|
||||
'd-xl-none',
|
||||
'd-xl-none'
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
|
|
|
@ -35,7 +35,7 @@ interface TopicProps {
|
|||
export const TopicCard = (props: TopicProps) => {
|
||||
const { t, lang } = useLocalize()
|
||||
const title = createMemo(() =>
|
||||
capitalize(lang() === 'en' ? props.topic.slug.replaceAll('-', ' ') : props.topic.title || ''),
|
||||
capitalize(lang() === 'en' ? props.topic.slug.replaceAll('-', ' ') : props.topic.title || '')
|
||||
)
|
||||
const { author, requireAuthentication } = useSession()
|
||||
const { setFollowing, loading: subLoading } = useFollowing()
|
||||
|
@ -74,7 +74,7 @@ export const TopicCard = (props: TopicProps) => {
|
|||
classList={{
|
||||
row: !props.subscribeButtonBottom,
|
||||
[styles.topicCompact]: props.compact,
|
||||
[styles.topicInRow]: props.isTopicInRow,
|
||||
[styles.topicInRow]: props.isTopicInRow
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
@ -85,7 +85,7 @@ export const TopicCard = (props: TopicProps) => {
|
|||
props.subscribeButtonBottom ||
|
||||
props.isNarrow ||
|
||||
props.compact
|
||||
),
|
||||
)
|
||||
}}
|
||||
>
|
||||
<Show when={title() && !props.isCardMode}>
|
||||
|
@ -120,7 +120,7 @@ export const TopicCard = (props: TopicProps) => {
|
|||
classList={{
|
||||
'col-sm-6 col-md-24 col-lg-10 col-xl-9': props.isNarrow,
|
||||
'col-24 col-sm-7 col-md-6': props.compact,
|
||||
'col-sm-7 col-md-6': !(props.subscribeButtonBottom || props.isNarrow || props.compact),
|
||||
'col-sm-7 col-md-6': !(props.subscribeButtonBottom || props.isNarrow || props.compact)
|
||||
}}
|
||||
>
|
||||
<ShowOnlyOnClient>
|
||||
|
@ -143,7 +143,7 @@ export const TopicCard = (props: TopicProps) => {
|
|||
isSubscribeButton={true}
|
||||
class={clsx(styles.actionButton, {
|
||||
[styles.isSubscribing]: subLoading(),
|
||||
[stylesButton.subscribed]: followed(),
|
||||
[stylesButton.subscribed]: followed()
|
||||
})}
|
||||
// disabled={subLoading()}
|
||||
/>
|
||||
|
|
|
@ -46,8 +46,8 @@ export const TopicBadge = (props: Props) => {
|
|||
() => props.isFollowed,
|
||||
() => {
|
||||
setIsFollowed(props.isFollowed.value)
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
const title = () =>
|
||||
|
@ -61,11 +61,11 @@ export const TopicBadge = (props: Props) => {
|
|||
href={`/topic/${props.topic.slug}`}
|
||||
class={clsx(styles.picture, {
|
||||
[styles.withImage]: props.topic.pic,
|
||||
[styles.smallSize]: isMobileView(),
|
||||
[styles.smallSize]: isMobileView()
|
||||
})}
|
||||
style={
|
||||
props.topic.pic && {
|
||||
'background-image': `url('${getImageUrl(props.topic.pic, { width: 40, height: 40 })}')`,
|
||||
'background-image': `url('${getImageUrl(props.topic.pic, { width: 40, height: 40 })}')`
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
|
0
src/components/Views/AllAuthors.tsx
Normal file
0
src/components/Views/AllAuthors.tsx
Normal file
|
@ -33,7 +33,7 @@ export const AllAuthors = (props: Props) => {
|
|||
const { searchParams, changeSearchParams } = useRouter<AllAuthorsPageSearchParams>()
|
||||
const { sortedAuthors } = useAuthorsStore({
|
||||
authors: props.authors,
|
||||
sortBy: searchParams().by || 'name',
|
||||
sortBy: searchParams().by || 'name'
|
||||
})
|
||||
|
||||
const [searchQuery, setSearchQuery] = createSignal('')
|
||||
|
@ -51,7 +51,7 @@ export const AllAuthors = (props: Props) => {
|
|||
const byLetter = createMemo<{ [letter: string]: Author[] }>(() => {
|
||||
return sortedAuthors().reduce(
|
||||
(acc, author) => authorLetterReduce(acc, author, lang()),
|
||||
{} as { [letter: string]: Author[] },
|
||||
{} as { [letter: string]: Author[] }
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -87,21 +87,21 @@ export const AllAuthors = (props: Props) => {
|
|||
<ul class={clsx(styles.viewSwitcher, 'view-switcher')}>
|
||||
<li
|
||||
class={clsx({
|
||||
['view-switcher__item--selected']: !searchParams().by || searchParams().by === 'shouts',
|
||||
['view-switcher__item--selected']: !searchParams().by || searchParams().by === 'shouts'
|
||||
})}
|
||||
>
|
||||
<a href="/authors?by=shouts">{t('By shouts')}</a>
|
||||
</li>
|
||||
<li
|
||||
class={clsx({
|
||||
['view-switcher__item--selected']: searchParams().by === 'followers',
|
||||
['view-switcher__item--selected']: searchParams().by === 'followers'
|
||||
})}
|
||||
>
|
||||
<a href="/authors?by=followers">{t('By popularity')}</a>
|
||||
</li>
|
||||
<li
|
||||
class={clsx({
|
||||
['view-switcher__item--selected']: searchParams().by === 'name',
|
||||
['view-switcher__item--selected']: searchParams().by === 'name'
|
||||
})}
|
||||
>
|
||||
<a href="/authors?by=name">{t('By name')}</a>
|
||||
|
|
|
@ -39,13 +39,13 @@ export const AllTopics = (props: Props) => {
|
|||
|
||||
const { sortedTopics } = useTopicsStore({
|
||||
topics: props.topics,
|
||||
sortBy: searchParams().by || 'shouts',
|
||||
sortBy: searchParams().by || 'shouts'
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!searchParams().by) {
|
||||
changeSearchParams({
|
||||
by: 'shouts',
|
||||
by: 'shouts'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -64,7 +64,7 @@ export const AllTopics = (props: Props) => {
|
|||
acc[letter].push(topic)
|
||||
return acc
|
||||
},
|
||||
{} as { [letter: string]: Topic[] },
|
||||
{} as { [letter: string]: Topic[] }
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -112,7 +112,7 @@ export const AllTopics = (props: Props) => {
|
|||
const ogImage = getImageUrl('production/image/logo_image.png')
|
||||
const ogTitle = t('Themes and plots')
|
||||
const description = t(
|
||||
'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'
|
||||
)
|
||||
|
||||
return (
|
||||
|
@ -193,7 +193,7 @@ export const AllTopics = (props: Props) => {
|
|||
topic={topic}
|
||||
isFollowed={{
|
||||
loaded: filteredResults().length > 0,
|
||||
value: isOwnerSubscribed(topic.slug),
|
||||
value: isOwnerSubscribed(topic.slug)
|
||||
}}
|
||||
showStat={true}
|
||||
/>
|
||||
|
|
|
@ -72,7 +72,7 @@ export const AuthorView = (props: Props) => {
|
|||
try {
|
||||
const [subscriptionsResult, followersResult] = await Promise.all([
|
||||
apiClient.getAuthorFollows({ slug }),
|
||||
apiClient.getAuthorFollowers({ slug }),
|
||||
apiClient.getAuthorFollowers({ slug })
|
||||
])
|
||||
|
||||
const { authors, topics } = subscriptionsResult
|
||||
|
@ -98,7 +98,7 @@ export const AuthorView = (props: Props) => {
|
|||
const { hasMore } = await loadShouts({
|
||||
filters: { author: props.authorSlug },
|
||||
limit: LOAD_MORE_PAGE_SIZE,
|
||||
offset: sortedArticles().length,
|
||||
offset: sortedArticles().length
|
||||
})
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
restoreScrollPosition()
|
||||
|
@ -115,12 +115,12 @@ export const AuthorView = (props: Props) => {
|
|||
})
|
||||
|
||||
const pages = createMemo<Shout[][]>(() =>
|
||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
|
||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
||||
)
|
||||
|
||||
const fetchComments = async (commenter: Author) => {
|
||||
const data = await apiClient.getReactionsBy({
|
||||
by: { comment: false, created_by: commenter.id },
|
||||
by: { comment: false, created_by: commenter.id }
|
||||
})
|
||||
setCommented(data)
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ export const AuthorView = (props: Props) => {
|
|||
const ogImage = createMemo(() =>
|
||||
author()?.pic
|
||||
? getImageUrl(author()?.pic, { width: 1200 })
|
||||
: getImageUrl('production/image/logo_image.png'),
|
||||
: getImageUrl('production/image/logo_image.png')
|
||||
)
|
||||
const description = createMemo(() => getDescription(author()?.bio))
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ type Props = {
|
|||
export const MAX_HEADER_LIMIT = 100
|
||||
export const EMPTY_TOPIC: Topic = {
|
||||
id: -1,
|
||||
slug: '',
|
||||
slug: ''
|
||||
}
|
||||
|
||||
const AUTO_SAVE_INTERVAL = 5000
|
||||
|
@ -46,7 +46,7 @@ const handleScrollTopButtonClick = (e) => {
|
|||
e.preventDefault()
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ export const EditView = (props: Props) => {
|
|||
setFormErrors,
|
||||
saveDraft,
|
||||
saveDraftToLocalStorage,
|
||||
getDraftFromLocalStorage,
|
||||
getDraftFromLocalStorage
|
||||
} = useEditorContext()
|
||||
const shoutTopics = props.shout.topics || []
|
||||
const draft = getDraftFromLocalStorage(props.shout.id)
|
||||
|
@ -81,7 +81,7 @@ export const EditView = (props: Props) => {
|
|||
body: props.shout.body,
|
||||
coverImageUrl: props.shout.cover,
|
||||
media: props.shout.media,
|
||||
layout: props.shout.layout,
|
||||
layout: props.shout.layout
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ export const EditView = (props: Props) => {
|
|||
const handleBeforeUnload = (event) => {
|
||||
if (!deepEqual(prevForm, form)) {
|
||||
event.returnValue = t(
|
||||
'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?'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ export const EditView = (props: Props) => {
|
|||
const [baseAudioFields, setBaseAudioFields] = createSignal({
|
||||
artist: '',
|
||||
date: '',
|
||||
genre: '',
|
||||
genre: ''
|
||||
})
|
||||
|
||||
const handleBaseFieldsChange = (key, value) => {
|
||||
|
@ -225,7 +225,7 @@ export const EditView = (props: Props) => {
|
|||
<div class="wide-container">
|
||||
<button
|
||||
class={clsx(styles.scrollTopButton, {
|
||||
[styles.visible]: isScrolled(),
|
||||
[styles.visible]: isScrolled()
|
||||
})}
|
||||
onClick={handleScrollTopButtonClick}
|
||||
>
|
||||
|
@ -350,8 +350,8 @@ export const EditView = (props: Props) => {
|
|||
class={styles.cover}
|
||||
style={{
|
||||
'background-image': `url(${getImageUrl(form.coverImageUrl, {
|
||||
width: 1600,
|
||||
})})`,
|
||||
width: 1600
|
||||
})})`
|
||||
}}
|
||||
>
|
||||
<Popover content={t('Delete cover')}>
|
||||
|
|
|
@ -41,7 +41,7 @@ export const Expo = (props: Props) => {
|
|||
// })
|
||||
const { sortedArticles } = useArticlesStore({
|
||||
shouts: props.shouts || [],
|
||||
layout: props.layout,
|
||||
layout: props.layout
|
||||
})
|
||||
|
||||
const getLoadShoutsFilters = (additionalFilters: LoadShoutsFilters = {}): LoadShoutsFilters => {
|
||||
|
@ -61,7 +61,7 @@ export const Expo = (props: Props) => {
|
|||
const options: LoadShoutsOptions = {
|
||||
filters: getLoadShoutsFilters(),
|
||||
limit: count,
|
||||
offset: sortedArticles().length,
|
||||
offset: sortedArticles().length
|
||||
}
|
||||
|
||||
options.filters = props.layout
|
||||
|
@ -82,7 +82,7 @@ export const Expo = (props: Props) => {
|
|||
const options: LoadShoutsOptions = {
|
||||
filters: getLoadShoutsFilters(),
|
||||
limit: 10,
|
||||
random_limit: 100,
|
||||
random_limit: 100
|
||||
}
|
||||
|
||||
const result = await apiClient.getRandomTopShouts({ options })
|
||||
|
@ -96,7 +96,7 @@ export const Expo = (props: Props) => {
|
|||
const options: LoadShoutsOptions = {
|
||||
filters: getLoadShoutsFilters({ after }),
|
||||
limit: 10,
|
||||
random_limit: 10,
|
||||
random_limit: 10
|
||||
}
|
||||
|
||||
const result = await apiClient.getRandomTopShouts({ options })
|
||||
|
@ -104,7 +104,7 @@ export const Expo = (props: Props) => {
|
|||
}
|
||||
|
||||
const pages = createMemo<Shout[][]>(() =>
|
||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
|
||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
||||
)
|
||||
|
||||
onMount(() => {
|
||||
|
@ -135,8 +135,8 @@ export const Expo = (props: Props) => {
|
|||
loadMore(PRERENDERED_ARTICLES_COUNT + LOAD_MORE_PAGE_SIZE)
|
||||
loadRandomTopArticles()
|
||||
loadRandomTopMonthArticles()
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
onCleanup(() => {
|
||||
|
|
|
@ -101,12 +101,12 @@ export const FeedView = (props: Props) => {
|
|||
const periods: PeriodItem[] = [
|
||||
{ value: 'week', title: t('This week') },
|
||||
monthPeriod,
|
||||
{ value: 'year', title: t('This year') },
|
||||
{ value: 'year', title: t('This year') }
|
||||
]
|
||||
|
||||
const visibilities: VisibilityItem[] = [
|
||||
{ value: 'community', title: t('All') },
|
||||
{ value: 'featured', title: t('Published') },
|
||||
{ value: 'featured', title: t('Published') }
|
||||
]
|
||||
|
||||
const { page, searchParams, changeSearchParams } = useRouter<FeedSearchParams>()
|
||||
|
@ -168,14 +168,14 @@ export const FeedView = (props: Props) => {
|
|||
resetSortedArticles()
|
||||
loadMore()
|
||||
},
|
||||
{ defer: true },
|
||||
),
|
||||
{ defer: true }
|
||||
)
|
||||
)
|
||||
|
||||
const loadFeedShouts = () => {
|
||||
const options: LoadShoutsOptions = {
|
||||
limit: FEED_PAGE_SIZE,
|
||||
offset: sortedArticles().length,
|
||||
offset: sortedArticles().length
|
||||
}
|
||||
|
||||
const orderBy = getOrderBy(searchParams().by)
|
||||
|
@ -189,7 +189,7 @@ export const FeedView = (props: Props) => {
|
|||
} else if (visibilityMode) {
|
||||
options.filters = {
|
||||
...options.filters,
|
||||
featured: visibilityMode === 'featured',
|
||||
featured: visibilityMode === 'featured'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,8 +208,8 @@ export const FeedView = (props: Props) => {
|
|||
|
||||
loadReactionsBy({
|
||||
by: {
|
||||
shouts: newShouts.map((s) => s.slug),
|
||||
},
|
||||
shouts: newShouts.map((s) => s.slug)
|
||||
}
|
||||
})
|
||||
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
|
@ -217,7 +217,7 @@ export const FeedView = (props: Props) => {
|
|||
|
||||
const ogImage = getImageUrl('production/image/logo_image.png')
|
||||
const description = t(
|
||||
'Independent media project about culture, science, art and society with horizontal editing',
|
||||
'Independent media project about culture, science, art and society with horizontal editing'
|
||||
)
|
||||
const ogTitle = t('Feed')
|
||||
|
||||
|
@ -250,7 +250,7 @@ export const FeedView = (props: Props) => {
|
|||
<li
|
||||
class={clsx({
|
||||
'view-switcher__item--selected':
|
||||
searchParams().by === 'publish_date' || !searchParams().by,
|
||||
searchParams().by === 'publish_date' || !searchParams().by
|
||||
})}
|
||||
>
|
||||
<a href={getPagePath(router, page().route)}>{t('Recent')}</a>
|
||||
|
@ -260,7 +260,7 @@ export const FeedView = (props: Props) => {
|
|||
{/*</li>*/}
|
||||
<li
|
||||
class={clsx({
|
||||
'view-switcher__item--selected': searchParams().by === 'rating',
|
||||
'view-switcher__item--selected': searchParams().by === 'rating'
|
||||
})}
|
||||
>
|
||||
<span class="link" onClick={() => changeSearchParams({ by: 'rating' })}>
|
||||
|
@ -269,7 +269,7 @@ export const FeedView = (props: Props) => {
|
|||
</li>
|
||||
<li
|
||||
class={clsx({
|
||||
'view-switcher__item--selected': searchParams().by === 'last_comment',
|
||||
'view-switcher__item--selected': searchParams().by === 'last_comment'
|
||||
})}
|
||||
>
|
||||
<span class="link" onClick={() => changeSearchParams({ by: 'last_comment' })}>
|
||||
|
@ -362,7 +362,7 @@ export const FeedView = (props: Props) => {
|
|||
<div class={clsx('text-truncate', styles.commentBody)}>
|
||||
<a
|
||||
href={`${getPagePath(router, 'article', {
|
||||
slug: comment.shout.slug,
|
||||
slug: comment.shout.slug
|
||||
})}?commentId=${comment.id}`}
|
||||
innerHTML={comment.body}
|
||||
/>
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
loadShouts,
|
||||
loadTopArticles,
|
||||
loadTopMonthArticles,
|
||||
useArticlesStore,
|
||||
useArticlesStore
|
||||
} from '../../stores/zine/articles'
|
||||
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
|
||||
import { useTopicsStore } from '../../stores/zine/topics'
|
||||
|
@ -44,7 +44,7 @@ const LOAD_MORE_PAGE_SIZE = 16 // Row1 + Row3 + Row2 + Beside (3 + 1) + Row1 + R
|
|||
export const HomeView = (props: Props) => {
|
||||
const { sortedArticles, topArticles, topCommentedArticles, topMonthArticles, topViewedArticles } =
|
||||
useArticlesStore({
|
||||
shouts: props.shouts,
|
||||
shouts: props.shouts
|
||||
})
|
||||
|
||||
const { topTopics } = useTopicsStore()
|
||||
|
@ -62,7 +62,7 @@ export const HomeView = (props: Props) => {
|
|||
const { hasMore } = await loadShouts({
|
||||
filters: { featured: true },
|
||||
limit: CLIENT_LOAD_ARTICLES_COUNT,
|
||||
offset: sortedArticles().length,
|
||||
offset: sortedArticles().length
|
||||
})
|
||||
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
|
@ -84,7 +84,7 @@ export const HomeView = (props: Props) => {
|
|||
const { hasMore } = await loadShouts({
|
||||
filters: { featured: true },
|
||||
limit: LOAD_MORE_PAGE_SIZE,
|
||||
offset: sortedArticles().length,
|
||||
offset: sortedArticles().length
|
||||
})
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
|
||||
|
@ -95,8 +95,8 @@ export const HomeView = (props: Props) => {
|
|||
splitToPages(
|
||||
sortedArticles(),
|
||||
PRERENDERED_ARTICLES_COUNT + CLIENT_LOAD_ARTICLES_COUNT,
|
||||
LOAD_MORE_PAGE_SIZE,
|
||||
),
|
||||
LOAD_MORE_PAGE_SIZE
|
||||
)
|
||||
)
|
||||
|
||||
return (
|
||||
|
|
|
@ -56,7 +56,7 @@ export const InboxView = (props: Props) => {
|
|||
const { changeSearchParams, searchParams } = useRouter<InboxSearchParams>()
|
||||
|
||||
const messagesContainerRef: { current: HTMLDivElement } = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const getQuery = (query) => {
|
||||
|
@ -68,7 +68,7 @@ export const InboxView = (props: Props) => {
|
|||
const handleOpenChat = async (chat: Chat) => {
|
||||
setCurrentDialog(chat)
|
||||
changeSearchParams({
|
||||
chat: chat.id,
|
||||
chat: chat.id
|
||||
})
|
||||
try {
|
||||
await getMessages(chat.id)
|
||||
|
@ -77,7 +77,7 @@ export const InboxView = (props: Props) => {
|
|||
} finally {
|
||||
messagesContainerRef.current.scroll({
|
||||
top: messagesContainerRef.current.scrollHeight,
|
||||
behavior: 'instant',
|
||||
behavior: 'instant'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ export const InboxView = (props: Props) => {
|
|||
sendMessage({
|
||||
body: message,
|
||||
chat_id: currentDialog()?.id.toString(),
|
||||
reply_to: messageToReply()?.id,
|
||||
reply_to: messageToReply()?.id
|
||||
})
|
||||
setClear(true)
|
||||
setMessageToReply(null)
|
||||
|
@ -107,7 +107,7 @@ export const InboxView = (props: Props) => {
|
|||
await loadChats()
|
||||
changeSearchParams({
|
||||
initChat: null,
|
||||
chat: newChat.chat.id,
|
||||
chat: newChat.chat.id
|
||||
})
|
||||
const chatToOpen = chats().find((chat) => chat.id === newChat.chat.id)
|
||||
await handleOpenChat(chatToOpen)
|
||||
|
@ -147,11 +147,11 @@ export const InboxView = (props: Props) => {
|
|||
}
|
||||
messagesContainerRef.current.scroll({
|
||||
top: messagesContainerRef.current.scrollHeight,
|
||||
behavior: 'smooth',
|
||||
behavior: 'smooth'
|
||||
})
|
||||
},
|
||||
}
|
||||
),
|
||||
{ defer: true },
|
||||
{ defer: true }
|
||||
)
|
||||
const handleScrollMessageContainer = () => {
|
||||
if (
|
||||
|
@ -166,7 +166,7 @@ export const InboxView = (props: Props) => {
|
|||
const handleScrollToNew = () => {
|
||||
messagesContainerRef.current.scroll({
|
||||
top: messagesContainerRef.current.scrollHeight,
|
||||
behavior: 'smooth',
|
||||
behavior: 'smooth'
|
||||
})
|
||||
setIsScrollToNewVisible(false)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ const shorten = (str: string, maxLen: number) => {
|
|||
|
||||
const EMPTY_TOPIC: Topic = {
|
||||
id: -1,
|
||||
slug: '',
|
||||
slug: ''
|
||||
}
|
||||
const emptyConfig = {
|
||||
coverImageUrl: '',
|
||||
|
@ -47,7 +47,7 @@ const emptyConfig = {
|
|||
title: '',
|
||||
subtitle: '',
|
||||
description: '',
|
||||
selectedTopics: [],
|
||||
selectedTopics: []
|
||||
}
|
||||
|
||||
export const PublishSettings = (props: Props) => {
|
||||
|
@ -74,7 +74,7 @@ export const PublishSettings = (props: Props) => {
|
|||
title: props.form?.title,
|
||||
subtitle: props.form?.subtitle,
|
||||
description: composeDescription(),
|
||||
selectedTopics: [],
|
||||
selectedTopics: []
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -105,7 +105,7 @@ export const PublishSettings = (props: Props) => {
|
|||
setSettingsForm((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
mainTopic: newSelectedTopics[0],
|
||||
mainTopic: newSelectedTopics[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ export const PublishSettings = (props: Props) => {
|
|||
|
||||
const handleBackClick = () => {
|
||||
redirectPage(router, 'edit', {
|
||||
shoutId: props.shoutId.toString(),
|
||||
shoutId: props.shoutId.toString()
|
||||
})
|
||||
}
|
||||
const handleCancelClick = () => {
|
||||
|
@ -163,7 +163,7 @@ export const PublishSettings = (props: Props) => {
|
|||
</div>
|
||||
<div
|
||||
class={clsx(styles.shoutCardCoverContainer, {
|
||||
[styles.hasImage]: settingsForm.coverImageUrl,
|
||||
[styles.hasImage]: settingsForm.coverImageUrl
|
||||
})}
|
||||
>
|
||||
<Show when={settingsForm.coverImageUrl ?? initialData().coverImageUrl}>
|
||||
|
@ -183,7 +183,7 @@ export const PublishSettings = (props: Props) => {
|
|||
</div>
|
||||
<p class="description">
|
||||
{t(
|
||||
'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.'
|
||||
)}
|
||||
</p>
|
||||
|
||||
|
@ -229,7 +229,7 @@ export const PublishSettings = (props: Props) => {
|
|||
<h4>{t('Topics')}</h4>
|
||||
<p class="description">
|
||||
{t(
|
||||
'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'
|
||||
)}
|
||||
</p>
|
||||
<div class={styles.inputContainer}>
|
||||
|
|
|
@ -40,7 +40,7 @@ export const SearchView = (props: Props) => {
|
|||
const { hasMore } = await loadShoutsSearch({
|
||||
text: query(),
|
||||
offset: offset(),
|
||||
limit: LOAD_MORE_PAGE_SIZE,
|
||||
limit: LOAD_MORE_PAGE_SIZE
|
||||
})
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
setOffset(offset() + LOAD_MORE_PAGE_SIZE)
|
||||
|
@ -80,14 +80,14 @@ export const SearchView = (props: Props) => {
|
|||
<ul class="view-switcher">
|
||||
<li
|
||||
classList={{
|
||||
'view-switcher__item--selected': searchParams().by === 'relevance',
|
||||
'view-switcher__item--selected': searchParams().by === 'relevance'
|
||||
}}
|
||||
>
|
||||
<a href="?by=relevance">{t('By relevance')}</a>
|
||||
</li>
|
||||
<li
|
||||
classList={{
|
||||
'view-switcher__item--selected': searchParams().by === 'rating',
|
||||
'view-switcher__item--selected': searchParams().by === 'rating'
|
||||
}}
|
||||
>
|
||||
<a href="?by=rating">{t('Top rated')}</a>
|
||||
|
|
|
@ -57,8 +57,8 @@ export const TopicView = (props: Props) => {
|
|||
lang() === 'en'
|
||||
? topic()?.slug.replace(/-/, ' ')
|
||||
: topic()?.title || topic()?.slug.replace(/-/, ' '),
|
||||
true,
|
||||
)}`,
|
||||
true
|
||||
)}`
|
||||
)
|
||||
|
||||
const loadMore = async () => {
|
||||
|
@ -67,7 +67,7 @@ export const TopicView = (props: Props) => {
|
|||
const { hasMore } = await loadShouts({
|
||||
filters: { topic: topic()?.slug },
|
||||
limit: LOAD_MORE_PAGE_SIZE,
|
||||
offset: sortedArticles().length,
|
||||
offset: sortedArticles().length
|
||||
})
|
||||
setIsLoadMoreButtonVisible(hasMore)
|
||||
|
||||
|
@ -89,7 +89,7 @@ export const TopicView = (props: Props) => {
|
|||
})
|
||||
*/
|
||||
const pages = createMemo<Shout[][]>(() =>
|
||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
|
||||
splitToPages(sortedArticles(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
|
||||
)
|
||||
|
||||
const ogImage = () =>
|
||||
|
@ -120,14 +120,14 @@ export const TopicView = (props: Props) => {
|
|||
<ul class="view-switcher">
|
||||
<li
|
||||
classList={{
|
||||
'view-switcher__item--selected': searchParams().by === 'recent' || !searchParams().by,
|
||||
'view-switcher__item--selected': searchParams().by === 'recent' || !searchParams().by
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
changeSearchParams({
|
||||
by: 'recent',
|
||||
by: 'recent'
|
||||
})
|
||||
}
|
||||
>
|
||||
|
|
|
@ -37,9 +37,9 @@ export const Button = (props: Props) => {
|
|||
styles[props.variant ?? 'primary'],
|
||||
{
|
||||
[styles.loading]: props.loading,
|
||||
[styles.subscribeButton]: props.isSubscribeButton,
|
||||
[styles.subscribeButton]: props.isSubscribeButton
|
||||
},
|
||||
props.class,
|
||||
props.class
|
||||
)}
|
||||
>
|
||||
{props.value}
|
||||
|
|
|
@ -63,14 +63,14 @@ export const DropArea = (props: Props) => {
|
|||
|
||||
const { files, selectFiles } = createFileUploader({
|
||||
multiple: true,
|
||||
accept: `${props.fileType}/*`,
|
||||
accept: `${props.fileType}/*`
|
||||
})
|
||||
|
||||
const { setRef: dropzoneRef, files: droppedFiles } = createDropzone({
|
||||
onDrop: async () => {
|
||||
setDragActive(false)
|
||||
await initUpload(droppedFiles())
|
||||
},
|
||||
}
|
||||
})
|
||||
const handleDrag = (event) => {
|
||||
if (event.type === 'dragenter' || event.type === 'dragover') {
|
||||
|
|
|
@ -47,7 +47,7 @@ export const DropDown = <TOption extends Option = Option>(props: Props<TOption>)
|
|||
{props.currentOption.title}{' '}
|
||||
<Chevron
|
||||
class={clsx(styles.chevron, {
|
||||
[styles.rotate]: isPopupVisible(),
|
||||
[styles.rotate]: isPopupVisible()
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const DropdownSelect = (props: Props) => {
|
|||
const [isDropDownVisible, setIsDropDownVisible] = createSignal(false)
|
||||
|
||||
const containerRef: { current: HTMLElement } = {
|
||||
current: null,
|
||||
current: null
|
||||
}
|
||||
|
||||
const handleShowDropdown = () => {
|
||||
|
@ -30,7 +30,7 @@ export const DropdownSelect = (props: Props) => {
|
|||
useOutsideClickHandler({
|
||||
containerRef,
|
||||
predicate: () => isDropDownVisible(),
|
||||
handler: () => setIsDropDownVisible(false),
|
||||
handler: () => setIsDropDownVisible(false)
|
||||
})
|
||||
|
||||
return (
|
||||
|
|
|
@ -16,7 +16,7 @@ export default (props: Props) => {
|
|||
return (
|
||||
<div
|
||||
class={clsx(styles.PanelWrapper, {
|
||||
[styles.PanelWrapperVisible]: props.isVisible,
|
||||
[styles.PanelWrapperVisible]: props.isVisible
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user