async-fixes

This commit is contained in:
Untone 2024-02-05 18:04:23 +03:00
parent 238a17a9de
commit 361c916687
32 changed files with 62 additions and 59 deletions

View File

@ -52,11 +52,17 @@
"noSvgWithoutTitle": "off" "noSvgWithoutTitle": "off"
}, },
"nursery": { "nursery": {
"useImportRestrictions": "off" "all": true,
"useImportRestrictions": "off",
"useImportType": "off",
"useFilenamingConvention": "off"
}, },
"style": { "style": {
"all": true,
"useBlockStatements": "off",
"noImplicitBoolean": "off",
"useNamingConvention": "off", "useNamingConvention": "off",
"noUnusedTemplateLiteral": "off" "noDefaultExport": "off"
}, },
"suspicious": { "suspicious": {
"noConsoleLog": "off", "noConsoleLog": "off",

View File

@ -81,8 +81,8 @@ export const FullArticle = (props: Props) => {
const canEdit = () => props.article.authors?.some((a) => Boolean(a) && a?.slug === author()?.slug) const canEdit = () => props.article.authors?.some((a) => Boolean(a) && a?.slug === author()?.slug)
const mainTopic = createMemo(() => { const mainTopic = createMemo(() => {
const main_topic_slug = props.article.topics.length > 0 ? props.article.main_topic : null const mainTopicSlug = props.article.topics.length > 0 ? props.article.main_topic : null
const mt = props.article.topics.find((tpc: Topic) => tpc.slug === main_topic_slug) const mt = props.article.topics.find((tpc: Topic) => tpc.slug === mainTopicSlug)
if (mt) { if (mt) {
mt.title = lang() === 'en' ? capitalize(mt.slug.replace(/-/, ' ')) : mt.title mt.title = lang() === 'en' ? capitalize(mt.slug.replace(/-/, ' ')) : mt.title
return mt return mt

View File

@ -52,7 +52,7 @@ export const ShoutRatingControl = (props: ShoutRatingControlProps) => {
return deleteReaction(reactionToDelete.id) return deleteReaction(reactionToDelete.id)
} }
const handleRatingChange = async (isUpvote: boolean) => { const handleRatingChange = (isUpvote: boolean) => {
requireAuthentication(async () => { requireAuthentication(async () => {
setIsLoading(true) setIsLoading(true)
if (isUpvoted()) { if (isUpvoted()) {

View File

@ -15,7 +15,7 @@ export const AuthGuard = (props: Props) => {
const { isAuthenticated, isSessionLoaded } = useSession() const { isAuthenticated, isSessionLoaded } = useSession()
const { changeSearchParams } = useRouter<RootSearchParams & AuthModalSearchParams>() const { changeSearchParams } = useRouter<RootSearchParams & AuthModalSearchParams>()
createEffect(async () => { createEffect(() => {
if (props.disabled) { if (props.disabled) {
return return
} }

View File

@ -51,7 +51,7 @@ export const AuthorBadge = (props: Props) => {
const initChat = () => { const initChat = () => {
// eslint-disable-next-line solid/reactivity // eslint-disable-next-line solid/reactivity
requireAuthentication(() => { requireAuthentication(() => {
openPage(router, `inbox`) openPage(router, 'inbox')
changeSearchParams({ changeSearchParams({
initChat: props.author.id.toString(), initChat: props.author.id.toString(),
}) })

View File

@ -2,7 +2,7 @@ import type { Author, Community } from '../../../graphql/schema/core.gen'
import { openPage, redirectPage } from '@nanostores/router' import { openPage, redirectPage } from '@nanostores/router'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' import { For, Show, createEffect, createMemo, createSignal, onMount } from 'solid-js'
import { useFollowing } from '../../../context/following' import { useFollowing } from '../../../context/following'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
@ -61,7 +61,7 @@ export const AuthorCard = (props: Props) => {
const initChat = () => { const initChat = () => {
// eslint-disable-next-line solid/reactivity // eslint-disable-next-line solid/reactivity
requireAuthentication(() => { requireAuthentication(() => {
openPage(router, `inbox`) openPage(router, 'inbox')
changeSearchParams({ changeSearchParams({
initChat: props.author.id.toString(), initChat: props.author.id.toString(),
}) })

View File

@ -89,7 +89,7 @@ export const Footer = () => {
}, },
]) ])
const SOCIAL = [ const social = [
{ {
name: 'facebook', name: 'facebook',
href: 'https://facebook.com/discoursio', href: 'https://facebook.com/discoursio',
@ -146,7 +146,7 @@ export const Footer = () => {
<a href="/about/terms-of-use">{t('Terms of use')}</a> <a href="/about/terms-of-use">{t('Terms of use')}</a>
</div> </div>
<div class={clsx(styles.footerCopyrightSocial, 'col-md-6 col-lg-4')}> <div class={clsx(styles.footerCopyrightSocial, 'col-md-6 col-lg-4')}>
<For each={SOCIAL}> <For each={social}>
{(social) => ( {(social) => (
<div class={clsx(styles.socialItem, styles[`socialItem${social.name}`])}> <div class={clsx(styles.socialItem, styles[`socialItem${social.name}`])}>
<a href={social.href}> <a href={social.href}>

View File

@ -1,4 +1,5 @@
import { Buffer } from 'buffer' // biome-ignore lint/nursery/noNodejsModules: TODO: maybe cause problems
import { Buffer } from 'node:buffer'
import { clsx } from 'clsx' import { clsx } from 'clsx'
import { Show } from 'solid-js' import { Show } from 'solid-js'

View File

@ -22,12 +22,13 @@ type FloatingMenuProps = {
ref: (el: HTMLDivElement) => void ref: (el: HTMLDivElement) => void
} }
const embedData = async (data) => { const embedData = (data) => {
const element = document.createRange().createContextualFragment(data) const element = document.createRange().createContextualFragment(data)
const { attributes } = element.firstChild as HTMLIFrameElement const { attributes } = element.firstChild as HTMLIFrameElement
const result: { src: string; width?: string; height?: string } = { src: '' } const result: { src: string; width?: string; height?: string } = { src: '' }
// biome-ignore lint/nursery/useForOf: <explanation>
for (let i = 0; i < attributes.length; i++) { for (let i = 0; i < attributes.length; i++) {
const attribute = attributes[i] const attribute = attributes[i]
result[attribute.name] = attribute.value result[attribute.name] = attribute.value
@ -69,7 +70,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
.run() .run()
} }
const validateEmbed = async (value) => { const validateEmbed = (value) => {
const element = document.createRange().createContextualFragment(value) const element = document.createRange().createContextualFragment(value)
if (element.firstChild?.nodeName !== 'IFRAME') { if (element.firstChild?.nodeName !== 'IFRAME') {
return t('Error') return t('Error')

View File

@ -216,7 +216,7 @@ const SimplifiedEditor = (props: Props) => {
} }
}) })
const handleKeyDown = async (event) => { const handleKeyDown = (event) => {
if (isEmpty() || !isFocused()) { if (isEmpty() || !isFocused()) {
return return
} }

View File

@ -71,7 +71,7 @@ export const TextBubbleMenu = (props: BubbleMenuProps) => {
} }
setListBubbleOpen((prev) => !prev) setListBubbleOpen((prev) => !prev)
} }
const handleKeyDown = async (event) => { const handleKeyDown = (event) => {
if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !props.editor.state.selection.empty) { if (event.code === 'KeyK' && (event.metaKey || event.ctrlKey) && !props.editor.state.selection.empty) {
event.preventDefault() event.preventDefault()
setLinkEditorOpen(true) setLinkEditorOpen(true)

View File

@ -56,7 +56,7 @@ export const UploadModalContent = (props: Props) => {
} }
} }
const handleUpload = async () => { const handleUpload = () => {
selectFiles(async ([uploadFile]) => { selectFiles(async ([uploadFile]) => {
await runUpload(uploadFile) await runUpload(uploadFile)
}) })

View File

@ -57,7 +57,7 @@ export const VideoUploader = (props: Props) => {
} }
} }
const handleUrlInput = async (value: string) => { const handleUrlInput = (value: string) => {
setError() setError()
if (validateUrl(value)) { if (validateUrl(value)) {
props.onVideoAdd(composeMediaItems([{ url: value }])) props.onVideoAdd(composeMediaItems([{ url: value }]))

View File

@ -48,7 +48,7 @@ export const LoginForm = () => {
setPassword(newPassword) setPassword(newPassword)
} }
const handleSendLinkAgainClick = async (event: Event) => { const handleSendLinkAgainClick = (event: Event) => {
event.preventDefault() event.preventDefault()
setIsLinkSent(true) setIsLinkSent(true)

View File

@ -56,7 +56,7 @@ export const AuthModal = () => {
classList={{ [styles.hidden]: mode() !== 'register' && mode() !== 'confirm-email' }} classList={{ [styles.hidden]: mode() !== 'register' && mode() !== 'confirm-email' }}
> >
<div> <div>
<h4>{t(`Join the global community of authors!`)}</h4> <h4>{t('Join the global community of authors!')}</h4>
<p class={styles.authBenefits}> <p class={styles.authBenefits}>
{t( {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',

View File

@ -9,7 +9,7 @@ import { useFollowing } from '../../../context/following'
import { useLocalize } from '../../../context/localize' import { useLocalize } from '../../../context/localize'
import { apiClient } from '../../../graphql/client/core' import { apiClient } from '../../../graphql/client/core'
import { router, useRouter } from '../../../stores/router' import { router, useRouter } from '../../../stores/router'
import { loadMyFeed, loadShouts, useArticlesStore } from '../../../stores/zine/articles' import { loadShouts, useArticlesStore } from '../../../stores/zine/articles'
import { loadAuthor, useAuthorsStore } from '../../../stores/zine/authors' import { loadAuthor, useAuthorsStore } from '../../../stores/zine/authors'
import { getImageUrl } from '../../../utils/getImageUrl' import { getImageUrl } from '../../../utils/getImageUrl'
import { getDescription } from '../../../utils/meta' import { getDescription } from '../../../utils/meta'
@ -61,7 +61,7 @@ export const AuthorView = (props: Props) => {
createEffect(() => { createEffect(() => {
if (author()?.id && !author().stat) { if (author()?.id && !author().stat) {
const a = loadAuthor({ slug: '', author_id: author().id }) const a = loadAuthor({ slug: '', author_id: author().id })
console.debug(`[AuthorView] loaded author:`, a) console.debug('[AuthorView] loaded author:', a)
} }
}) })
@ -128,7 +128,7 @@ export const AuthorView = (props: Props) => {
const data = await apiClient.getReactionsBy({ const data = await apiClient.getReactionsBy({
by: { comment: false, created_by: commenter.id }, by: { comment: false, created_by: commenter.id },
}) })
console.debug(`[components.Author] fetched comments`, data) console.debug('[components.Author] fetched comments', data)
setCommented(data) setCommented(data)
} }

View File

@ -5,7 +5,7 @@ import { createStore } from 'solid-js/store'
import { ShoutForm, useEditorContext } from '../../context/editor' import { ShoutForm, useEditorContext } from '../../context/editor'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'
import { type Shout, type Topic } from '../../graphql/schema/core.gen' import type { Shout, Topic } from '../../graphql/schema/core.gen'
import { LayoutType, MediaItem } from '../../pages/types' import { LayoutType, MediaItem } from '../../pages/types'
import { useRouter } from '../../stores/router' import { useRouter } from '../../stores/router'
import { clone } from '../../utils/clone' import { clone } from '../../utils/clone'
@ -112,7 +112,7 @@ export const EditView = (props: Props) => {
const handleBeforeUnload = (event) => { const handleBeforeUnload = (event) => {
if (!deepEqual(prevForm, form)) { if (!deepEqual(prevForm, form)) {
event.returnValue = t( 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?',
) )
} }
} }

View File

@ -7,12 +7,7 @@ import { useLocalize } from '../../../context/localize'
import { useReactions } from '../../../context/reactions' import { useReactions } from '../../../context/reactions'
import { useSession } from '../../../context/session' import { useSession } from '../../../context/session'
import { apiClient } from '../../../graphql/client/core' import { apiClient } from '../../../graphql/client/core'
import { import type { Author, LoadShoutsOptions, Reaction, Shout } from '../../../graphql/schema/core.gen'
type Author,
type LoadShoutsOptions,
type Reaction,
type Shout,
} from '../../../graphql/schema/core.gen'
import { router, useRouter } from '../../../stores/router' import { router, useRouter } from '../../../stores/router'
import { showModal } from '../../../stores/ui' import { showModal } from '../../../stores/ui'
import { resetSortedArticles, useArticlesStore } from '../../../stores/zine/articles' import { resetSortedArticles, useArticlesStore } from '../../../stores/zine/articles'

View File

@ -9,7 +9,6 @@ import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session' import { useSession } from '../../../context/session'
import { useRouter } from '../../../stores/router' import { useRouter } from '../../../stores/router'
import { showModal } from '../../../stores/ui' import { showModal } from '../../../stores/ui'
import { useAuthorsStore } from '../../../stores/zine/authors'
import SimplifiedEditor from '../../Editor/SimplifiedEditor' import SimplifiedEditor from '../../Editor/SimplifiedEditor'
import DialogCard from '../../Inbox/DialogCard' import DialogCard from '../../Inbox/DialogCard'
import DialogHeader from '../../Inbox/DialogHeader' import DialogHeader from '../../Inbox/DialogHeader'
@ -83,7 +82,7 @@ export const InboxView = (props: Props) => {
} }
} }
const handleSubmit = async (message: string) => { const handleSubmit = (message: string) => {
sendMessage({ sendMessage({
body: message, body: message,
chat_id: currentDialog()?.id.toString(), chat_id: currentDialog()?.id.toString(),

View File

@ -50,7 +50,7 @@ export const SearchView = (props: Props) => {
restoreScrollPosition() restoreScrollPosition()
} }
onMount(async () => { onMount(() => {
const q = window.location.pathname.replace('/search/', '') || props.query const q = window.location.pathname.replace('/search/', '') || props.query
setQuery(q) setQuery(q)
searchEl.value = q searchEl.value = q

View File

@ -74,7 +74,7 @@ export const TopicView = (props: Props) => {
restoreScrollPosition() restoreScrollPosition()
} }
onMount(async () => { onMount(() => {
if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) { if (sortedArticles().length === PRERENDERED_ARTICLES_COUNT) {
loadMore() loadMore()
} }

View File

@ -33,7 +33,7 @@ const GrowingTextarea = (props: Props) => {
props.value(textareaValue) props.value(textareaValue)
} }
const handleKeyDown = async (event) => { const handleKeyDown = (event) => {
if (event.key === 'Enter' && event.shiftKey) { if (event.key === 'Enter' && event.shiftKey) {
return return
} }

View File

@ -84,7 +84,7 @@ export const EditorSwiper = (props: Props) => {
const { selectFiles } = createFileUploader({ const { selectFiles } = createFileUploader({
multiple: true, multiple: true,
accept: `image/*`, accept: 'image/*',
}) })
const initUpload = async (selectedFiles) => { const initUpload = async (selectedFiles) => {

View File

@ -27,6 +27,8 @@ type KebabObjectKeys<T> = {
type SwiperRef = HTMLElement & { swiper: Swiper; initialize: () => void } type SwiperRef = HTMLElement & { swiper: Swiper; initialize: () => void }
declare module 'solid-js' { declare module 'solid-js' {
// biome-ignore lint/style/useNamingConvention: JSX is ok
// biome-ignore lint/style/noNamespace: TODO: explain why
namespace JSX { namespace JSX {
interface IntrinsicElements { interface IntrinsicElements {
'swiper-container': SwiperContainerAttributes 'swiper-container': SwiperContainerAttributes

View File

@ -29,7 +29,7 @@ export interface ConnectContextType {
const ConnectContext = createContext<ConnectContextType>() const ConnectContext = createContext<ConnectContextType>()
export const ConnectProvider = (props: { children: JSX.Element }) => { export const ConnectProvider = (props: { children: JSX.Element }) => {
const [messageHandlers, setHandlers] = createSignal<Array<MessageHandler>>([]) const [messageHandlers, setHandlers] = createSignal<MessageHandler[]>([])
// const [messages, setMessages] = createSignal<Array<SSEMessage>>([]); // const [messages, setMessages] = createSignal<Array<SSEMessage>>([]);
const [connected, setConnected] = createSignal(false) const [connected, setConnected] = createSignal(false)
const { session } = useSession() const { session } = useSession()

View File

@ -13,10 +13,10 @@ type InboxContextType = {
chats: Accessor<Chat[]> chats: Accessor<Chat[]>
messages?: Accessor<Message[]> messages?: Accessor<Message[]>
createChat: (members: number[], title: string) => Promise<{ chat: Chat }> createChat: (members: number[], title: string) => Promise<{ chat: Chat }>
loadChats: () => Promise<Array<Chat>> loadChats: () => Promise<Chat[]>
loadRecipients: () => Array<Author> loadRecipients: () => Author[]
loadMessages: (by: MessagesBy, limit: number, offset: number) => Promise<Array<Message>> loadMessages: (by: MessagesBy, limit: number, offset: number) => Promise<Message[]>
getMessages?: (chatId: string) => Promise<Array<Message>> getMessages?: (chatId: string) => Promise<Message[]>
sendMessage?: (args: MutationCreate_MessageArgs) => void sendMessage?: (args: MutationCreate_MessageArgs) => void
} }
@ -47,7 +47,7 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
const { addHandler } = useConnect() const { addHandler } = useConnect()
addHandler(handleMessage) addHandler(handleMessage)
const loadMessages = async (by: MessagesBy, limit = 50, offset = 0): Promise<Array<Message>> => { const loadMessages = async (by: MessagesBy, limit = 50, offset = 0): Promise<Message[]> => {
if (inboxClient.private) { if (inboxClient.private) {
const msgs = await inboxClient.loadChatMessages({ by, limit, offset }) const msgs = await inboxClient.loadChatMessages({ by, limit, offset })
setMessages((mmm) => [...new Set([...mmm, ...msgs])]) setMessages((mmm) => [...new Set([...mmm, ...msgs])])

View File

@ -58,7 +58,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
setTotalNotificationsCount(total) setTotalNotificationsCount(total)
setUnreadNotificationsCount(unread) setUnreadNotificationsCount(unread)
setNotificationEntities(newGroupsEntries) setNotificationEntities(newGroupsEntries)
console.debug(`[context.notifications] groups updated`, groups) console.debug('[context.notifications] groups updated', groups)
return groups return groups
} }
return [] return []
@ -75,7 +75,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
onMount(() => { onMount(() => {
addHandler((data: SSEMessage) => { addHandler((data: SSEMessage) => {
if (data.entity === 'reaction' && isAuthenticated()) { if (data.entity === 'reaction' && isAuthenticated()) {
console.info(`[context.notifications] event`, data) console.info('[context.notifications] event', data)
loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) }) loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) })
} }
}) })

View File

@ -38,7 +38,7 @@ export const ProfileFormProvider = (props: { children: JSX.Element }) => {
} }
} }
createEffect(async () => { createEffect(() => {
if (author()) { if (author()) {
const currentAuthor = author() const currentAuthor = author()
setForm({ setForm({

View File

@ -5,7 +5,6 @@ import type { Author } from '../graphql/schema/core.gen'
import { import {
ApiResponse, ApiResponse,
AuthToken, AuthToken,
AuthorizeResponse,
Authorizer, Authorizer,
ConfigType, ConfigType,
ForgotPasswordInput, ForgotPasswordInput,
@ -71,6 +70,7 @@ export type SessionContextType = {
authorizer: () => Authorizer authorizer: () => Authorizer
} }
// biome-ignore lint/nursery/noEmptyBlockStatements: noop
const noop = () => {} const noop = () => {}
const SessionContext = createContext<SessionContextType>() const SessionContext = createContext<SessionContextType>()
@ -257,7 +257,7 @@ export const SessionProvider = (props: {
), ),
) )
const [authCallback, setAuthCallback] = createSignal<() => void>(() => {}) const [authCallback, setAuthCallback] = createSignal<() => void>(noop)
const requireAuthentication = (callback: () => void, modalSource: AuthModalSource) => { const requireAuthentication = (callback: () => void, modalSource: AuthModalSource) => {
setAuthCallback((_cb) => callback) setAuthCallback((_cb) => callback)
if (!session()) { if (!session()) {
@ -285,13 +285,8 @@ export const SessionProvider = (props: {
} }
return { data: resp?.data, errors: resp?.errors } return { data: resp?.data, errors: resp?.errors }
} }
const signUp = async (params: SignupInput) => { const signUp = async (params: SignupInput) => await authenticate(authorizer().signup, params)
return authenticate(authorizer().signup, params) const signIn = async (params: LoginInput) => await authenticate(authorizer().login, params)
}
const signIn = async (params: LoginInput) => {
return authenticate(authorizer().login, params)
}
const signOut = async () => { const signOut = async () => {
const authResult: ApiResponse<GenericResponse> = await authorizer().logout() const authResult: ApiResponse<GenericResponse> = await authorizer().logout()

View File

@ -1,4 +1,6 @@
import { ROUTES } from '../stores/router' import { ROUTES } from '../stores/router'
import { getServerRoute } from '../utils/getServerRoute' import { getServerRoute as gsr } from '../utils/getServerRoute'
export default getServerRoute(ROUTES.drafts) const getServerRoute = () => gsr(ROUTES.drafts)
export { getServerRoute }

View File

@ -6,7 +6,7 @@ import { SearchResult } from '../graphql/schema/core.gen'
export const onBeforeRender = async (pageContext: PageContext) => { export const onBeforeRender = async (pageContext: PageContext) => {
const { q: text } = pageContext.routeParams const { q: text } = pageContext.routeParams
const searchResults: Array<SearchResult> = await apiClient.getShoutsSearch({ text, limit: 50 }) const searchResults: SearchResult[] = await apiClient.getShoutsSearch({ text, limit: 50 })
const pageProps: PageProps = { searchResults, seo: { title: '' } } const pageProps: PageProps = { searchResults, seo: { title: '' } }
return { return {

View File

@ -1,4 +1,6 @@
// /:slug -> /@slug // /:slug -> /@slug
// https://vike.dev/routing // https://vike.dev/routing
// https://www.npmjs.com/package/@nanostores/router // https://www.npmjs.com/package/@nanostores/router
export const getServerRoute = (clientRoute: string) => clientRoute.replaceAll(':', '@') import { ROUTES } from '../stores/router'
export const getServerRoute = (clientRoute: string = ROUTES) => clientRoute.replaceAll(':', '@')