From 4da78d2e687f53048abe5694859e09f74102417b Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 16 Oct 2023 18:57:29 +0300 Subject: [PATCH 01/10] Feature/update confirm modal (#264) Update confirm modal --- public/locales/en/translation.json | 5 +- public/locales/ru/translation.json | 3 + src/components/Article/Comment.tsx | 7 ++- src/components/Article/CommentsTree.tsx | 2 +- src/components/Draft/Draft.tsx | 9 ++- src/components/Inbox/Message.tsx | 1 - .../Nav/ConfirmModal/ConfirmModal.module.scss | 60 ++++++------------- .../Nav/ConfirmModal/ConfirmModal.tsx | 27 +++++---- src/components/Nav/Modal/Modal.module.scss | 4 +- src/components/Nav/Modal/Modal.tsx | 3 +- .../NotificationView/NotificationView.tsx | 2 - .../NotificationsPanel/NotificationsPanel.tsx | 2 +- src/components/Views/Author/Author.tsx | 1 - .../_shared/Button/Button.module.scss | 11 ++++ src/components/_shared/Button/Button.tsx | 3 +- src/context/confirm.tsx | 11 +++- 16 files changed, 81 insertions(+), 70 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index d277ae8c..1f5e43b4 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -28,6 +28,8 @@ "All posts": "All posts", "All topics": "All topics", "Almost done! Check your email.": "Almost done! Just checking your email.", + "Are you sure you want to delete this comment?": "Are you sure you want to delete this comment?", + "Are you sure you want to delete this draft?": "Are you sure you want to delete this draft?", "Are you sure you want to to proceed the action?": "Are you sure you want to to proceed the action?", "Art": "Art", "Artist": "Artist", @@ -100,6 +102,7 @@ "Discussion rules": "Discussion rules", "Discussions": "Discussions", "Dogma": "Dogma", + "Draft successfully deleted": "Draft successfully deleted", "Drafts": "Drafts", "Drag the image to this area": "Drag the image to this area", "Each image must be no larger than 5 MB.": "Each image must be no larger than 5 MB.", @@ -193,6 +196,7 @@ "Manifesto": "Manifesto", "Many files, choose only one": "Many files, choose only one", "Material card": "Material card", + "Message": "Message", "More": "More", "Most commented": "Commented", "Most read": "Readable", @@ -353,7 +357,6 @@ "Where": "From", "Words": "Слов", "Work with us": "Cooperate with Discourse", - "Message": "Message", "Write a comment...": "Write a comment...", "Write a short introduction": "Write a short introduction", "Write about the topic": "Write about the topic", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index 2a983fa3..cb2559b3 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -31,6 +31,8 @@ "All posts": "Все публикации", "All topics": "Все темы", "Almost done! Check your email.": "Почти готово! Осталось подтвердить вашу почту.", + "Are you sure you want to delete this comment?": "Уверены, что хотите удалить этот комментарий?", + "Are you sure you want to delete this draft?": "Уверены, что хотите удалить этот черновик?", "Are you sure you want to to proceed the action?": "Вы уверены, что хотите продолжить?", "Art": "Искусство", "Artist": "Исполнитель", @@ -104,6 +106,7 @@ "Discussion rules": "Правила сообществ самиздата в соцсетях", "Discussions": "Дискуссии", "Dogma": "Догма", + "Draft successfully deleted": "Черновик успешно удален", "Drafts": "Черновики", "Drag the image to this area": "Перетащите изображение в эту область", "Each image must be no larger than 5 MB.": "Каждое изображение должно быть размером не больше 5 мб.", diff --git a/src/components/Article/Comment.tsx b/src/components/Article/Comment.tsx index 35788445..823a59f7 100644 --- a/src/components/Article/Comment.tsx +++ b/src/components/Article/Comment.tsx @@ -62,7 +62,12 @@ export const Comment = (props: Props) => { const remove = async () => { if (comment()?.id) { try { - const isConfirmed = await showConfirm() + const isConfirmed = await showConfirm({ + confirmBody: t('Are you sure you want to delete this comment?'), + confirmButtonLabel: t('Delete'), + confirmButtonVariant: 'danger', + declineButtonVariant: 'primary' + }) if (isConfirmed) { await deleteReaction(comment().id) diff --git a/src/components/Article/CommentsTree.tsx b/src/components/Article/CommentsTree.tsx index 0122f626..96340312 100644 --- a/src/components/Article/CommentsTree.tsx +++ b/src/components/Article/CommentsTree.tsx @@ -1,4 +1,4 @@ -import { Show, createMemo, createSignal, onMount, For, createEffect } from 'solid-js' +import { Show, createMemo, createSignal, onMount, For } from 'solid-js' import { Comment } from './Comment' import styles from './Article.module.scss' import { clsx } from 'clsx' diff --git a/src/components/Draft/Draft.tsx b/src/components/Draft/Draft.tsx index f5095a65..7ee25154 100644 --- a/src/components/Draft/Draft.tsx +++ b/src/components/Draft/Draft.tsx @@ -35,11 +35,16 @@ export const Draft = (props: Props) => { const handleDeleteLinkClick = async (e) => { e.preventDefault() - const isConfirmed = await showConfirm() + const isConfirmed = await showConfirm({ + confirmBody: t('Are you sure you want to delete this draft?'), + confirmButtonLabel: t('Delete'), + confirmButtonVariant: 'danger', + declineButtonVariant: 'primary' + }) if (isConfirmed) { props.onDelete(props.shout) - await showSnackbar({ type: 'success', body: t('Success') }) + await showSnackbar({ body: t('Draft successfully deleted') }) } } diff --git a/src/components/Inbox/Message.tsx b/src/components/Inbox/Message.tsx index bdcf0b9d..9a345cf4 100644 --- a/src/components/Inbox/Message.tsx +++ b/src/components/Inbox/Message.tsx @@ -7,7 +7,6 @@ import formattedTime from '../../utils/formatDateTime' import { Icon } from '../_shared/Icon' import { MessageActionsPopup } from './MessageActionsPopup' import QuotedMessage from './QuotedMessage' -import MD from '../Article/MD' type Props = { content: MessageType diff --git a/src/components/Nav/ConfirmModal/ConfirmModal.module.scss b/src/components/Nav/ConfirmModal/ConfirmModal.module.scss index deb8f946..b955ec59 100644 --- a/src/components/Nav/ConfirmModal/ConfirmModal.module.scss +++ b/src/components/Nav/ConfirmModal/ConfirmModal.module.scss @@ -1,48 +1,22 @@ .confirmModal { - background: #fff; - min-height: 550px; position: relative; - @include media-breakpoint-up(md) { - min-height: 710px; - } -} - -.confirmModalTitle { - font-size: 26px; - line-height: 32px; - font-weight: 700; - color: #141414; - text-align: left; -} - -.confirmModalActions { - display: flex; - justify-content: space-between; - margin-top: 16px; -} - -.confirmModalButton { - display: block; - width: 100%; - margin-right: 12px; - font-weight: 700; - margin-top: 32px; - padding: 1.6rem !important; - border: 1px solid black; - - &:hover { - background-color: rgb(0 0 0 / 8%); - } -} - -.confirmModalButtonPrimary { - margin-right: 0; - background-color: black; - color: white; - border: none; - - &:hover { - background-color: rgb(0 0 0 / 60%); + .confirmModalTitle { + @include font-size(2rem); + + font-weight: 700; + color: var(--default-color); + text-align: center; + } + + .confirmModalActions { + display: flex; + justify-content: space-between; + margin-top: 4rem; + gap: 2rem; + + .confirmAction { + flex: 1; + } } } diff --git a/src/components/Nav/ConfirmModal/ConfirmModal.tsx b/src/components/Nav/ConfirmModal/ConfirmModal.tsx index b224f0f0..63f6a767 100644 --- a/src/components/Nav/ConfirmModal/ConfirmModal.tsx +++ b/src/components/Nav/ConfirmModal/ConfirmModal.tsx @@ -1,7 +1,7 @@ -import { clsx } from 'clsx' import { useConfirm } from '../../../context/confirm' -import styles from './ConfirmModal.module.scss' import { useLocalize } from '../../../context/localize' +import { Button } from '../../_shared/Button' +import styles from './ConfirmModal.module.scss' export const ConfirmModal = () => { const { t } = useLocalize() @@ -12,21 +12,26 @@ export const ConfirmModal = () => { } = useConfirm() return ( -
+

{confirmMessage().confirmBody ?? t('Are you sure you want to to proceed the action?')}

- - + value={confirmMessage().confirmButtonLabel ?? t('Confirm')} + size="L" + variant={confirmMessage().confirmButtonVariant ?? 'primary'} + class={styles.confirmAction} + />
) diff --git a/src/components/Nav/Modal/Modal.module.scss b/src/components/Nav/Modal/Modal.module.scss index ebc49a08..f69f99c2 100644 --- a/src/components/Nav/Modal/Modal.module.scss +++ b/src/components/Nav/Modal/Modal.module.scss @@ -71,8 +71,8 @@ } .close { - right: 3.6rem; - top: 12px; + right: 1.6rem; + top: 1.6rem; } } } diff --git a/src/components/Nav/Modal/Modal.tsx b/src/components/Nav/Modal/Modal.tsx index 929c7ab1..0b6e1415 100644 --- a/src/components/Nav/Modal/Modal.tsx +++ b/src/components/Nav/Modal/Modal.tsx @@ -1,4 +1,4 @@ -import { createEffect, createMemo, createSignal, on, Show } from 'solid-js' +import { createEffect, createMemo, createSignal, Show } from 'solid-js' import type { JSX } from 'solid-js' import { clsx } from 'clsx' import { hideModal, useModalStore } from '../../../stores/ui' @@ -8,7 +8,6 @@ import styles from './Modal.module.scss' import { redirectPage } from '@nanostores/router' import { router } from '../../../stores/router' import { Icon } from '../../_shared/Icon' -import { resetSortedArticles } from '../../../stores/zine/articles' interface Props { name: string diff --git a/src/components/NotificationsPanel/NotificationView/NotificationView.tsx b/src/components/NotificationsPanel/NotificationView/NotificationView.tsx index 397572fb..079d3a73 100644 --- a/src/components/NotificationsPanel/NotificationView/NotificationView.tsx +++ b/src/components/NotificationsPanel/NotificationView/NotificationView.tsx @@ -1,7 +1,6 @@ import { clsx } from 'clsx' import styles from './NotificationView.module.scss' import type { Notification } from '../../../graphql/types.gen' -import { formatDate } from '../../../utils' import { createMemo, createSignal, onMount, Show } from 'solid-js' import { NotificationType } from '../../../graphql/types.gen' import { openPage } from '@nanostores/router' @@ -9,7 +8,6 @@ import { router } from '../../../stores/router' import { useNotifications } from '../../../context/notifications' import { Userpic } from '../../Author/Userpic' import { useLocalize } from '../../../context/localize' -import notifications from '../../../graphql/query/notifications' type Props = { notification: Notification diff --git a/src/components/NotificationsPanel/NotificationsPanel.tsx b/src/components/NotificationsPanel/NotificationsPanel.tsx index ddfc66b2..4041dbed 100644 --- a/src/components/NotificationsPanel/NotificationsPanel.tsx +++ b/src/components/NotificationsPanel/NotificationsPanel.tsx @@ -4,7 +4,7 @@ import { useEscKeyDownHandler } from '../../utils/useEscKeyDownHandler' import { useOutsideClickHandler } from '../../utils/useOutsideClickHandler' import { useLocalize } from '../../context/localize' import { Icon } from '../_shared/Icon' -import { createEffect, For, onCleanup, onMount } from 'solid-js' +import { createEffect, For } from 'solid-js' import { useNotifications } from '../../context/notifications' import { NotificationView } from './NotificationView' diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index d9ff4217..2eafec93 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -16,7 +16,6 @@ import { apiClient } from '../../../utils/apiClient' import { Comment } from '../../Article/Comment' import { useLocalize } from '../../../context/localize' import { AuthorRatingControl } from '../../Author/AuthorRatingControl' -import { hideModal } from '../../../stores/ui' import { getPagePath } from '@nanostores/router' import { useSession } from '../../../context/session' import { Loading } from '../../_shared/Loading' diff --git a/src/components/_shared/Button/Button.module.scss b/src/components/_shared/Button/Button.module.scss index 5a6c786b..cb20f387 100644 --- a/src/components/_shared/Button/Button.module.scss +++ b/src/components/_shared/Button/Button.module.scss @@ -31,6 +31,17 @@ } } + &.danger { + border: 3px solid var(--danger-color); + background: var(--background-color); + color: var(--danger-color); + + &:hover { + background: var(--danger-color); + color: #fff; + } + } + &.inline { font-weight: 700; font-size: 16px; diff --git a/src/components/_shared/Button/Button.tsx b/src/components/_shared/Button/Button.tsx index 42d7d5ec..e892ef5e 100644 --- a/src/components/_shared/Button/Button.tsx +++ b/src/components/_shared/Button/Button.tsx @@ -2,10 +2,11 @@ import type { JSX } from 'solid-js' import { clsx } from 'clsx' import styles from './Button.module.scss' +export type ButtonVariant = 'primary' | 'secondary' | 'bordered' | 'inline' | 'light' | 'outline' | 'danger' type Props = { value: string | JSX.Element size?: 'S' | 'M' | 'L' - variant?: 'primary' | 'secondary' | 'bordered' | 'inline' | 'light' | 'outline' + variant?: ButtonVariant type?: 'submit' | 'button' loading?: boolean disabled?: boolean diff --git a/src/context/confirm.tsx b/src/context/confirm.tsx index 61c6875a..a4a4f745 100644 --- a/src/context/confirm.tsx +++ b/src/context/confirm.tsx @@ -2,11 +2,14 @@ import { createContext, createSignal, useContext } from 'solid-js' import type { Accessor, JSX } from 'solid-js' import { hideModal, showModal } from '../stores/ui' +import { ButtonVariant } from '../components/_shared/Button/Button' type ConfirmMessage = { confirmBody?: string | JSX.Element confirmButtonLabel?: string + confirmButtonVariant?: ButtonVariant declineButtonLabel?: string + declineButtonVariant?: ButtonVariant } type ConfirmContextType = { @@ -15,7 +18,9 @@ type ConfirmContextType = { showConfirm: (message?: { confirmBody?: ConfirmMessage['confirmBody'] confirmButtonLabel?: ConfirmMessage['confirmButtonLabel'] + confirmButtonVariant?: ConfirmMessage['confirmButtonVariant'] declineButtonLabel?: ConfirmMessage['declineButtonLabel'] + declineButtonVariant?: ConfirmMessage['declineButtonVariant'] }) => Promise resolveConfirm: (value: boolean) => void } @@ -36,13 +41,17 @@ export const ConfirmProvider = (props: { children: JSX.Element }) => { message: { confirmBody?: ConfirmMessage['confirmBody'] confirmButtonLabel?: ConfirmMessage['confirmButtonLabel'] + confirmButtonVariant?: ConfirmMessage['confirmButtonVariant'] declineButtonLabel?: ConfirmMessage['declineButtonLabel'] + declineButtonVariant?: ConfirmMessage['declineButtonVariant'] } = {} ): Promise => { const messageToShow = { confirmBody: message.confirmBody, confirmButtonLabel: message.confirmButtonLabel, - declineButtonLabel: message.declineButtonLabel + confirmButtonVariant: message.confirmButtonVariant, + declineButtonLabel: message.declineButtonLabel, + declineButtonVariant: message.declineButtonVariant } setConfirmMessage(messageToShow) From 9262367f688d31b92f1ea9f5b6573bf18e45b570 Mon Sep 17 00:00:00 2001 From: Ilya Y <75578537+ilya-bkv@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:24:33 +0300 Subject: [PATCH 02/10] notification system update (#265) * notification system update Co-authored-by: Igor Lobanov --- public/locales/en/translation.json | 15 +++- public/locales/ru/translation.json | 14 +++- src/components/Article/Comment.tsx | 4 +- src/components/Article/CommentsTree.tsx | 2 +- src/components/Article/FullArticle.tsx | 31 +++++-- src/components/Nav/Header/Header.tsx | 2 +- .../EmptyMessage/EmptyMessage.module.scss | 12 +++ .../EmptyMessage/EmptyMessage.tsx | 14 ++++ .../NotificationsPanel/EmptyMessage/index.ts | 1 + .../NotificationView.module.scss | 7 ++ .../NotificationView/NotificationView.tsx | 83 +++++++++++++------ .../NotificationsPanel/NotificationsPanel.tsx | 20 ++++- src/context/notifications.tsx | 7 +- src/pages/article.page.tsx | 13 +-- 14 files changed, 162 insertions(+), 63 deletions(-) create mode 100644 src/components/NotificationsPanel/EmptyMessage/EmptyMessage.module.scss create mode 100644 src/components/NotificationsPanel/EmptyMessage/EmptyMessage.tsx create mode 100644 src/components/NotificationsPanel/EmptyMessage/index.ts diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 1f5e43b4..577ddfcf 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -210,12 +210,19 @@ "New only": "New only", "New password": "New password", "New stories every day and even more!": "New stories and more are waiting for you every day!", - "NewCommentNotificationText": "{commentsCount, plural, one {New comment} other {{commentsCount} comments}} to your publication {shoutTitle} from {lastCommenterName}{restUsersCount, plural, =0 {} one { one more user} other { and more {restUsersCount} users}}", - "NewReplyNotificationText": "{commentsCount, plural, one {New reply} other {{commentsCount} replays} other {{commentsCount} новых ответов}} to your publication {shoutTitle} от {lastCommenterName}{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}", + + "NotificationNewCommentText1": "{commentsCount, plural, one {New comment} other {{commentsCount} comments}} to your publication", + "NotificationNewCommentText2": "from", + "NotificationNewCommentText3": "{restUsersCount, plural, =0 {} one { one more user} other { and more {restUsersCount} users}}", + + "NotificationNewReplyText1": "{commentsCount, plural, one {New reply} other {{commentsCount} replays}} to your publication", + "NotificationNewReplyText2": "from", + "NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}", + "Newsletter": "Newsletter", "Night mode": "Night mode", - "No notifications, yet": "No notifications, yet", - "No such account, please try to register": "No such account found, please try to register", + "No notifications yet": "No notifications yet", + "Write good articles, comment\nand it won't be so empty here": "Write good articles, comment\nand it won't be so empty here", "Nothing here yet": "There's nothing here yet", "Nothing is here": "There is nothing here", "Notifications": "Notifications", diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index cb2559b3..a263456e 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -220,11 +220,19 @@ "New only": "Только новые", "New password": "Новый пароль", "New stories every day and even more!": "Каждый день вас ждут новые истории и ещё много всего интересного!", - "NewCommentNotificationText": "{commentsCount, plural, one {Новый комментарий} few {{commentsCount} новых комментария} other {{commentsCount} новых комментариев}} к вашей публикации {shoutTitle} от {lastCommenterName}{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}", - "NewReplyNotificationText": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} к вашему комментарию к публикации {shoutTitle} от {lastCommenterName}{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}", + + "NotificationNewCommentText1": "{commentsCount, plural, one {Новый комментарий} few {{commentsCount} новых комментария} other {{commentsCount} новых комментариев}} к вашей публикации", + "NotificationNewCommentText2": "от", + "NotificationNewCommentText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}", + + "NotificationNewReplyText1": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} к вашему комментарию к публикации", + "NotificationNewReplyText2": "от", + "NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}", + "Newsletter": "Рассылка", "Night mode": "Ночная тема", - "No notifications, yet": "Тут пока пусто", + "No notifications yet": "Уведомлений пока нет", + "Write good articles, comment\nand it won't be so empty here": "Пишите хорошие статьи, комментируйте,\nи здесь станет не так пусто", "No such account, please try to register": "Такой адрес не найден, попробуйте зарегистрироваться", "Nothing here yet": "Здесь пока ничего нет", "Nothing is here": "Здесь ничего нет", diff --git a/src/components/Article/Comment.tsx b/src/components/Article/Comment.tsx index 823a59f7..3405b49b 100644 --- a/src/components/Article/Comment.tsx +++ b/src/components/Article/Comment.tsx @@ -141,7 +141,7 @@ export const Comment = (props: Props) => { })} /> - {comment()?.shout.title || ''} + {comment()?.shout.title || ''}
} @@ -179,7 +179,7 @@ export const Comment = (props: Props) => { -
+
}> {t('Loading')}

}> { handleSubmitComment(value)} diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 7d68604b..705fb083 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -28,11 +28,24 @@ import styles from './Article.module.scss' import { CardTopic } from '../Feed/CardTopic' import { createPopper } from '@popperjs/core' -interface Props { +type Props = { article: Shout scrollToComments?: boolean } +export type ArticlePageSearchParams = { + scrollTo: 'comments' + commentId: string +} + +const scrollTo = (el: HTMLElement) => { + window.scrollTo({ + top: el.offsetTop - 96, + left: 0, + behavior: 'smooth' + }) +} + export const FullArticle = (props: Props) => { const { t } = useLocalize() const { @@ -78,15 +91,12 @@ export const FullArticle = (props: Props) => { }) const commentsRef: { current: HTMLDivElement } = { current: null } + const scrollToComments = () => { - window.scrollTo({ - top: commentsRef.current.offsetTop - 96, - left: 0, - behavior: 'smooth' - }) + scrollTo(commentsRef.current) } - const { searchParams, changeSearchParam } = useRouter() + const { searchParams, changeSearchParam } = useRouter() createEffect(() => { if (props.scrollToComments) { @@ -105,9 +115,12 @@ export const FullArticle = (props: Props) => { createEffect(() => { if (searchParams().commentId && isReactionsLoaded()) { - const commentElement = document.querySelector(`[id='comment_${searchParams().commentId}']`) + const commentElement = document.querySelector( + `[id='comment_${searchParams().commentId}']` + ) + changeSearchParam({ commentId: null }) if (commentElement) { - commentElement.scrollIntoView({ behavior: 'smooth' }) + scrollTo(commentElement) } } }) diff --git a/src/components/Nav/Header/Header.tsx b/src/components/Nav/Header/Header.tsx index 4ce186bd..855086ac 100644 --- a/src/components/Nav/Header/Header.tsx +++ b/src/components/Nav/Header/Header.tsx @@ -67,7 +67,7 @@ export const Header = (props: Props) => { let windowScrollTop = 0 createEffect(() => { - const mainContent = document.querySelector('.main-content') as HTMLDivElement + const mainContent = document.querySelector('.main-content') if (fixed() || modal() !== null) { windowScrollTop = window.scrollY diff --git a/src/components/NotificationsPanel/EmptyMessage/EmptyMessage.module.scss b/src/components/NotificationsPanel/EmptyMessage/EmptyMessage.module.scss new file mode 100644 index 00000000..b1484a0d --- /dev/null +++ b/src/components/NotificationsPanel/EmptyMessage/EmptyMessage.module.scss @@ -0,0 +1,12 @@ +.EmptyMessage { + // TODO: check markup + color: var(--black-500); + text-align: center; + font-size: 15px; + line-height: 24px; + white-space: pre-line; +} + +.title { + font-weight: 500; +} diff --git a/src/components/NotificationsPanel/EmptyMessage/EmptyMessage.tsx b/src/components/NotificationsPanel/EmptyMessage/EmptyMessage.tsx new file mode 100644 index 00000000..855009ff --- /dev/null +++ b/src/components/NotificationsPanel/EmptyMessage/EmptyMessage.tsx @@ -0,0 +1,14 @@ +import { clsx } from 'clsx' +import styles from './EmptyMessage.module.scss' +import { useLocalize } from '../../../context/localize' + +export const EmptyMessage = () => { + const { t } = useLocalize() + + return ( +
+
{t('No notifications yet')}
+
{t("Write good articles, comment\nand it won't be so empty here")}
+
+ ) +} diff --git a/src/components/NotificationsPanel/EmptyMessage/index.ts b/src/components/NotificationsPanel/EmptyMessage/index.ts new file mode 100644 index 00000000..f26b9a9c --- /dev/null +++ b/src/components/NotificationsPanel/EmptyMessage/index.ts @@ -0,0 +1 @@ +export { EmptyMessage } from './EmptyMessage' diff --git a/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss b/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss index 284af187..26401acd 100644 --- a/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss +++ b/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss @@ -20,6 +20,13 @@ &:hover { background-color: var(--gray-100); } + + a, + a:visited { + padding-bottom: 0 !important; + border-bottom: none !important; + font-weight: 700; + } } .userpic { diff --git a/src/components/NotificationsPanel/NotificationView/NotificationView.tsx b/src/components/NotificationsPanel/NotificationView/NotificationView.tsx index 079d3a73..00cfa019 100644 --- a/src/components/NotificationsPanel/NotificationView/NotificationView.tsx +++ b/src/components/NotificationsPanel/NotificationView/NotificationView.tsx @@ -3,11 +3,12 @@ import styles from './NotificationView.module.scss' import type { Notification } from '../../../graphql/types.gen' import { createMemo, createSignal, onMount, Show } from 'solid-js' import { NotificationType } from '../../../graphql/types.gen' -import { openPage } from '@nanostores/router' -import { router } from '../../../stores/router' +import { getPagePath, openPage } from '@nanostores/router' +import { router, useRouter } from '../../../stores/router' import { useNotifications } from '../../../context/notifications' import { Userpic } from '../../Author/Userpic' import { useLocalize } from '../../../context/localize' +import type { ArticlePageSearchParams } from '../../Article/FullArticle' type Props = { notification: Notification @@ -26,13 +27,16 @@ type NotificationData = { slug: string userpic: string }[] + reactionIds: number[] } export const NotificationView = (props: Props) => { const { - actions: { markNotificationAsRead } + actions: { markNotificationAsRead, hideNotificationsPanel } } = useNotifications() + const { changeSearchParam } = useRouter() + const { t } = useLocalize() const [data, setData] = createSignal(null) @@ -49,6 +53,11 @@ export const NotificationView = (props: Props) => { return data().users[data().users.length - 1] }) + const handleLinkClick = (event: MouseEvent) => { + event.stopPropagation() + hideNotificationsPanel() + } + const content = createMemo(() => { if (!data()) { return null @@ -64,47 +73,67 @@ export const NotificationView = (props: Props) => { } if (shoutTitle.length < data().shout.title.length) { - shoutTitle += '...' + shoutTitle = `${shoutTitle.trim()}...` + + if (shoutTitle[0] === '«') { + shoutTitle += '»' + } } switch (props.notification.type) { case NotificationType.NewComment: { - return t('NewCommentNotificationText', { - commentsCount: props.notification.occurrences, - shoutTitle, - lastCommenterName: lastUser().name, - restUsersCount: data().users.length - 1 - }) + return ( + <> + {t('NotificationNewCommentText1', { + commentsCount: props.notification.occurrences + })}{' '} + + {shoutTitle} + {' '} + {t('NotificationNewCommentText2')}{' '} + + {lastUser().name} + {' '} + {t('NotificationNewCommentText3', { + restUsersCount: data().users.length - 1 + })} + + ) } case NotificationType.NewReply: { - return t('NewReplyNotificationText', { - commentsCount: props.notification.occurrences, - shoutTitle, - lastCommenterName: lastUser().name, - restUsersCount: data().users.length - 1 - }) + return ( + <> + {t('NotificationNewReplyText1', { + commentsCount: props.notification.occurrences + })}{' '} + + {shoutTitle} + {' '} + {t('NotificationNewReplyText2')}{' '} + + {lastUser().name} + {' '} + {t('NotificationNewReplyText3', { + restUsersCount: data().users.length - 1 + })} + + ) } } }) const handleClick = () => { + props.onClick() + if (!props.notification.seen) { markNotificationAsRead(props.notification) } openPage(router, 'article', { slug: data().shout.slug }) - props.onClick() - // switch (props.notification.type) { - // case NotificationType.NewComment: { - // openPage(router, 'article', { slug: data().shout.slug }) - // break - // } - // case NotificationType.NewReply: { - // openPage(router, 'article', { slug: data().shout.slug }) - // break - // } - // } + if (data().reactionIds) { + changeSearchParam({ commentId: data().reactionIds[0].toString() }) + } } return ( diff --git a/src/components/NotificationsPanel/NotificationsPanel.tsx b/src/components/NotificationsPanel/NotificationsPanel.tsx index 4041dbed..c47e770f 100644 --- a/src/components/NotificationsPanel/NotificationsPanel.tsx +++ b/src/components/NotificationsPanel/NotificationsPanel.tsx @@ -7,6 +7,7 @@ import { Icon } from '../_shared/Icon' import { createEffect, For } from 'solid-js' import { useNotifications } from '../../context/notifications' import { NotificationView } from './NotificationView' +import { EmptyMessage } from './EmptyMessage' type Props = { isOpen: boolean @@ -30,8 +31,22 @@ export const NotificationsPanel = (props: Props) => { handler: () => handleHide() }) + let windowScrollTop = 0 + createEffect(() => { + const mainContent = document.querySelector('.main-content') + + if (props.isOpen) { + windowScrollTop = window.scrollY + mainContent.style.marginTop = `-${windowScrollTop}px` + } + document.body.classList.toggle('fixed', props.isOpen) + + if (!props.isOpen) { + mainContent.style.marginTop = '' + window.scrollTo(0, windowScrollTop) + } }) useEscKeyDownHandler(handleHide) @@ -52,10 +67,7 @@ export const NotificationsPanel = (props: Props) => {
{t('Notifications')}
- {t('No notifications, yet')}
} - > + }> {(notification) => ( actions: { showNotificationsPanel: () => void + hideNotificationsPanel: () => void markNotificationAsRead: (notification: Notification) => Promise } } @@ -80,7 +81,11 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => { setIsNotificationsPanelOpen(true) } - const actions = { showNotificationsPanel, markNotificationAsRead } + const hideNotificationsPanel = () => { + setIsNotificationsPanelOpen(false) + } + + const actions = { showNotificationsPanel, hideNotificationsPanel, markNotificationAsRead } const value: NotificationsContextType = { notificationEntities, diff --git a/src/pages/article.page.tsx b/src/pages/article.page.tsx index c522b76d..c5ed9aac 100644 --- a/src/pages/article.page.tsx +++ b/src/pages/article.page.tsx @@ -11,18 +11,9 @@ import { setPageLoadManagerPromise } from '../utils/pageLoadManager' export const ArticlePage = (props: PageProps) => { const shouts = props.article ? [props.article] : [] + const { page } = useRouter() - const slug = createMemo(() => { - const { page: getPage } = useRouter() - - const page = getPage() - - if (page.route !== 'article') { - throw new Error('ts guard') - } - - return page.params.slug - }) + const slug = createMemo(() => page().params['slug'] as string) const { articleEntities } = useArticlesStore({ shouts From 51762e3dee1843532ee210fde9dbde535dd19dde Mon Sep 17 00:00:00 2001 From: Igor Lobanov Date: Mon, 16 Oct 2023 21:50:22 +0200 Subject: [PATCH 03/10] notifications fixes --- public/locales/ru/translation.json | 2 +- src/components/Article/FullArticle.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/public/locales/ru/translation.json b/public/locales/ru/translation.json index a263456e..b8f5d551 100644 --- a/public/locales/ru/translation.json +++ b/public/locales/ru/translation.json @@ -225,7 +225,7 @@ "NotificationNewCommentText2": "от", "NotificationNewCommentText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}", - "NotificationNewReplyText1": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} к вашему комментарию к публикации", + "NotificationNewReplyText1": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} на ваш комментарий к публикации", "NotificationNewReplyText2": "от", "NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}", diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index 705fb083..7647b2b1 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -39,8 +39,10 @@ export type ArticlePageSearchParams = { } const scrollTo = (el: HTMLElement) => { + const { top } = el.getBoundingClientRect() + window.scrollTo({ - top: el.offsetTop - 96, + top: top + window.scrollY - 96, left: 0, behavior: 'smooth' }) @@ -90,7 +92,9 @@ export const FullArticle = (props: Props) => { return JSON.parse(props.article.media || '[]') }) - const commentsRef: { current: HTMLDivElement } = { current: null } + const commentsRef: { + current: HTMLDivElement + } = { current: null } const scrollToComments = () => { scrollTo(commentsRef.current) @@ -118,7 +122,9 @@ export const FullArticle = (props: Props) => { const commentElement = document.querySelector( `[id='comment_${searchParams().commentId}']` ) + changeSearchParam({ commentId: null }) + if (commentElement) { scrollTo(commentElement) } From e0ae47bd5e83773d58985351f1ec460e8326d759 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Mon, 16 Oct 2023 23:11:08 +0300 Subject: [PATCH 04/10] Mobile style fixes --- public/icons/key.svg | 4 ++ .../Feed/Sidebar/Sidebar.module.scss | 2 + src/components/Nav/Header/Header.module.scss | 53 +++++++++++++------ src/components/Nav/Header/Header.tsx | 13 +++-- src/components/Nav/HeaderAuth.tsx | 4 +- src/components/Nav/Topics/Topics.module.scss | 43 +++++++++++++-- src/components/Views/Feed.module.scss | 3 +- src/styles/app.scss | 12 +++-- 8 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 public/icons/key.svg diff --git a/public/icons/key.svg b/public/icons/key.svg new file mode 100644 index 00000000..11d788ca --- /dev/null +++ b/public/icons/key.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/components/Feed/Sidebar/Sidebar.module.scss b/src/components/Feed/Sidebar/Sidebar.module.scss index c09de070..5735b053 100644 --- a/src/components/Feed/Sidebar/Sidebar.module.scss +++ b/src/components/Feed/Sidebar/Sidebar.module.scss @@ -1,9 +1,11 @@ .sidebar { + margin-top: -0.7rem; max-height: calc(100vh - 120px); overflow: auto; top: 120px; @include media-breakpoint-up(md) { + margin-top: 0; position: sticky; ul > li { diff --git a/src/components/Nav/Header/Header.module.scss b/src/components/Nav/Header/Header.module.scss index 9d6fc432..5dffec89 100644 --- a/src/components/Nav/Header/Header.module.scss +++ b/src/components/Nav/Header/Header.module.scss @@ -104,9 +104,9 @@ position: relative; @include media-breakpoint-down(lg) { - flex: 1 !important; max-width: 100% !important; - padding: 0 !important; + position: absolute; + right: 0; } } @@ -139,7 +139,7 @@ overflow: auto; padding: $container-padding-x !important; position: fixed; - top: 64px; + top: 58px; width: 100%; z-index: 1; @@ -191,8 +191,9 @@ } } - ul { + :global(.view-switcher) { margin-top: 0; + overflow: hidden; } li { @@ -217,6 +218,10 @@ .fixed & { display: block; } + + a { + padding-top: 0.1em; + } } .mainNavigationSocial a { @@ -246,6 +251,30 @@ background: #f7f7f8; border: none; border-radius: 1.6rem; + padding-right: 5.6rem; + + &:not(:placeholder-shown) { + & ~ .mobileSubscriptionSubmit { + display: block; + } + } + } +} + +.mobileSubscriptionSubmit { + aspect-ratio: 1/1; + display: none; + height: 100%; + position: absolute; + right: 0; + top: 0; + + img { + aspect-ratio: 1/1; + left: 50%; + position: relative; + transform: translateX(-50%); + width: 16px !important; } } @@ -387,19 +416,11 @@ display: flex; justify-content: flex-end; position: absolute; - right: 5rem; + right: 0; top: 50%; transform: translateY(-50%); width: 100%; - @include media-breakpoint-up(lg) { - right: 0; - } - - @include media-breakpoint-up(xl) { - right: 2rem; - } - .control { cursor: pointer; border: 0; @@ -446,10 +467,6 @@ z-index: -1; } - @include media-breakpoint-down(md) { - padding: divide($container-padding-x, 2); - } - .userpic { align-items: center; margin-right: 0; @@ -534,6 +551,8 @@ } &:global(.loginbtn) { + background: #e9e9ee; + .icon { height: 2.4rem; width: 2.4rem; diff --git a/src/components/Nav/Header/Header.tsx b/src/components/Nav/Header/Header.tsx index 855086ac..23cc3435 100644 --- a/src/components/Nav/Header/Header.tsx +++ b/src/components/Nav/Header/Header.tsx @@ -173,6 +173,11 @@ export const Header = (props: Props) => {