ga-integration

This commit is contained in:
Untone 2024-06-26 11:22:05 +03:00
parent a5eaeab5cd
commit e38fce68c6
177 changed files with 1054 additions and 860 deletions

View File

@ -1,5 +0,0 @@
{
"*.{js,ts,cjs,mjs,d.mts,jsx,tsx,json,jsonc}": [
"npx @biomejs/biome check ./src && tsc"
]
}

View File

@ -13,7 +13,7 @@ export default async function handler(req, res) {
from: 'Discours Feedback Robot <robot@discours.io>',
to: 'welcome@discours.io',
subject,
text,
text
}
try {

View File

@ -11,18 +11,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
})
}
}

View File

@ -8,12 +8,12 @@ export default defineConfig({
ssr: true,
server: {
preset: isVercel ? 'vercel_edge' : isBun ? 'bun' : 'node',
port: 3000,
port: 3000
},
devOverlay: true,
build: {
chunkSizeWarningLimit: 1024,
target: 'esnext',
target: 'esnext'
},
vite: {
envPrefix: 'PUBLIC_',
@ -22,12 +22,12 @@ export default defineConfig({
include: ['path', 'stream', 'util'],
exclude: ['http'],
globals: {
Buffer: true,
Buffer: true
},
overrides: {
fs: 'memfs',
fs: 'memfs'
},
protocolImports: true,
protocolImports: true
}),
sassDts()
],
@ -35,13 +35,13 @@ export default defineConfig({
preprocessorOptions: {
scss: {
additionalData: '@import "src/styles/imports";\n',
includePaths: ['./public', './src/styles'],
},
},
includePaths: ['./public', './src/styles']
}
}
},
build: {
chunkSizeWarningLimit: 1024,
target: 'esnext',
target: 'esnext'
}
}
} as SolidStartInlineConfig)

View File

@ -1,21 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.2/schema.json",
"files": {
"include": [
"*.tsx",
"*.ts",
"*.js",
"*.json"
],
"ignore": [
"./dist",
"./node_modules",
".husky",
"docs",
"gen",
"*.gen.ts",
"*.d.ts"
]
"include": ["*.tsx", "*.ts", "*.js", "*.json"],
"ignore": ["./dist", "./node_modules", ".husky", "docs", "gen", "*.gen.ts", "*.d.ts"]
},
"vcs": {
"defaultBranch": "dev",
@ -23,19 +10,13 @@
},
"organizeImports": {
"enabled": true,
"ignore": [
"./api",
"./gen"
]
"ignore": ["./api", "./gen"]
},
"formatter": {
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 108,
"ignore": [
"./src/graphql/schema",
"./gen"
]
"ignore": ["./src/graphql/schema", "./gen"]
},
"javascript": {
"formatter": {
@ -43,17 +24,12 @@
"quoteStyle": "single",
"enabled": true,
"jsxQuoteStyle": "double",
"arrowParentheses": "always"
"arrowParentheses": "always",
"trailingCommas": "none"
}
},
"linter": {
"ignore": [
"*.scss",
"*.md",
".DS_Store",
"*.svg",
"*.d.ts"
],
"ignore": ["*.scss", "*.md", ".DS_Store", "*.svg", "*.d.ts"],
"enabled": true,
"rules": {
"all": true,
@ -85,6 +61,7 @@
"noBarrelFile": "off"
},
"style": {
"noNamespaceImport": "warn",
"useBlockStatements": "off",
"noImplicitBoolean": "off",
"useNamingConvention": "off",
@ -98,4 +75,4 @@
}
}
}
}
}

View File

@ -30,6 +30,7 @@
"@solid-primitives/media": "^2.2.9",
"@solid-primitives/memo": "^1.3.8",
"@solid-primitives/pagination": "^0.3.0",
"@solid-primitives/script-loader": "^2.2.0",
"@solid-primitives/share": "^2.0.6",
"@solid-primitives/storage": "^3.7.1",
"@solid-primitives/upload": "^0.0.117",
@ -112,11 +113,7 @@
"yjs": "13.6.18",
"y-prosemirror": "1.2.9"
},
"trustedDependencies": [
"@biomejs/biome",
"esbuild",
"protobufjs"
],
"trustedDependencies": ["@biomejs/biome", "esbuild", "protobufjs"],
"dependencies": {
"form-data": "^4.0.0",
"idb": "^8.0.0",
@ -125,4 +122,4 @@
"engines": {
"node": "20.x"
}
}
}

View File

@ -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 many viewports.
// {
@ -66,7 +66,7 @@ export default defineConfig({
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
]
/* Run local dev server before starting the tests */
//webServer: {

View File

@ -15,7 +15,7 @@ type Props = {
onMediaItemFieldChange?: (
index: number,
field: keyof MediaItem | string | number | symbol,
value: string,
value: string
) => void
onChangeMediaIndex?: (direction: 'up' | 'down', index: number) => void
}
@ -101,7 +101,7 @@ export const AudioPlayer = (props: Props) => {
const handleMediaItemFieldChange = (
index: number,
field: keyof MediaItem | string | number | symbol,
value: string,
value: string
) => {
props.onMediaItemFieldChange?.(index, field, value)
}
@ -135,7 +135,7 @@ export const AudioPlayer = (props: Props) => {
<div
class={styles.progressFilled}
style={{
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`,
width: `${(currentTime() / currentTrackDuration()) * 100 || 0}%`
}}
/>
</div>

View File

@ -26,7 +26,7 @@ export const PlayerHeader = (props: Props) => {
useOutsideClickHandler({
containerRef: volumeContainerRef,
predicate: () => isVolumeBarOpened(),
handler: () => toggleVolumeBar(),
handler: () => toggleVolumeBar()
})
return (
@ -38,7 +38,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"

View File

@ -12,7 +12,7 @@ import {
MutationCreate_ReactionArgs,
MutationUpdate_ReactionArgs,
Reaction,
ReactionKind,
ReactionKind
} from '../../../graphql/schema/core.gen'
import { AuthorLink } from '../../Author/AuthorLink'
import { Userpic } from '../../Author/Userpic'
@ -54,7 +54,7 @@ export const Comment = (props: Props) => {
const canEdit = createMemo(
() =>
Boolean(author()?.id) &&
(props.comment?.created_by?.slug === author()?.slug || session()?.user?.roles?.includes('editor')),
(props.comment?.created_by?.slug === author()?.slug || session()?.user?.roles?.includes('editor'))
)
const body = createMemo(() => (editedBody() ? editedBody()?.trim() : props.comment.body?.trim() || ''))
@ -66,7 +66,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) => {
await showSnackbar({
type: notificationType,
body: notificationMessage,
duration: 3,
duration: 3
})
if (!error && props.onDelete) {
@ -102,8 +102,8 @@ 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
}
} as MutationCreate_ReactionArgs)
setClearEditor(true)
setIsReplyVisible(false)
@ -126,8 +126,8 @@ export const Comment = (props: Props) => {
id: props.comment.id || 0,
kind: ReactionKind.Comment,
body: value,
shout: props.comment.shout.id,
},
shout: props.comment.shout.id
}
} as MutationUpdate_ReactionArgs)
if (reaction) {
setEditedBody(value)
@ -144,7 +144,7 @@ export const Comment = (props: Props) => {
id={`comment_${props.comment.id}`}
class={clsx(styles.comment, props.class, {
[styles.isNew]:
(props.lastSeen || Date.now()) > (props.comment.updated_at || props.comment.created_at),
(props.lastSeen || Date.now()) > (props.comment.updated_at || props.comment.created_at)
})}
>
<Show when={!!body()}>
@ -157,7 +157,7 @@ export const Comment = (props: Props) => {
name={props.comment.created_by.name || ''}
userpic={props.comment.created_by.pic || ''}
class={clsx({
[styles.compactUserpic]: props.compact,
[styles.compactUserpic]: props.compact
})}
/>
<small>

View File

@ -28,7 +28,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>

View File

@ -30,7 +30,7 @@ export const CommentRatingControl = (props: Props) => {
r.kind === reactionKind &&
r.created_by.id === uid() &&
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))
@ -41,8 +41,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) => {
@ -51,7 +51,7 @@ export const CommentRatingControl = (props: Props) => {
r.kind === reactionKind &&
r.created_by.id === uid() &&
r.shout.id === props.comment.shout.id &&
r.reply_to === props.comment.id,
r.reply_to === props.comment.id
)
if (reactionToDelete) return deleteReaction(reactionToDelete.id)
}
@ -67,8 +67,8 @@ export const CommentRatingControl = (props: Props) => {
reaction: {
kind: isUpvote ? ReactionKind.Like : ReactionKind.Dislike,
shout: props.comment.shout.id,
reply_to: props.comment.id,
},
reply_to: props.comment.id
}
})
}
} catch {
@ -77,7 +77,7 @@ export const CommentRatingControl = (props: Props) => {
await loadShout(props.comment.shout.slug)
await loadReactionsBy({
by: { shout: props.comment.shout.slug },
by: { shout: props.comment.shout.slug }
})
}
@ -88,7 +88,7 @@ export const CommentRatingControl = (props: Props) => {
disabled={!(canVote() && uid())}
onClick={() => handleRatingChange(true)}
class={clsx(styles.commentRatingControl, styles.commentRatingControlUp, {
[styles.voted]: isUpvoted(),
[styles.voted]: isUpvoted()
})}
/>
<Popup
@ -96,7 +96,7 @@ export const CommentRatingControl = (props: Props) => {
<div
class={clsx(styles.commentRatingValue, {
[styles.commentRatingPositive]: (props.comment?.stat?.rating || 0) > 0,
[styles.commentRatingNegative]: (props.comment?.stat?.rating || 0) < 0,
[styles.commentRatingNegative]: (props.comment?.stat?.rating || 0) < 0
})}
>
{props.comment?.stat?.rating || 0}
@ -114,7 +114,7 @@ export const CommentRatingControl = (props: Props) => {
disabled={!(canVote() && uid())}
onClick={() => handleRatingChange(false)}
class={clsx(styles.commentRatingControl, styles.commentRatingControlDown, {
[styles.voted]: isDownvoted(),
[styles.voted]: isDownvoted()
})}
/>
</div>

View File

@ -34,7 +34,7 @@ export const CommentsTree = (props: Props) => {
const { reactionEntities, createReaction, loadReactionsBy } = useReactions()
const comments = createMemo(() =>
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT'),
Object.values(reactionEntities).filter((reaction) => reaction.kind === 'COMMENT')
)
const sortedComments = createMemo(() => {
@ -80,8 +80,8 @@ export const CommentsTree = (props: Props) => {
reaction: {
kind: ReactionKind.Comment,
body: value,
shout: props.shoutId,
},
shout: props.shoutId
}
})
setClearEditor(true)
await loadReactionsBy({ by: { shout: props.shoutSlug } })

View File

@ -26,7 +26,7 @@ const coverImages = [
CoverImage9,
CoverImage10,
CoverImage11,
CoverImage12,
CoverImage12
]
let counter = 0

View File

@ -61,7 +61,7 @@ const scrollTo = (el: HTMLElement) => {
window.scrollTo({
top: top + window.scrollY - DEFAULT_HEADER_OFFSET,
left: 0,
behavior: 'smooth',
behavior: 'smooth'
})
}
@ -86,7 +86,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(() => {
@ -156,7 +156,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) {
@ -174,7 +174,7 @@ export const FullArticle = (props: Props) => {
}
const tooltipElements: NodeListOf<HTMLElement> = document.querySelectorAll(
'[data-toggle="tooltip"], footnote',
'[data-toggle="tooltip"], footnote'
)
if (!tooltipElements) {
return
@ -199,19 +199,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'
@ -297,8 +297,8 @@ export const FullArticle = (props: Props) => {
() => props.article,
() => {
updateIframeSizes()
},
),
}
)
)
onMount(async () => {
@ -330,7 +330,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() || media()[0]?.body)

View File

@ -31,7 +31,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))
@ -39,8 +39,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) => {
@ -49,7 +49,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
)
if (reactionToDelete) return deleteReaction(reactionToDelete.id)
}
@ -65,14 +65,14 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
await createReaction({
reaction: {
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)

View File

@ -26,14 +26,14 @@ export const AuthGuard = (props: Props) => {
changeSearchParams(
{
source: 'authguard',
m: 'auth',
m: 'auth'
},
{ replace: true },
{ replace: true }
)
}
},
{ defer: true },
),
{ defer: true }
)
)
return <Show when={author() || props.disabled}>{props.children}</Show>

View File

@ -34,7 +34,7 @@ export const AuthorBadge = (props: Props) => {
const { follow, unfollow, follows, following } = useFollowing()
const [isMobileView, setIsMobileView] = createSignal(false)
const [isFollowed, setIsFollowed] = createSignal<boolean>(
Boolean(follows?.authors?.some((authorEntity) => Boolean(authorEntity.id === props.author?.id))),
Boolean(follows?.authors?.some((authorEntity) => Boolean(authorEntity.id === props.author?.id)))
)
createEffect(() => setIsMobileView(!mediaMatches.sm))
createEffect(
@ -42,11 +42,11 @@ export const AuthorBadge = (props: Props) => {
[() => follows?.authors, () => props.author, following],
([followingAuthors, currentAuthor, _]) => {
setIsFollowed(
Boolean(followingAuthors?.some((followedAuthor) => followedAuthor.id === currentAuthor?.id)),
Boolean(followingAuthors?.some((followedAuthor) => followedAuthor.id === currentAuthor?.id))
)
},
{ defer: true },
),
{ defer: true }
)
)
const [, changeSearchParams] = useSearchParams()
@ -58,7 +58,7 @@ export const AuthorBadge = (props: Props) => {
requireAuthentication(() => {
navigate('/inbox')
changeSearchParams({
initChat: props.author?.id.toString(),
initChat: props.author?.id.toString()
})
}, 'discussions')
}
@ -108,7 +108,7 @@ export const AuthorBadge = (props: Props) => {
fallback={
<div class={styles.bio}>
{t('Registered since {date}', {
date: formatDate(new Date((props.author.created_at || 0) * 1000)),
date: formatDate(new Date((props.author.created_at || 0) * 1000))
})}
</div>
}

View File

@ -65,7 +65,7 @@ export const AuthorCard = (props: Props) => {
requireAuthentication(() => {
navigate('/inbox')
changeSearchParams({
initChat: props.author?.id.toString(),
initChat: props.author?.id.toString()
})
}, 'discussions')
}
@ -127,7 +127,7 @@ export const AuthorCard = (props: Props) => {
<ul class="view-switcher">
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'all',
'view-switcher__item--selected': followsFilter() === 'all'
})}
>
<button type="button" onClick={() => setFollowsFilter('all')}>
@ -137,7 +137,7 @@ export const AuthorCard = (props: Props) => {
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'authors',
'view-switcher__item--selected': followsFilter() === 'authors'
})}
>
<button type="button" onClick={() => setFollowsFilter('authors')}>
@ -147,7 +147,7 @@ export const AuthorCard = (props: Props) => {
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'topics',
'view-switcher__item--selected': followsFilter() === 'topics'
})}
>
<button type="button" onClick={() => setFollowsFilter('topics')}>
@ -236,7 +236,7 @@ export const AuthorCard = (props: Props) => {
value={followButtonText()}
isSubscribeButton={true}
class={clsx({
[stylesButton.followed]: isFollowed(),
[stylesButton.followed]: isFollowed()
})}
/>
</Show>
@ -265,7 +265,7 @@ export const AuthorCard = (props: Props) => {
description={props.author.bio || ''}
imageUrl={props.author.pic || ''}
shareUrl={getShareUrl({
pathname: `/author/${props.author.slug}`,
pathname: `/author/${props.author.slug}`
})}
trigger={<Button variant="secondary" value={t('Share')} />}
/>

View File

@ -27,7 +27,7 @@ export const AuthorLink = (props: Props) => {
return (
<div
class={clsx(styles.AuthorLink, props.class, styles[(props.size ?? 'M') as keyof Props['size']], {
[styles.authorLinkFloorImportant]: props.isFloorImportant,
[styles.authorLinkFloorImportant]: props.isFloorImportant
})}
>
<a class={styles.link} href={`/author/${props.author.slug}`}>

View File

@ -21,7 +21,7 @@ export const AuthorRatingControl = (props: AuthorRatingControlProps) => {
const value = isUpvote ? 1 : -1
const _resp = await mutation(rateAuthorMutation, {
rated_slug: props.author?.slug,
value,
value
}).toPromise()
setRating((r) => (r || 0) + value)
}
@ -32,7 +32,7 @@ export const AuthorRatingControl = (props: AuthorRatingControlProps) => {
<div
class={clsx(styles.rating, props.class, {
[styles.isUpvoted]: isUpvoted,
[styles.isDownvoted]: isDownvoted,
[styles.isDownvoted]: isDownvoted
})}
>
<button

View File

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

View File

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

View File

@ -35,7 +35,7 @@ export const AuthorsList = (props: Props) => {
const resp = await query(loadAuthorsByQuery, {
by: { order: queryType },
limit: PAGE_SIZE,
offset,
offset
})
const result = resp?.data?.load_authors_by
if ((result?.length || 0) > 0) {
@ -52,7 +52,7 @@ export const AuthorsList = (props: Props) => {
const loadMoreAuthors = () => {
const nextPage = currentPage()[props.query] + 1
fetchAuthors(props.query, nextPage).then(() =>
setCurrentPage({ ...currentPage(), [props.query]: nextPage }),
setCurrentPage({ ...currentPage(), [props.query]: nextPage })
)
}
@ -65,8 +65,8 @@ export const AuthorsList = (props: Props) => {
setCurrentPage((prev) => ({ ...prev, [query]: 0 }))
fetchAuthors(query, 0).then(() => setCurrentPage((prev) => ({ ...prev, [query]: 1 })))
}
},
),
}
)
)
const authorsList = () => (props.query === 'shouts' ? authorsByShouts() : authorsByFollowers())

View File

@ -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() // чек для регулярных платежей
}
}
}
},
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
(opts: any) => {
@ -118,9 +118,9 @@ export const Donate = () => {
showSnackbar({
type: 'error',
body: reason,
body: reason
})
},
}
)
}

View File

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

View File

@ -5,12 +5,11 @@ import { Icon } from '../_shared/Icon'
import { Newsletter } from '../_shared/Newsletter'
import styles from './Footer.module.scss'
const social = [
{ name: 'facebook', href: 'https://facebook.com/discoursio' },
{ name: 'vk', href: 'https://vk.com/discoursio' },
{ name: 'twitter', href: 'https://twitter.com/discours_io' },
{ name: 'telegram', href: 'https://t.me/discoursio' },
{ name: 'telegram', href: 'https://t.me/discoursio' }
]
type FooterItem = {
title: string
@ -19,7 +18,7 @@ type FooterItem = {
}
export const FooterView = () => {
const { t, lang } = useLocalize()
const [footerLinks, setFooterLinks] = createSignal<Array<{ header: string, items: FooterItem[]}>>([])
const [footerLinks, setFooterLinks] = createSignal<Array<{ header: string; items: FooterItem[] }>>([])
onMount(() => {
setFooterLinks([
@ -30,8 +29,8 @@ export const FooterView = () => {
{ title: t('How it works'), slug: '/about/guide' },
{ title: t('Dogma'), slug: '/about/dogma' },
{ title: t('Principles'), slug: '/about/principles' },
{ title: t('How to write an article'), slug: '/how-to-write-a-good-article' },
],
{ title: t('How to write an article'), slug: '/how-to-write-a-good-article' }
]
},
{
header: t('Participating'),
@ -39,8 +38,11 @@ export const FooterView = () => {
{ title: t('Suggest an idea'), slug: '/connect' },
{ title: t('Become an author'), slug: '/create' },
{ title: t('Support Discours'), slug: '/about/help' },
{ title: t('Work with us'), slug: 'https://docs.google.com/forms/d/e/1FAIpQLSeNNvIzKlXElJtkPkYiXl-jQjlvsL9u4-kpnoRjz1O8Wo40xQ/viewform' },
],
{
title: t('Work with us'),
slug: 'https://docs.google.com/forms/d/e/1FAIpQLSeNNvIzKlXElJtkPkYiXl-jQjlvsL9u4-kpnoRjz1O8Wo40xQ/viewform'
}
]
},
{
header: t('Sections'),
@ -49,14 +51,17 @@ export const FooterView = () => {
{ title: t('Communities'), slug: '/community' },
{ title: t('Partners'), slug: '/about/partners' },
{ title: t('Special projects'), slug: '/about/projects' },
{ title: lang() === 'ru' ? 'English' : 'Русский', slug: `?lng=${lang() === 'ru' ? 'en' : 'ru'}`, rel: 'external' },
],
},
{
title: lang() === 'ru' ? 'English' : 'Русский',
slug: `?lng=${lang() === 'ru' ? 'en' : 'ru'}`,
rel: 'external'
}
]
}
])
})
return (
<footer class={styles.discoursFooter}>
<div class="wide-container">
<div class="row">
@ -89,7 +94,7 @@ export const FooterView = () => {
<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')} &copy; 2015&ndash;{new Date().getFullYear()}{' '}
<a href="/about/terms-of-use">{t('Terms of use')}</a>
@ -97,7 +102,12 @@ export const FooterView = () => {
<div class={clsx(styles.footerCopyrightSocial, 'col-md-6 col-lg-4')}>
<For each={social}>
{(provider) => (
<div class={clsx(styles.socialItem, styles[`socialItem${provider.name}` as keyof typeof styles])}>
<div
class={clsx(
styles.socialItem,
styles[`socialItem${provider.name}` as keyof typeof styles]
)}
>
<a href={provider.href}>
<Icon name={`${provider.name}-white`} class={styles.icon} />
</a>

View File

@ -16,7 +16,7 @@ export default () => {
<h4 innerHTML={t('Horizontal collaborative journalistic platform')} />
<p
innerHTML={t(
'Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects',
'Discours is an intellectual environment, a web space and tools that allows authors to collaborate with readers and come together to co-create publications and media projects'
)}
/>
<div class={styles.aboutDiscoursActions}>
@ -28,7 +28,7 @@ export default () => {
onClick={() => {
showModal('auth')
changeSearchParams({
mode: 'register',
mode: 'register'
})
}}
>

View File

@ -36,7 +36,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)

View File

@ -31,7 +31,7 @@ export const AudioUploader = (props: Props) => {
const handleMediaItemFieldChange = (
index: number,
field: keyof MediaItem | string | symbol | number,
value: string,
value: string
) => {
props.onAudioChange(index, { ...props.audio[index], [field]: value })
}

View File

@ -66,7 +66,7 @@ const allowedImageTypes = new Set([
'image/png',
'image/tiff',
'image/webp',
'image/x-icon',
'image/x-icon'
])
const yDocs: Record<string, Doc> = {}
@ -91,7 +91,7 @@ export const EditorComponent = (props: Props) => {
url: 'wss://hocuspocus.discours.io',
name: docName,
document: yDocs[docName],
token: session()?.access_token || '',
token: session()?.access_token || ''
})
}
@ -120,7 +120,7 @@ export const EditorComponent = (props: Props) => {
source: blob.toString(),
name: file.name,
size: file.size,
file,
file
}
showSnackbar({ body: t('Uploading image') })
@ -135,13 +135,13 @@ export const EditorComponent = (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) {
@ -158,7 +158,7 @@ export const EditorComponent = (props: Props) => {
element: ee,
editorProps: {
attributes: {
class: 'articleEditor',
class: 'articleEditor'
},
transformPastedHTML(html) {
return html.replaceAll(/<img.*?>/g, '')
@ -166,7 +166,7 @@ export const EditorComponent = (props: Props) => {
handlePaste: () => {
handleClipboardPaste()
return false
},
}
},
extensions: [
Document,
@ -181,34 +181,34 @@ export const EditorComponent = (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,
@ -216,8 +216,8 @@ export const EditorComponent = (props: Props) => {
Highlight.configure({
multicolor: true,
HTMLAttributes: {
class: 'highlight',
},
class: 'highlight'
}
}),
Image,
Iframe,
@ -253,8 +253,8 @@ export const EditorComponent = (props: Props) => {
onHide: () => {
const fe = freshEditor() as Editor
fe?.commands.focus()
},
},
}
}
}),
BubbleMenu.configure({
pluginKey: 'blockquoteBubbleMenu',
@ -262,7 +262,7 @@ export const EditorComponent = (props: Props) => {
shouldShow: ({ editor: e, view, state }) => {
const { empty } = state.selection
return view.hasFocus() && !empty && e.isActive('blockquote')
},
}
}),
BubbleMenu.configure({
pluginKey: 'figureBubbleMenu',
@ -270,7 +270,7 @@ export const EditorComponent = (props: Props) => {
shouldShow: ({ editor: e, view, state }) => {
const { empty } = state.selection
return view.hasFocus() && !empty && e.isActive('figure')
},
}
}),
BubbleMenu.configure({
pluginKey: 'incutBubbleMenu',
@ -278,7 +278,7 @@ export const EditorComponent = (props: Props) => {
shouldShow: ({ editor: e, view, state }) => {
const { empty } = state.selection
return view.hasFocus() && !empty && e.isActive('figcaption')
},
}
}),
FloatingMenu.configure({
element: floatingMenuRef,
@ -290,10 +290,10 @@ export const EditorComponent = (props: Props) => {
if (!(isRootDepth && empty)) return false
return !(e.isActive('codeBlock') || e.isActive('heading'))
},
}
}),
TrailingNode,
Article,
Article
],
onTransaction: ({ transaction }) => {
if (transaction.docChanged) {
@ -305,7 +305,7 @@ export const EditorComponent = (props: Props) => {
}
}
},
content: initialContent,
content: initialContent
}))
if (freshEditor) {
@ -317,7 +317,7 @@ export const EditorComponent = (props: Props) => {
setEditor(freshEditor() as Editor)
}
}
}),
})
)
onCleanup(() => {

View File

@ -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) => {

View File

@ -25,7 +25,7 @@ export const InsertLinkForm = (props: Props) => {
() => props.editor,
(ed) => {
return ed?.getAttributes('link').href || ''
},
}
)
const handleClearLinkForm = () => {
if (currentUrl()) {

View File

@ -32,7 +32,7 @@ export const Panel = (props: Props) => {
toggleEditorPanel,
saveShout,
saveDraft,
publishShout,
publishShout
} = useEditorContext()
let containerRef: HTMLElement | undefined
@ -42,7 +42,7 @@ export const Panel = (props: Props) => {
useOutsideClickHandler({
containerRef,
predicate: () => isEditorPanelVisible(),
handler: () => toggleEditorPanel(),
handler: () => toggleEditorPanel()
})
useEscKeyDownHandler(() => {

View File

@ -17,7 +17,7 @@ import {
createTiptapEditor,
useEditorHTML,
useEditorIsEmpty,
useEditorIsFocused,
useEditorIsFocused
} from 'solid-tiptap'
import { UploadedFile } from '~/types/upload'
@ -82,7 +82,7 @@ const SimplifiedEditor = (props: Props) => {
const ImageFigure = Figure.extend({
name: 'capturedImage',
content: 'figcaption image',
content: 'figcaption image'
})
createEffect(
@ -94,8 +94,8 @@ const SimplifiedEditor = (props: Props) => {
element: ee,
editorProps: {
attributes: {
class: styles.simplifiedEditorField,
},
class: styles.simplifiedEditorField
}
},
extensions: [
Document,
@ -104,18 +104,18 @@ const SimplifiedEditor = (props: Props) => {
Bold,
Italic,
Link.extend({
inclusive: false,
inclusive: false
}).configure({
autolink: true,
openOnClick: false,
openOnClick: false
}),
CharacterCount.configure({
limit: props.noLimits ? null : maxLength,
limit: props.noLimits ? null : maxLength
}),
Blockquote.configure({
HTMLAttributes: {
class: styles.blockQuote,
},
class: styles.blockQuote
}
}),
BubbleMenu.configure({
pluginKey: 'textBubbleMenu',
@ -125,7 +125,7 @@ const SimplifiedEditor = (props: Props) => {
const { selection } = state
const { empty } = selection
return view.hasFocus() && !empty
},
}
}),
BubbleMenu.configure({
pluginKey: 'linkBubbleMenu',
@ -136,27 +136,27 @@ 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: props.initialContent || null,
content: props.initialContent || null
}))
const editorInstance = freshEditor()
if (!editorInstance) return
setEditor(editorInstance)
}
},
{ defer: true },
),
{ defer: true }
)
)
const isEmpty = useEditorIsEmpty(() => editor())
@ -167,7 +167,7 @@ const SimplifiedEditor = (props: Props) => {
() => editor(),
(ed) => {
return ed?.isActive(name)
},
}
)
const html = useEditorHTML(() => editor())
@ -186,13 +186,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()
@ -259,7 +259,7 @@ const SimplifiedEditor = (props: Props) => {
const maxHeightStyle = {
overflow: 'auto',
'max-height': `${props.maxHeight}px`,
'max-height': `${props.maxHeight}px`
}
const handleShowLinkBubble = () => {
@ -281,7 +281,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()}>

View File

@ -26,7 +26,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
const isActive = (name: string, attributes?: Record<string, string | number>) =>
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: string) => {
@ -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()

View File

@ -41,7 +41,7 @@ export const TopicSelect = (props: TopicSelectProps) => {
const filteredTopics = () => {
return props.topics.filter((topic: Topic) =>
topic?.title?.toLowerCase().includes(searchTerm().toLowerCase()),
topic?.title?.toLowerCase().includes(searchTerm().toLowerCase())
)
}
@ -52,7 +52,7 @@ export const TopicSelect = (props: TopicSelectProps) => {
{(topic) => (
<div
class={clsx(styles.selectedTopic, {
[styles.mainTopic]: props.mainTopic?.slug === topic.slug,
[styles.mainTopic]: props.mainTopic?.slug === topic.slug
})}
onClick={() => handleMainTopicChange(topic)}
>
@ -76,8 +76,8 @@ export const TopicSelect = (props: TopicSelectProps) => {
<div
class={clsx(styles.option, {
[styles.disabled]: props.selectedTopics.some(
(selectedTopic) => selectedTopic.slug === topic.slug,
),
(selectedTopic) => selectedTopic.slug === topic.slug
)
})}
onClick={() => handleChange(topic)}
>

View File

@ -46,13 +46,13 @@ export const UploadModalContent = (props: Props) => {
const data = await fetch(value)
const blob = await data.blob()
const file = new File([blob], 'convertedFromUrl', {
type: data.headers.get('Content-Type') || undefined,
type: data.headers.get('Content-Type') || undefined
})
const fileToUpload: UploadFile = {
source: blob.toString(),
name: file.name,
size: file.size,
file: file,
file: file
}
await runUpload(fileToUpload)
} catch (error) {
@ -76,7 +76,7 @@ export const UploadModalContent = (props: Props) => {
} else {
setDragError(t('Image format not supported'))
}
},
}
})
const handleDrag = (event: MouseEvent) => {
if (event.type === 'dragenter' || event.type === 'dragover') {

View File

@ -33,13 +33,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: DragEvent) => {
if (event.type === 'dragenter' || event.type === 'dragover') {
@ -78,8 +78,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}

View File

@ -18,8 +18,8 @@ export default Node.create({
parseHTML() {
return [
{
tag: 'article',
},
tag: 'article'
}
]
},
@ -29,18 +29,18 @@ export default Node.create({
addOptions() {
return {
'data-type': 'incut',
'data-type': 'incut'
}
},
addAttributes() {
return {
'data-float': {
default: null,
default: null
},
'data-bg': {
default: null,
},
default: null
}
}
},
@ -61,7 +61,7 @@ export default Node.create({
(value) =>
({ commands }) => {
return commands.updateAttributes(this.name, { 'data-bg': value })
},
}
}
},
}
})

View File

@ -23,11 +23,11 @@ export const CustomBlockquote = Blockquote.extend({
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({
setBlockQuoteFloat:
(value) =>
({ commands }) =>
commands.updateAttributes(this.name, { 'data-float': value }),
commands.updateAttributes(this.name, { 'data-float': value })
}
},
}
})

View File

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

View File

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

View File

@ -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 }
}
},
// @ts-ignore FIXME: why
@ -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 })
},
}
}
},
}
})

View File

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

View File

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

View File

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

View File

@ -43,7 +43,7 @@ export const ToggleTextWrap = Extension.create({
return true
}
return false
},
}
}
},
}
})

View File

@ -3,7 +3,7 @@ import { Plugin, PluginKey } from '@tiptap/pm/state'
function nodeEqualsType({
types,
node,
node
}: {
// biome-ignore lint/suspicious/noExplicitAny: FIXME: any in editor extension
types: any
@ -30,7 +30,7 @@ export const TrailingNode = Extension.create<TrailingNodeOptions>({
addOptions() {
return {
node: 'paragraph',
notAfter: ['paragraph'],
notAfter: ['paragraph']
}
},
@ -69,9 +69,9 @@ export const TrailingNode = Extension.create<TrailingNodeOptions>({
const lastNode = tr.doc.lastChild
return !nodeEqualsType({ node: lastNode, types: disabledNodes })
},
},
}),
}
}
})
]
},
}
})

View File

@ -53,11 +53,11 @@ const desktopCoverImageWidths: Record<string, number> = {
XS: 300,
S: 400,
M: 600,
L: 800,
L: 800
}
const getTitleAndSubtitle = (
article: Shout,
article: Shout
): {
title: string
subtitle: string
@ -98,7 +98,7 @@ const LAYOUT_ASPECT: { [key: string]: string } = {
audio: styles.aspectRatio1x1,
literature: styles.aspectRatio16x9,
video: styles.aspectRatio16x9,
image: styles.aspectRatio4x3,
image: styles.aspectRatio4x3
}
export const ArticleCard = (props: ArticleCardProps) => {
@ -115,7 +115,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(
@ -123,14 +123,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 navigate = useNavigate()
const scrollToComments = (event: MouseEvent & { currentTarget: HTMLAnchorElement; target: Element }) => {
event.preventDefault()
navigate(`/article/${props.article.slug}`)
changeSearchParams({
scrollTo: 'comments',
scrollTo: 'comments'
})
}
@ -153,7 +153,7 @@ 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
})}
>
{/* Cover Image */}
@ -162,7 +162,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
<div class={styles.shoutCardCoverContainer}>
<div
class={clsx(styles.shoutCardCover, {
[styles.loading]: props.article.cover && isCoverImageLoading(),
[styles.loading]: props.article.cover && isCoverImageLoading()
})}
>
<Show
@ -217,7 +217,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
{/* Title and Subtitle */}
<div
class={clsx(styles.shoutCardTitlesContainer, {
[styles.shoutCardTitlesContainerFeedMode]: props.settings?.isFeedMode,
[styles.shoutCardTitlesContainerFeedMode]: props.settings?.isFeedMode
})}
>
<A href={`/article${props.article.slug}`}>
@ -249,7 +249,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
size={'XS'}
author={a as Author}
isFloorImportant={Boolean(
props.settings?.isFloorImportant || props.settings?.isWithCover,
props.settings?.isFloorImportant || props.settings?.isWithCover
)}
/>
)}

View File

@ -40,7 +40,7 @@ export const Beside = (props: Props) => {
'col-lg-8',
styles[
`besideRatingColumn${props.wrapper?.charAt(0)?.toUpperCase() + props.wrapper.slice(1)}` as keyof typeof styles
],
]
)}
>
<Show when={!!props.title}>
@ -64,7 +64,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]}>

View File

@ -15,7 +15,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={`/topic/${props.slug}`}>{props.title}</A>

View File

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

View File

@ -36,14 +36,14 @@ const data: PlaceholderData = {
text: 'Placeholder feed',
buttonLabelAuthor: 'Popular authors',
buttonLabelFeed: 'Create own feed',
href: '/authors?by=followers',
href: '/authors?by=followers'
},
feedCollaborations: {
image: 'placeholder-experts.webp',
header: 'Find collaborators',
text: 'Placeholder feedCollaborations',
buttonLabel: 'Find co-authors',
href: '/authors?by=name',
href: '/authors?by=name'
},
feedDiscussions: {
image: 'placeholder-discussions.webp',
@ -51,7 +51,7 @@ const data: PlaceholderData = {
text: 'Placeholder feedDiscussions',
buttonLabelAuthor: 'Current discussions',
buttonLabelFeed: 'Enter',
href: '/feed?by=last_comment',
href: '/feed?by=last_comment'
},
author: {
image: 'placeholder-join.webp',
@ -62,9 +62,9 @@ const data: PlaceholderData = {
profileLinks: [
{
href: '/how-to-write-a-good-article',
label: 'How to write a good article',
},
],
label: 'How to write a good article'
}
]
},
authorComments: {
image: 'placeholder-discussions.webp',
@ -75,14 +75,14 @@ const data: PlaceholderData = {
profileLinks: [
{
href: '/about/discussion-rules',
label: 'Discussion rules',
label: 'Discussion rules'
},
{
href: '/about/discussion-rules#ban',
label: 'Block rules',
},
],
},
label: 'Block rules'
}
]
}
}
export const Placeholder = (props: PlaceholderProps) => {
@ -96,7 +96,7 @@ export const Placeholder = (props: PlaceholderProps) => {
class={clsx(
styles.placeholder,
styles[`placeholder--${props.type}` as keyof typeof styles],
styles[`placeholder--${props.mode}-mode` as keyof typeof styles],
styles[`placeholder--${props.mode}-mode` as keyof typeof styles]
)}
>
<div class={styles.placeholderCover}>
@ -128,7 +128,7 @@ export const Placeholder = (props: PlaceholderProps) => {
{t(
session()?.access_token
? placeholderData()?.buttonLabelAuthor || ''
: placeholderData()?.buttonLabelFeed || '',
: placeholderData()?.buttonLabelFeed || ''
)}
</a>
}
@ -137,7 +137,7 @@ export const Placeholder = (props: PlaceholderProps) => {
{t(
session()?.access_token
? placeholderData()?.buttonLabelAuthor || ''
: placeholderData()?.buttonLabelFeed || '',
: placeholderData()?.buttonLabelFeed || ''
)}
<Show when={props.mode === 'profile'}>
<Icon name="arrow-right-2" class={styles.icon} />

View File

@ -21,7 +21,7 @@ export const Row1 = (props: {
isSingle: true,
nodate: props.nodate,
noAuthorLink: props.noAuthorLink,
noauthor: props.noauthor,
noauthor: props.noauthor
}}
desktopCoverSize="L"
/>

View File

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

View File

@ -28,7 +28,7 @@ export const Row3 = (props: {
settings={{
nodate: props.nodate,
noAuthorLink: props.noAuthorLink,
noauthor: props.noauthor,
noauthor: props.noauthor
}}
desktopCoverSize="S"
/>

View File

@ -18,7 +18,7 @@ export default (props: { articles: Shout[] }) => (
isWithCover: true,
isBigTitle: true,
isVertical: true,
nodate: true,
nodate: true
}}
desktopCoverSize="S"
/>

View File

@ -34,7 +34,7 @@ export const Sidebar = () => {
<A
href={'feed'}
class={clsx({
[styles.selected]: matchFeed(),
[styles.selected]: matchFeed()
})}
>
<span class={styles.sidebarItemName}>
@ -47,7 +47,7 @@ export const Sidebar = () => {
<A
href={'/feed/my'}
class={clsx({
[styles.selected]: matchFeedMy(),
[styles.selected]: matchFeedMy()
})}
>
<span class={styles.sidebarItemName}>
@ -60,7 +60,7 @@ export const Sidebar = () => {
<A
href={'/feed/collabs'}
class={clsx({
[styles.selected]: matchFeedCollabs(),
[styles.selected]: matchFeedCollabs()
})}
>
<span class={styles.sidebarItemName}>
@ -73,7 +73,7 @@ export const Sidebar = () => {
<a
href={'/feed/discussions'}
class={clsx({
[styles.selected]: matchFeedDiscussions(),
[styles.selected]: matchFeedDiscussions()
})}
>
<span class={styles.sidebarItemName}>

View File

@ -50,7 +50,7 @@ const CreateModalContent = (props: Props) => {
const handleClick = (user: inviteUser) => {
setCollectionToInvite((userCollection) => {
return userCollection.map((clickedUser) =>
user.id === clickedUser.id ? { ...clickedUser, selected: !clickedUser.selected } : clickedUser,
user.id === clickedUser.id ? { ...clickedUser, selected: !clickedUser.selected } : clickedUser
)
})
}

View File

@ -27,13 +27,13 @@ const colors = [
'#668cff',
'#c34cfe',
'#e699ff',
'#6633ff',
'#6633ff'
]
const getById = (letter: string) =>
colors[
Math.abs(
Number(BigInt(((letter || '').toLowerCase()?.codePointAt(0) || 97) - 97) % BigInt(colors.length)),
Number(BigInt(((letter || '').toLowerCase()?.codePointAt(0) || 97) - 97) % BigInt(colors.length))
)
]
@ -48,7 +48,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()}` }}
>
@ -62,7 +62,7 @@ const DialogAvatar = (props: Props) => {
? getImageUrl(props.url || '', { width: 40, height: 40 })
: props.url
}
)`,
)`
}}
/>
</Show>

View File

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

View File

@ -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() || 'select')

View File

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

View File

@ -12,7 +12,7 @@ export const AuthModalHeader = (props: Props) => {
const [searchParams] = useSearchParams<{ source: string }>()
const generateModalTextsFromSource = (
modalType: 'login' | 'register',
modalType: 'login' | 'register'
): { title: string; description: string } => {
const title = modalType === 'login' ? 'Welcome to Discours' : 'Create account'
@ -20,53 +20,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&nbsp;bookmarks, you can save favorite discussions and&nbsp;materials that you want to return to',
),
'In&nbsp;bookmarks, you can save favorite discussions and&nbsp;materials that you want to return to'
)
}
}
case 'discussions': {
return {
title: t(`${title} to participate in discussions`),
description: t(
"You&nbsp;ll be able to participate in&nbsp;discussions, rate others' comments and&nbsp;learn about&nbsp;new responses",
),
"You&nbsp;ll be able to participate in&nbsp;discussions, rate others' comments and&nbsp;learn about&nbsp;new responses"
)
}
}
case 'follow': {
return {
title: t(`${title} to subscribe`),
description: t(
'This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed',
),
'This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed'
)
}
}
case 'subscribe': {
return {
title: t(`${title} to subscribe to new publications`),
description: t(
'This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed',
),
'This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed'
)
}
}
case 'vote': {
return {
title: t(`${title} to vote`),
description: t(
'This way we&nbsp;ll realize that you&nbsp;re a real person and&nbsp;ll take your vote into account. And&nbsp;you&nbsp;ll see how others voted',
),
'This way we&nbsp;ll realize that you&nbsp;re a real person and&nbsp;ll take your vote into account. And&nbsp;you&nbsp;ll see how others voted'
)
}
}
default: {
return {
title: t(title),
description: '',
description: ''
}
}
}

View File

@ -75,7 +75,7 @@ export const ChangePasswordForm = () => {
class={styles.authLink}
onClick={() =>
changeSearchParams({
mode: 'login',
mode: 'login'
})
}
>

View File

@ -58,7 +58,7 @@ export const LoginForm = () => {
if (value === '' || !validateEmail(value)) {
setValidationErrors((prev) => ({
...prev,
email: t('Invalid email'),
email: t('Invalid email')
}))
return false
}
@ -66,7 +66,7 @@ export const LoginForm = () => {
if (value === '') {
setValidationErrors((prev) => ({
...prev,
password: t('Please enter password'),
password: t('Please enter password')
}))
return false
}
@ -99,7 +99,7 @@ export const LoginForm = () => {
case 'bad user credentials': {
setValidationErrors((prev) => ({
...prev,
password: t('Something went wrong, check email and password'),
password: t('Something went wrong, check email and password')
}))
break
}
@ -119,7 +119,7 @@ export const LoginForm = () => {
<span class={'link'} onClick={handleSendLinkAgainClick}>
{t('Send link again')}
</span>
</div>,
</div>
)
}
}
@ -139,7 +139,7 @@ export const LoginForm = () => {
<AuthModalHeader modalType="login" />
<div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().email,
'pretty-form__item--error': validationErrors().email
})}
>
<input
@ -177,7 +177,7 @@ export const LoginForm = () => {
class="link"
onClick={() =>
setSearchParams({
mode: 'send-reset-link',
mode: 'send-reset-link'
})
}
>
@ -194,7 +194,7 @@ export const LoginForm = () => {
class={styles.authLink}
onClick={() =>
setSearchParams({
mode: 'register',
mode: 'register'
})
}
>

View File

@ -91,7 +91,7 @@ export const PasswordField = (props: Props) => {
<Show when={error()}>
<div
class={clsx(styles.registerPassword, styles.validationError, {
'form-message--error': props.setError,
'form-message--error': props.setError
})}
>
{error()}

View File

@ -91,7 +91,7 @@ export const RegisterForm = () => {
email: cleanEmail,
password: password(),
confirm_password: password(),
redirect_uri: window?.location?.origin || '',
redirect_uri: window?.location?.origin || ''
}
const success = await signUp(opts)
setIsSuccess(success)
@ -106,7 +106,7 @@ export const RegisterForm = () => {
const handleResendLink = async (_ev: any) => {
const success: boolean = await resendVerifyEmail({
email: email(),
identifier: 'basic_signup',
identifier: 'basic_signup'
})
setIsSuccess(success)
}
@ -123,7 +123,7 @@ export const RegisterForm = () => {
{t('resend confirmation link')}
</span>
</>
),
)
}))
break
}
@ -137,7 +137,7 @@ export const RegisterForm = () => {
{t('enter')}
</span>
</>
),
)
}))
break
}
@ -152,7 +152,7 @@ export const RegisterForm = () => {
{t('Set the new password')}
</span>
</>
),
)
}))
break
}
@ -185,7 +185,7 @@ export const RegisterForm = () => {
<AuthModalHeader modalType="register" />
<div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().fullName,
'pretty-form__item--error': validationErrors().fullName
})}
>
<input
@ -204,7 +204,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'
})
}
>

View File

@ -12,7 +12,7 @@ export const SendEmailConfirm = () => {
<div
style={{
'align-items': 'center',
'justify-content': 'center',
'justify-content': 'center'
}}
>
<div class={styles.text}>{t('Link sent, check your email')}</div>

View File

@ -55,7 +55,7 @@ export const SendResetLinkForm = () => {
try {
const result = await forgotPassword({
email: email(),
redirect_uri: window?.location?.origin || '',
redirect_uri: window?.location?.origin || ''
})
if (result) {
setMessage(result || '')
@ -91,7 +91,7 @@ export const SendResetLinkForm = () => {
</Show>
<div
class={clsx('pretty-form__item', {
'pretty-form__item--error': validationErrors().email,
'pretty-form__item--error': validationErrors().email
})}
>
<input
@ -112,7 +112,7 @@ export const SendResetLinkForm = () => {
class={'link'}
onClick={() =>
changeSearchParams({
mode: 'register',
mode: 'register'
})
}
>
@ -140,7 +140,7 @@ export const SendResetLinkForm = () => {
class={styles.authLink}
onClick={() =>
changeSearchParams({
mode: 'login',
mode: 'login'
})
}
>

View File

@ -35,7 +35,7 @@ const AUTH_MODAL_MODES: Record<AuthModalMode, Component> = {
'send-reset-link': SendResetLinkForm,
'confirm-email': EmailConfirm,
'send-confirm-email': SendEmailConfirm,
'change-password': ChangePasswordForm,
'change-password': ChangePasswordForm
}
export const AuthModal = () => {
@ -60,7 +60,7 @@ export const AuthModal = () => {
ref={(el) => (rootRef = el)}
class={clsx(styles.view, {
row: !searchParams?.source,
[styles.signUp]: mode() === 'register' || mode() === 'confirm-email',
[styles.signUp]: mode() === 'register' || mode() === 'confirm-email'
})}
>
<Show when={!searchParams?.source}>
@ -73,7 +73,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'
)}
.&nbsp;
{t('New stories every day and even more!')}
@ -96,7 +96,7 @@ export const AuthModal = () => {
</Show>
<div
class={clsx(styles.auth, {
'col-md-12': !searchParams?.source,
'col-md-12': !searchParams?.source
})}
>
<Dynamic component={AUTH_MODAL_MODES[mode() as AuthModalMode]} />

View File

@ -169,7 +169,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
@ -324,7 +324,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}>
@ -337,7 +337,7 @@ export const Header = (props: Props) => {
<Show when={props.title}>
<div
class={clsx(styles.articleControls, 'col-auto', {
[styles.articleControlsAuthorized]: session()?.user?.id,
[styles.articleControlsAuthorized]: session()?.user?.id
})}
>
<SharePopup

View File

@ -52,7 +52,7 @@ export const HeaderAuth = (props: Props) => {
const isSaveButtonVisible = createMemo(() => session()?.access_token && isEditorPage())
const isCreatePostButtonVisible = createMemo(() => !isEditorPage())
const isAuthenticatedControlsVisible = createMemo(
() => session()?.access_token && session()?.user?.email_verified,
() => session()?.access_token && session()?.user?.email_verified
)
const handleBurgerButtonClick = () => {
@ -108,7 +108,7 @@ export const HeaderAuth = (props: Props) => {
class={clsx(
styles.userControlItem,
styles.userControlItemVerbose,
styles.userControlItemCreate,
styles.userControlItemCreate
)}
>
<A href={'/create'}>
@ -191,7 +191,7 @@ export const HeaderAuth = (props: Props) => {
{renderIconedButton({
value: t('Publish'),
icon: 'publish',
action: () => publishShout(form),
action: () => publishShout(form)
})}
</div>
@ -199,7 +199,7 @@ export const HeaderAuth = (props: Props) => {
class={clsx(
styles.userControlItem,
styles.settingsControlContainer,
styles.userControlItemVerbose,
styles.userControlItemVerbose
)}
>
<Popover content={t('Settings')}>
@ -221,7 +221,7 @@ export const HeaderAuth = (props: Props) => {
class={clsx(
styles.userControlItem,
styles.userControlItemVerbose,
styles.userControlItemCreate,
styles.userControlItemCreate
)}
>
<A href={'/create'}>
@ -249,7 +249,7 @@ export const HeaderAuth = (props: Props) => {
<Show when={!isSaveButtonVisible()}>
<div
class={clsx(
styles.userControlItem,
styles.userControlItem
// styles.userControlItemInbox
)}
>

View File

@ -32,7 +32,7 @@ export const Modal = (props: Props) => {
<Show when={modal() === props.name}>
<div
class={clsx(styles.backdrop, [styles[`modal-${props.name}` as keyof typeof styles]], {
[styles.isMobile]: props.isResponsive && isPortrait(),
[styles.isMobile]: props.isResponsive && isPortrait()
})}
onClick={handleHide}
>
@ -42,7 +42,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()}
>

View File

@ -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 = () => {
@ -60,7 +60,7 @@ export const SearchModal = () => {
const { hasMore, newShouts } = await loadShoutsSearch({
limit: FEED_PAGE_SIZE,
text: inputValue(),
offset: offset(),
offset: offset()
})
setIsLoading(false)
setOffset(newShouts.length)
@ -69,8 +69,8 @@ export const SearchModal = () => {
},
{
ssrLoadFrom: 'initial',
initialValue: [],
},
initialValue: []
}
)
let searchEl: HTMLInputElement
@ -124,7 +124,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'
)}
/>
@ -138,7 +138,7 @@ export const SearchModal = () => {
settings={{
isFloorImportant: true,
isSingle: true,
nodate: true,
nodate: true
}}
/>
</div>

View File

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

View File

@ -55,7 +55,7 @@ export const NotificationsPanel = (props: Props) => {
loadedNotificationsCount,
totalNotificationsCount,
loadNotificationsGrouped,
markSeenAll,
markSeenAll
} = useNotifications()
const handleHide = () => {
props.onClose()
@ -66,7 +66,7 @@ export const NotificationsPanel = (props: Props) => {
useOutsideClickHandler({
containerRef: panelRef,
predicate: () => props.isOpen,
handler: () => handleHide(),
handler: () => handleHide()
})
let windowScrollTop = 0
@ -99,13 +99,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))
)
})
@ -114,7 +114,7 @@ export const NotificationsPanel = (props: Props) => {
await loadNotificationsGrouped({
after: after() || hourAgo(),
limit: PAGE_SIZE,
offset: loadedNotificationsCount(),
offset: loadedNotificationsCount()
})
if (loadedNotificationsCount() < totalNotificationsCount()) {
const hasMore = (scrollContainerRef?.scrollHeight || 0) <= (scrollContainerRef?.offsetHeight || 0)
@ -158,13 +158,13 @@ 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 = el)} class={styles.panel}>

View File

@ -11,7 +11,7 @@ import {
lazy,
on,
onCleanup,
onMount,
onMount
} from 'solid-js'
import { createStore } from 'solid-js/store'
@ -123,7 +123,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) {
setClearAbout(true)
@ -165,7 +165,7 @@ export const ProfileSettings = () => {
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
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?'
)
}
}
@ -181,8 +181,8 @@ export const ProfileSettings = () => {
if (Object.keys(prevForm).length > 0) {
setIsFloatingPanelVisible(!deepEqual(form, prevForm))
}
},
),
}
)
)
const handleDeleteSocialLink = (link: string) => {
@ -221,8 +221,8 @@ export const ProfileSettings = () => {
style={{
'background-image': `url(${getImageUrl(form.pic || '', {
width: 180,
height: 180,
})})`,
height: 180
})})`
}}
/>
<div class={styles.controls}>
@ -265,7 +265,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">

View File

@ -26,7 +26,7 @@ const scrollToHeader = (element: HTMLElement) => {
top:
element.getBoundingClientRect().top -
document.body.getBoundingClientRect().top -
DEFAULT_HEADER_OFFSET,
DEFAULT_HEADER_OFFSET
})
}
@ -49,7 +49,7 @@ export const TableOfContents = (props: Props) => {
if (parent) {
setHeadings(
// eslint-disable-next-line unicorn/prefer-spread
Array.from(parent.querySelectorAll<HTMLElement>('h1, h2, h3, h4')),
Array.from(parent.querySelectorAll<HTMLElement>('h1, h2, h3, h4'))
)
}
}
@ -66,8 +66,8 @@ export const TableOfContents = (props: Props) => {
createEffect(
on(
() => props.body,
(_) => debouncedUpdateHeadings(),
),
(_) => debouncedUpdateHeadings()
)
)
onMount(() => {
@ -83,7 +83,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}>
@ -100,7 +100,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) => {
@ -119,9 +119,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()
@ -157,9 +157,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()

View File

@ -33,7 +33,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 { session, requireAuthentication } = useSession()
const author = createMemo<Author>(() => session()?.user?.app_data?.profile as Author)
@ -45,7 +45,7 @@ export const TopicCard = (props: TopicProps) => {
const followed = follows?.topics?.some((topics) => topics.id === props.topic?.id)
setIsFollowed(Boolean(followed))
}
}),
})
)
const handleFollowClick = () => {
@ -63,7 +63,7 @@ export const TopicCard = (props: TopicProps) => {
classList={{
row: !props.subscribeButtonBottom,
[styles.topicCompact]: props.compact,
[styles.topicInRow]: props.isTopicInRow,
[styles.topicInRow]: props.isTopicInRow
}}
>
<div
@ -74,7 +74,7 @@ export const TopicCard = (props: TopicProps) => {
props.subscribeButtonBottom ||
props.isNarrow ||
props.compact
),
)
}}
>
<Show when={title() && !props.isCardMode}>
@ -109,7 +109,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>

View File

@ -50,7 +50,7 @@ export const FullTopic = (props: Props) => {
<div class={styles.topicDetailsItem}>
<Icon name="feed-all" class={styles.topicDetailsIcon} />
{t('some posts', {
count: props.topic?.stat?.shouts ?? 0,
count: props.topic?.stat?.shouts ?? 0
})}
</div>
</Show>

View File

@ -31,7 +31,7 @@ export const TopicBadge = (props: Props) => {
const followed = follows?.topics?.some((topics) => topics.id === props.topic?.id)
setIsFollowed(followed)
}
}),
})
)
const handleFollowClick = () => {
@ -58,11 +58,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 })}')`
}
}
/>

View File

@ -50,7 +50,7 @@ export const AllAuthors = (props: Props) => {
const byLetterFiltered = createMemo<{ [letter: string]: Author[] }>(() => {
return filteredAuthors().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>

View File

@ -22,7 +22,7 @@ type Props = {
export const TOPICS_PER_PAGE = 20
export const ABC = {
ru: 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ#',
en: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#',
en: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'
}
export const AllTopics = (props: Props) => {
@ -42,7 +42,7 @@ export const AllTopics = (props: Props) => {
acc[letter].push(topic)
return acc
},
{} as { [letter: string]: Topic[] },
{} as { [letter: string]: Topic[] }
)
})
@ -98,7 +98,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 (

View File

@ -67,7 +67,7 @@ export const AuthorView = (props: Props) => {
const resp = await query(loadShoutsQuery, {
filters: { author: props.authorSlug },
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedFeed().length,
offset: sortedFeed().length
})
const hasMore = resp?.data?.load_shouts_by?.hasMore
setIsLoadMoreButtonVisible(hasMore)
@ -92,7 +92,7 @@ export const AuthorView = (props: Props) => {
await loadAuthor(s)
setIsFetching(false) // Сброс состояния загрузки после завершения
}
}),
})
)
// 3 // after fetch loading following data
createEffect(
@ -112,8 +112,8 @@ export const AuthorView = (props: Props) => {
console.info(`[Author] followers for @${slug()} fetched`)
setIsFetching(false)
},
{ defer: true },
),
{ defer: true }
)
)
// догружает ленту и комментарии
@ -125,14 +125,14 @@ export const AuthorView = (props: Props) => {
await loadMore()
const resp = await query(loadReactionsBy, {
by: { comment: true, created_by: profile.id },
by: { comment: true, created_by: profile.id }
}).toPromise()
const ccc = resp?.data?.load_reactions_by
if (ccc) setCommented(ccc)
}
},
}
// { defer: true },
),
)
)
let bioContainerRef: HTMLDivElement
@ -149,13 +149,13 @@ export const AuthorView = (props: Props) => {
})
const pages = createMemo<Shout[][]>(() =>
splitToPages(sortedFeed(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
splitToPages(sortedFeed(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
)
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 || ''))
const handleDeleteComment = (id: number) => {

View File

@ -4,9 +4,9 @@ import { useLocalize } from '~/context/localize'
export const ConnectView = () => {
const { t } = useLocalize()
const [state, setState] = createSignal<'initial' | 'loading' | 'success' | 'error'>('initial')
let formRef: HTMLFormElement | null
let formRef: HTMLFormElement | null = null
const handleFormSubmit = async (e: SubmitEvent) => {
e.preventDefault()
setState('loading')
@ -14,51 +14,53 @@ export const ConnectView = () => {
const postData = formRef
? Array.from(formRef.elements).reduce(
(acc, element) => {
const formField = element as unknown as { name: string; value: string }
const formField = element as HTMLInputElement
if (formField.name) {
acc[formField.name] = formField.value
}
return acc
},
{} as Record<string, string>,
{} as Record<string, string>
)
: {}
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(postData),
body: JSON.stringify(postData)
}
const result = await fetch('/api/feedback', requestOptions)
if (!result.ok) {
console.error('[handleFormSubmit]', result)
try {
const result = await fetch('/api/feedback', requestOptions)
if (!result.ok) {
console.error('[handleFormSubmit]', result)
setState('error')
return
}
setState('success')
window.scrollTo({
top: 0
})
} catch (error) {
console.error('[handleFormSubmit]', error)
setState('error')
return
}
setState('success')
window.scrollTo({
top: 0,
})
}
return (
<article class="wide-container container--static-page">
<div class="row">
<div class="col-sm-20 col-md-16 col-lg-14 col-xl-12 offset-md-5">
<Show when={state() === 'loading' || state() === 'initial' || state() === 'error'}>
<Show when={state() !== 'success'}>
<h1>
<span class="wrapped">{t('Suggest an idea')}</span>
</h1>
<p>
{t(
'Want to suggest, discuss or advise something? Share a topic or an idea? Please send us a message!',
'Want to suggest, discuss or advise something? Share a topic or an idea? Please send us a message!'
)}
{t('Specify your e-mail and we will reply.')}
</p>
@ -97,7 +99,10 @@ export const ConnectView = () => {
<br />
{t('Something went wrong, please try again')}
</Show>
<Show when={state() === 'success'}>{t('Thank you for reaching us')}!</Show>
<Show when={state() === 'success'}>
<br />
{t('Thank you for reaching us')}!
</Show>
</div>
</div>
</article>

View File

@ -35,8 +35,8 @@ export const DraftsView = () => {
setLoading(false)
}
},
{ defer: true },
),
{ defer: true }
)
)
const { publishShoutById, deleteShout } = useEditorContext()

View File

@ -10,7 +10,7 @@ import {
lazy,
on,
onCleanup,
onMount,
onMount
} from 'solid-js'
import { createStore } from 'solid-js/store'
import { debounce } from 'throttle-debounce'
@ -50,7 +50,7 @@ type Props = {
export const MAX_HEADER_LIMIT = 100
export const EMPTY_TOPIC: Topic = {
id: -1,
slug: '',
slug: ''
}
const AUTO_SAVE_DELAY = 3000
@ -59,7 +59,7 @@ const handleScrollTopButtonClick = (ev: MouseEvent | TouchEvent) => {
ev.preventDefault()
window.scrollTo({
top: 0,
behavior: 'smooth',
behavior: 'smooth'
})
}
@ -74,7 +74,7 @@ export const EditView = (props: Props) => {
setFormErrors,
saveDraft,
saveDraftToLocalStorage,
getDraftFromLocalStorage,
getDraftFromLocalStorage
} = useEditorContext()
const [shoutTopics, setShoutTopics] = createSignal<Topic[]>([])
const [draft, setDraft] = createSignal()
@ -112,15 +112,15 @@ export const EditView = (props: Props) => {
body: shout.body || '',
coverImageUrl: shout.cover || '',
media: shout.media || '',
layout: shout.layout,
layout: shout.layout
}
setForm((_) => draftForm)
console.debug('draft from props data: ', draftForm)
}
}
},
{ defer: true },
),
{ defer: true }
)
)
createEffect(
@ -133,8 +133,8 @@ export const EditView = (props: Props) => {
console.debug('draft from localstorage: ', draftForm)
}
},
{ defer: true },
),
{ defer: true }
)
)
createEffect(
@ -153,8 +153,8 @@ export const EditView = (props: Props) => {
}
}
},
{ defer: true },
),
{ defer: true }
)
)
onMount(() => {
@ -170,7 +170,7 @@ export const EditView = (props: Props) => {
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
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?'
)
}
}
@ -209,7 +209,7 @@ export const EditView = (props: Props) => {
const [baseAudioFields, setBaseAudioFields] = createSignal({
artist: '',
date: '',
genre: '',
genre: ''
})
const handleBaseFieldsChange = (key: string, value: string) => {
@ -281,7 +281,7 @@ export const EditView = (props: Props) => {
<div class="wide-container">
<button
class={clsx(styles.scrollTopButton, {
[styles.visible]: isScrolled(),
[styles.visible]: isScrolled()
})}
onClick={handleScrollTopButtonClick}
>
@ -404,8 +404,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')}>

View File

@ -51,7 +51,7 @@ export const Expo = (props: Props) => {
const options: LoadShoutsOptions = {
filters: getLoadShoutsFilters(),
limit: count,
offset: expoShouts().length,
offset: expoShouts().length
}
options.filters = props.layout
@ -76,7 +76,7 @@ export const Expo = (props: Props) => {
const options: LoadShoutsOptions = {
filters: { ...getLoadShoutsFilters(), featured: true },
limit: 10,
random_limit: 100,
random_limit: 100
}
const resp = await query(getRandomTopShoutsQuery, { options }).toPromise()
setFavoriteTopArticles(resp?.data?.load_shouts_random_top || [])
@ -89,7 +89,7 @@ export const Expo = (props: Props) => {
const options: LoadShoutsOptions = {
filters: { ...getLoadShoutsFilters({ after }), reacted: true },
limit: 10,
random_limit: 10,
random_limit: 10
}
const resp = await query(getRandomTopShoutsQuery, { options }).toPromise()
@ -114,8 +114,8 @@ export const Expo = (props: Props) => {
loadMore(PRERENDERED_ARTICLES_COUNT + LOAD_MORE_PAGE_SIZE)
loadRandomTopArticles()
loadRandomTopMonthArticles()
},
),
}
)
)
onCleanup(() => {

View File

@ -89,12 +89,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 { query } = useGraphQL()
const [searchParams, changeSearchParams] = useSearchParams<FeedSearchParams>()
@ -152,8 +152,8 @@ export const FeedView = (props: Props) => {
([s, seen]) => {
if (s?.access_token && !(seen?.length > 0)) loadUnratedArticles()
},
{ defer: true },
),
{ defer: true }
)
)
// TODO: declare some details
@ -164,14 +164,14 @@ export const FeedView = (props: Props) => {
setSearchResults([])
loadMore()
},
{ defer: true },
),
{ defer: true }
)
)
const loadFeedShouts = () => {
const options: LoadShoutsOptions = {
limit: FEED_PAGE_SIZE,
offset: sortedFeed()?.length || 0,
offset: sortedFeed()?.length || 0
}
if (searchParams?.by) {
@ -185,7 +185,7 @@ export const FeedView = (props: Props) => {
} else if (visibilityMode) {
options.filters = {
...options.filters,
featured: visibilityMode === 'featured',
featured: visibilityMode === 'featured'
}
}
@ -205,8 +205,8 @@ export const FeedView = (props: Props) => {
loadReactionsBy({
by: {
shouts: newShouts.map((s) => s.slug),
},
shouts: newShouts.map((s) => s.slug)
}
})
setIsLoadMoreButtonVisible(hasMore)
@ -214,7 +214,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')
@ -252,7 +252,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={loc.pathname}>{t('Recent')}</A>
@ -262,7 +262,7 @@ export const FeedView = (props: Props) => {
{/*</li>*/}
<li
class={clsx({
'view-switcher__item--selected': searchParams?.by === 'likes',
'view-switcher__item--selected': searchParams?.by === 'likes'
})}
>
<span class="link" onClick={() => changeSearchParams({ by: 'likes' })}>
@ -271,7 +271,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' })}>

View File

@ -49,22 +49,22 @@ export const HomeView = (props: HomeViewProps) => {
const shoutsByTopicLoader = loadShouts({
filters: { topic: topic.slug, featured: true },
limit: 5,
offset: 0,
offset: 0
})
const shouts = await shoutsByTopicLoader()
setRandomTopicArticles(shouts || [])
}
},
{ defer: true },
),
{ defer: true }
)
)
const pages = createMemo<Shout[][]>(() =>
splitToPages(
props.featuredShouts || [],
SHOUTS_PER_PAGE + CLIENT_LOAD_ARTICLES_COUNT,
LOAD_MORE_PAGE_SIZE,
),
LOAD_MORE_PAGE_SIZE
)
)
return (

View File

@ -9,7 +9,7 @@ import type {
Chat,
ChatMember,
Message as MessageType,
MutationCreate_MessageArgs,
MutationCreate_MessageArgs
} from '../../../graphql/schema/chat.gen'
import type { Author } from '../../../graphql/schema/core.gen'
import SimplifiedEditor from '../../Editor/SimplifiedEditor'
@ -68,7 +68,7 @@ export const InboxView = (props: Props) => {
const handleOpenChat = async (chat: Chat) => {
setCurrentDialog(chat)
changeSearchParams({
chat: chat.id,
chat: chat.id
})
try {
const mmm = await getMessages?.(chat.id)
@ -80,7 +80,7 @@ export const InboxView = (props: Props) => {
} finally {
messagesContainerRef?.scroll({
top: messagesContainerRef?.scrollHeight,
behavior: 'instant',
behavior: 'instant'
})
}
}
@ -89,7 +89,7 @@ export const InboxView = (props: Props) => {
sendMessage?.({
body: message,
reply_to: messageToReply()?.id,
chat_id: currentDialog()?.id || '',
chat_id: currentDialog()?.id || ''
} as MutationCreate_MessageArgs)
setClear(true)
setMessageToReply(null)
@ -111,7 +111,7 @@ export const InboxView = (props: Props) => {
await loadChats()
changeSearchParams({
initChat: undefined,
chat: newChat.chat.id,
chat: newChat.chat.id
})
const chatToOpen = chats().find((chat) => chat.id === newChat.chat.id)
await handleOpenChat(chatToOpen as Chat)
@ -148,11 +148,11 @@ export const InboxView = (props: Props) => {
if (messagesContainerRef) {
messagesContainerRef?.scroll({
top: messagesContainerRef.scrollHeight,
behavior: 'smooth',
behavior: 'smooth'
})
}
},
),
}
)
)
const handleScrollMessageContainer = () => {
if (
@ -167,7 +167,7 @@ export const InboxView = (props: Props) => {
const handleScrollToNew = () => {
messagesContainerRef?.scroll({
top: messagesContainerRef?.scrollHeight,
behavior: 'smooth',
behavior: 'smooth'
})
setIsScrollToNewVisible(false)
}

View File

@ -34,7 +34,7 @@ export const ProfileSubscriptions = () => {
} else {
setFiltered(flat)
}
}),
})
)
createEffect(() => {
@ -61,7 +61,7 @@ export const ProfileSubscriptions = () => {
<ul class="view-switcher">
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'all',
'view-switcher__item--selected': followsFilter() === 'all'
})}
>
<button type="button" onClick={() => setFollowsFilter('all')}>
@ -70,7 +70,7 @@ export const ProfileSubscriptions = () => {
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'authors',
'view-switcher__item--selected': followsFilter() === 'authors'
})}
>
<button type="button" onClick={() => setFollowsFilter('authors')}>
@ -79,7 +79,7 @@ export const ProfileSubscriptions = () => {
</li>
<li
class={clsx({
'view-switcher__item--selected': followsFilter() === 'topics',
'view-switcher__item--selected': followsFilter() === 'topics'
})}
>
<button type="button" onClick={() => setFollowsFilter('topics')}>

View File

@ -36,7 +36,7 @@ const shorten = (str: string, maxLen: number) => {
const EMPTY_TOPIC: Topic = {
id: -1,
slug: '',
slug: ''
}
interface FormConfig {
@ -56,7 +56,7 @@ const emptyConfig: FormConfig = {
title: '',
subtitle: '',
description: '',
selectedTopics: [],
selectedTopics: []
}
export const PublishSettings = (props: Props) => {
@ -85,7 +85,7 @@ export const PublishSettings = (props: Props) => {
title: props.form?.title || '',
subtitle: props.form?.subtitle || '',
description: composeDescription() || '',
selectedTopics: [],
selectedTopics: []
}
})
@ -115,7 +115,7 @@ export const PublishSettings = (props: Props) => {
setSettingsForm((prev) => {
return {
...prev,
mainTopic: newSelectedTopics[0],
mainTopic: newSelectedTopics[0]
}
})
}
@ -171,7 +171,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}>
@ -193,7 +193,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>
@ -242,7 +242,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}>

View File

@ -36,7 +36,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)
@ -76,14 +76,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>

View File

@ -65,7 +65,7 @@ export const TopicView = (props: Props) => {
await loadTopicAuthors()
loadRandom()
}
}),
})
)
const [followers, setFollowers] = createSignal<Author[]>(props.followers || [])
@ -84,7 +84,7 @@ export const TopicView = (props: Props) => {
const options: LoadShoutsOptions = {
filters: { featured: true, topic: topic },
limit: 10,
random_limit: 100,
random_limit: 100
}
const resp = await query(getRandomTopShoutsQuery, { options }).toPromise()
setFavoriteTopArticles(resp?.data?.l)
@ -97,7 +97,7 @@ export const TopicView = (props: Props) => {
const options: LoadShoutsOptions = {
filters: { after: after, featured: true, topic: topic },
limit: 10,
random_limit: 10,
random_limit: 10
}
const resp = await query(loadShoutsRandomQuery, { options }).toPromise()
@ -117,8 +117,8 @@ export const TopicView = (props: Props) => {
lang() === 'en'
? (topic() as Topic)?.slug.replace(/-/, ' ')
: (topic() as Topic)?.title || (topic() as Topic)?.slug.replace(/-/, ' '),
true,
)}`,
true
)}`
)
const loadMore = async () => {
@ -127,7 +127,7 @@ export const TopicView = (props: Props) => {
const { hasMore } = await loadShouts({
filters: { topic: topic()?.slug },
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedFeed().length, // FIXME: use feedByTopic
offset: sortedFeed().length // FIXME: use feedByTopic
})
setIsLoadMoreButtonVisible(hasMore)
@ -150,7 +150,7 @@ export const TopicView = (props: Props) => {
})
*/
const pages = createMemo<Shout[][]>(() =>
splitToPages(sortedFeed(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE),
splitToPages(sortedFeed(), PRERENDERED_ARTICLES_COUNT, LOAD_MORE_PAGE_SIZE)
)
const ogImage = () =>
@ -181,14 +181,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'
})
}
>

View File

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

View File

@ -72,14 +72,14 @@ export const DropArea = (props: Props) => {
const { selectFiles } = createFileUploader({
multiple: true,
accept: `${props.fileType}/*`,
accept: `${props.fileType}/*`
})
const { setRef: dropzoneRef, files: droppedFiles } = createDropzone({
onDrop: async () => {
setDragActive(false)
await initUpload(droppedFiles())
},
}
})
/**

View File

@ -48,7 +48,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>
@ -63,7 +63,7 @@ export const DropDown = <TOption extends Option = Option>(props: Props<TOption>)
<li>
<button
class={clsx(popupStyles.action, {
[styles.active]: props.currentOption.value === option.value,
[styles.active]: props.currentOption.value === option.value
})}
onClick={() => props.onChange(option)}
>

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