refactor contextual modals (#141)

* refactor contextual modals

* refactor by review comments

* refactored auth modal header to separate component, lint

---------

Co-authored-by: bniwredyc <bniwredyc@gmail.com>
This commit is contained in:
Arkadzi Rakouski 2023-07-30 15:31:54 +03:00 committed by GitHub
parent ed5f34ac69
commit d86d5e6028
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 184 additions and 89 deletions

View File

@ -30,16 +30,12 @@ module.exports = {
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^log$'
argsIgnorePattern: '^_'
}
],
// TODO: Remove any usage and enable
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'error',
// solid-js fix
'import/no-unresolved': [2, { ignore: ['solid-js/'] }]
// TODO: Remove any usage and enable
'@typescript-eslint/no-explicit-any': 'off'
}
}
],

View File

@ -61,11 +61,11 @@
"Create Chat": "Create Chat",
"Create Group": "Create a group",
"Create account": "Create an account",
"Create account from bookmark": "Create an account to add to your bookmarks",
"Create account from discussions": "Create an account to participate in discussions",
"Create account from follow": "Create an account to subscribe",
"Create account from subscribe": "Create an account to subscribe to new publications",
"Create account from vote": "Create an account to vote",
"Create an account to add to your bookmarks": "Create an account to add to your bookmarks",
"Create an account to participate in discussions": "Create an account to participate in discussions",
"Create an account to subscribe": "Create an account to subscribe",
"Create an account to subscribe to new publications": "Create an account to subscribe to new publications",
"Create an account to vote": "Create an account to vote",
"Create gallery": "Create gallery",
"Create post": "Create post",
"Create video": "Create video",
@ -91,6 +91,15 @@
"Enter image title": "Enter image title",
"Enter text": "Enter text",
"Enter the Discours": "Enter the Discours",
"Enter the Discours to add to your bookmarks": "Enter the Discours to add to your bookmarks",
"Enter the Discours to participate in discussions": "Enter the Discours to participate in discussions",
"Enter the Discours to subscribe": "Enter the Discours to subscribe",
"Enter the Discours to subscribe to new publications": "Enter the Discours to subscribe to new publications",
"Enter the Discours to vote": "Enter the Discours to vote",
"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",
"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",
"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",
"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",
"Enter the code or click the link from email to confirm": "Enter the code from the email or follow the link in the email to confirm registration",
"Enter your new password": "Enter your new password",
"Error": "Error",

View File

@ -65,11 +65,11 @@
"Create Chat": "Создать чат",
"Create Group": "Создать группу",
"Create account": "Создать аккаунт",
"Create account from bookmark": "Создайте аккаунт, чтобы добавить в закладки",
"Create account from discussions": "Создайте аккаунт для участия в дискуссиях",
"Create account from follow": "Создайте аккаунт, чтобы подписаться",
"Create account from subscribe": "Создайте аккаунт для подписки на новые публикации",
"Create account from vote": "Создайте аккаунт, чтобы голосовать",
"Create an account to add to your bookmarks": "Создайте аккаунт, чтобы добавить в закладки",
"Create an account to participate in discussions": "Создайте аккаунт для участия в дискуссиях",
"Create an account to subscribe": "Создайте аккаунт, чтобы подписаться",
"Create an account to subscribe to new publications": "Создайте аккаунт для подписки на новые публикации",
"Create an account to vote": "Создайте аккаунт, чтобы голосовать",
"Create gallery": "Создать галерею",
"Create post": "Создать публикацию",
"Create video": "Создать видео",
@ -96,6 +96,15 @@
"Enter image title": "Введите название изображения",
"Enter text": "Введите текст",
"Enter the Discours": "Войти в Дискурс",
"Enter the Discours to add to your bookmarks": "Войдите в Дискурс, чтобы добавить в закладки",
"Enter the Discours to participate in discussions": "Войдите в Дискурс для участия в дискуссиях",
"Enter the Discours to subscribe to new publications": "Войдите в Дискурс, чтобы подписаться",
"Enter the Discours to subscribe": "Войдите в Дискурс для подписки на новые публикации",
"Enter the Discours to vote": "Войдите в Дискурс, чтобы голосовать",
"In&nbsp;bookmarks, you can save favorite discussions and&nbsp;materials that you want to return to": "В&nbsp;закладках можно сохранять избранные дискуссии и&nbsp;материалы, к&nbsp;которым хочется вернуться",
"You&nbsp;ll be able to participate in&nbsp;discussions, rate others' comments and&nbsp;learn about&nbsp;new responses": "Вы&nbsp;сможете участвовать в&nbsp;обсуждениях, оценивать комментарии других и&nbsp;узнавать о&nbsp;новых ответах",
"This way you&nbsp;ll be able to subscribe to&nbsp;authors, interesting topics and&nbsp;customize your feed": "Так вы&nbsp;сможете подписаться на&nbsp;авторов, интересные темы и&nbsp;настроить свою ленту",
"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": "Так мы&nbsp;поймем, что вы&nbsp;реальный человек, и&nbsp;учтем ваш голос. А&nbsp;вы&nbsp;увидите, как проголосовали другие",
"Enter the code or click the link from email to confirm": "Введите код из письма или пройдите по ссылке в письме для подтверждения регистрации",
"Enter your new password": "Введите новый пароль",
"Error": "Ошибка",

View File

@ -1,5 +1,4 @@
import { createSignal, Show } from 'solid-js'
import MarkdownIt from 'markdown-it'
import { clsx } from 'clsx'
import styles from './Message.module.scss'
import DialogAvatar from './DialogAvatar'

View File

@ -0,0 +1,7 @@
.authFormDescription {
font-size: 14px;
font-weight: 400;
line-height: 18px;
letter-spacing: -0.14px;
margin: 12px 0 54px;
}

View File

@ -0,0 +1,81 @@
import styles from './AuthModalHeader.module.scss'
import { Show } from 'solid-js'
import { useLocalize } from '../../../../context/localize'
import { useRouter } from '../../../../stores/router'
import { AuthModalSearchParams } from '../types'
type Props = {
modalType: 'login' | 'register'
}
export const AuthModalHeader = (props: Props) => {
const { t } = useLocalize()
const { searchParams } = useRouter<AuthModalSearchParams>()
const { source } = searchParams()
const generateModalTextsFromSource = (
modalType: 'login' | 'register'
): { title: string; description: string } => {
const title = modalType === 'login' ? 'Enter the Discours' : 'Create account'
switch (source) {
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'
)
}
}
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"
)
}
}
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'
)
}
}
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'
)
}
}
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'
)
}
}
default: {
return {
title: t(title),
description: ''
}
}
}
}
const { title, description } = generateModalTextsFromSource(props.modalType)
return (
<>
<h4>{title}</h4>
<Show when={description}>
<p class={styles.authFormDescription} innerHTML={description} />
</Show>
</>
)
}

View File

@ -0,0 +1 @@
export { AuthModalHeader } from './AuthModalHeader'

View File

@ -10,11 +10,10 @@ import { hideModal } from '../../../stores/ui'
import { useSession } from '../../../context/session'
import { signSendLink } from '../../../stores/auth'
import { validateEmail } from '../../../utils/validateEmail'
import { generateModalTitleFromSource } from '../../../utils/custom-i18n'
import { useSnackbar } from '../../../context/snackbar'
import { useLocalize } from '../../../context/localize'
import { Icon } from '../../_shared/Icon'
import { AuthModalHeader } from './AuthModalHeader'
type FormFields = {
email: string
@ -118,7 +117,7 @@ export const LoginForm = () => {
return (
<form onSubmit={handleSubmit} class={styles.authForm}>
<div>
<h4>{generateModalTitleFromSource('login')}</h4>
<AuthModalHeader modalType="login" />
<Show when={submitError()}>
<div class={styles.authInfo}>
<div class={styles.warn}>{submitError()}</div>

View File

@ -12,7 +12,7 @@ import { checkEmail, useEmailChecks } from '../../../stores/emailChecks'
import { register } from '../../../stores/auth'
import { useLocalize } from '../../../context/localize'
import { validateEmail } from '../../../utils/validateEmail'
import { generateModalTitleFromSource } from '../../../utils/custom-i18n'
import { AuthModalHeader } from './AuthModalHeader'
type FormFields = {
name: string
@ -137,7 +137,7 @@ export const RegisterForm = () => {
<Show when={!isSuccess()}>
<form onSubmit={handleSubmit} class={styles.authForm}>
<div>
<h4>{generateModalTitleFromSource('register')}</h4>
<AuthModalHeader modalType="register" />
<Show when={submitError()}>
<div class={styles.authInfo}>
<ul>

View File

@ -1,5 +1,5 @@
import { Dynamic } from 'solid-js/web'
import { Component, createEffect, createMemo } from 'solid-js'
import { Show, Component, createEffect, createMemo } from 'solid-js'
import { hideModal } from '../../../stores/ui'
import { useRouter } from '../../../stores/router'
import { clsx } from 'clsx'
@ -24,6 +24,8 @@ export const AuthModal = () => {
const { t } = useLocalize()
const { searchParams } = useRouter<AuthModalSearchParams>()
const { source } = searchParams()
const mode = createMemo<AuthModalMode>(() => {
return AUTH_MODAL_MODES[searchParams().mode] ? searchParams().mode : 'login'
})
@ -37,39 +39,47 @@ export const AuthModal = () => {
return (
<div
ref={rootRef}
class={clsx('row', styles.view)}
class={clsx(styles.view, {
row: !source
})}
classList={{ [styles.signUp]: mode() === 'register' || mode() === 'confirm-email' }}
>
<div class={clsx('col-md-12 d-none d-md-flex', styles.authImage)}>
<div
class={styles.authImageText}
classList={{ [styles.hidden]: mode() !== 'register' && mode() !== 'confirm-email' }}
>
<div>
<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'
)}
.&nbsp;
{t('New stories every day and even more!')}
<Show when={!source}>
<div class={clsx('col-md-12 d-none d-md-flex', styles.authImage)}>
<div
class={styles.authImageText}
classList={{ [styles.hidden]: mode() !== 'register' && mode() !== 'confirm-email' }}
>
<div>
<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'
)}
.&nbsp;
{t('New stories every day and even more!')}
</p>
</div>
<p class={styles.disclaimer}>
{t('By signing up you agree with our')}{' '}
<a
href="/about/terms-of-use"
onClick={() => {
hideModal()
}}
>
{t('terms of use')}
</a>
, {t('personal data usage and email notifications')}.
</p>
</div>
<p class={styles.disclaimer}>
{t('By signing up you agree with our')}{' '}
<a
href="/about/terms-of-use"
onClick={() => {
hideModal()
}}
>
{t('terms of use')}
</a>
, {t('personal data usage and email notifications')}.
</p>
</div>
</div>
<div class={clsx('col-md-12', styles.auth)}>
</div>{' '}
</Show>
<div
class={clsx(styles.auth, {
'col-md-12': !source
})}
>
<Dynamic component={AUTH_MODAL_MODES[mode()]} />
</div>
</div>

View File

@ -22,24 +22,24 @@ type Props = {
scrollToComments?: (value: boolean) => void
}
type HeaderSearchParams = {
source?: string
}
export const Header = (props: Props) => {
const { t } = useLocalize()
// signals
const { modal } = useModalStore()
const { page, searchParams } = useRouter<HeaderSearchParams>()
const [getIsScrollingBottom, setIsScrollingBottom] = createSignal(false)
const [getIsScrolled, setIsScrolled] = createSignal(false)
const [fixed, setFixed] = createSignal(false)
const [isSharePopupVisible, setIsSharePopupVisible] = createSignal(false)
const [isProfilePopupVisible, setIsProfilePopupVisible] = createSignal(false)
const { modal } = useModalStore()
const { page } = useRouter()
// methods
const toggleFixed = () => setFixed((oldFixed) => !oldFixed)
// effects
let windowScrollTop = 0
@ -91,7 +91,7 @@ export const Header = (props: Props) => {
[styles.headerWithTitle]: Boolean(props.title)
}}
>
<Modal variant="wide" name="auth" noPadding={true}>
<Modal variant={searchParams().source ? 'narrow' : 'wide'} name="auth" noPadding={true}>
<AuthModal />
</Modal>

View File

@ -1,8 +1,10 @@
import { createEffect, createSignal, Show } from 'solid-js'
import type { JSX } from 'solid-js'
import { clsx } from 'clsx'
import { hideModal, useModalStore } from '../../../stores/ui'
import { useEscKeyDownHandler } from '../../../utils/useEscKeyDownHandler'
import { clsx } from 'clsx'
import styles from './Modal.module.scss'
interface ModalProps {
@ -16,6 +18,8 @@ interface ModalProps {
export const Modal = (props: ModalProps) => {
const { modal } = useModalStore()
const [visible, setVisible] = createSignal(false)
const handleHide = () => {
if (modal()) {
hideModal()
@ -25,8 +29,6 @@ export const Modal = (props: ModalProps) => {
useEscKeyDownHandler(handleHide)
const [visible, setVisible] = createSignal(false)
createEffect(() => {
setVisible(modal() === props.name)
})

View File

@ -1,4 +1,4 @@
import { createEffect, createSignal, For, Match, Show, Switch, on } from 'solid-js'
import { createEffect, createSignal, For, Show, on } from 'solid-js'
import { MediaItem, UploadedFile } from '../../../pages/types'
import { Icon } from '../Icon'
import { Popover } from '../Popover'

View File

@ -1,11 +1,9 @@
import type { Accessor, JSX } from 'solid-js'
import { createContext, createSignal, useContext, createMemo } from 'solid-js'
import { createSubClient } from '../graphql/privateGraphQLClient'
import { createContext, createSignal, useContext } from 'solid-js'
// import { createSubClient } from '../graphql/privateGraphQLClient'
import type { Chat, Message, MutationCreateMessageArgs } from '../graphql/types.gen'
import { apiClient } from '../utils/apiClient'
import newMessage from '../graphql/subs/new-message'
import type { Client } from '@urql/core'
import { pipe, subscribe } from 'wonka'
// import type { Client } from '@urql/core'
import { loadMessages } from '../stores/inbox'
type InboxContextType = {
@ -29,7 +27,7 @@ export function useInbox() {
export const InboxProvider = (props: { children: JSX.Element }) => {
const [chats, setChats] = createSignal<Chat[]>([])
const [messages, setMessages] = createSignal<Message[]>([])
const subclient = createMemo<Client>(() => createSubClient())
// const subclient = createMemo<Client>(() => createSubClient())
const loadChats = async () => {
try {
const newChats = await apiClient.getChats({ limit: 50, offset: 0 })

View File

@ -64,7 +64,7 @@ export const hideModal = () => {
}
changeSearchParam('modal', null, true)
changeSearchParam('source', null, true)
changeSearchParam('source', null)
setModal(null)
}

View File

@ -1,16 +0,0 @@
import { useRouter } from '../stores/router'
import type { AuthModalSearchParams } from '../components/Nav/AuthModal/types'
import { useLocalize } from '../context/localize'
export const generateModalTitleFromSource = (modalType: 'login' | 'register') => {
const { searchParams } = useRouter<AuthModalSearchParams>()
const { t } = useLocalize()
const { source } = searchParams()
let title = modalType === 'login' ? 'Enter the Discours' : 'Create account'
if (source) title = `${title} from ${source}`
return t(title)
}