From ea9d440cdf6c28e3426864fe984890075bdbdd35 Mon Sep 17 00:00:00 2001 From: ilya-bkv Date: Sun, 27 Nov 2022 08:49:48 +0300 Subject: [PATCH] Invite users modal --- public/icons/cross.svg | 3 + public/icons/plus-button.svg | 4 + public/icons/plus.svg | 3 + .../Inbox/CreateModalContent.module.scss | 9 ++ src/components/Inbox/CreateModalContent.tsx | 112 ++++++++++++++++++ src/components/Inbox/DialogCard.tsx | 12 +- src/components/Inbox/InviteUser.module.scss | 35 ++++++ src/components/Inbox/InviteUser.tsx | 22 ++++ src/components/Inbox/Search.module.scss | 4 +- src/components/Nav/Header.tsx | 2 +- .../Nav/{Modal.scss => Modal.module.scss} | 36 +++--- src/components/Nav/Modal.tsx | 20 +++- src/components/Pages/about/ManifestPage.tsx | 4 +- src/components/Views/Inbox.tsx | 25 +++- src/context/inbox.tsx | 7 +- src/locales/ru.json | 9 +- src/stores/ui.ts | 5 +- src/styles/Inbox.scss | 10 +- src/styles/app.scss | 2 + 19 files changed, 272 insertions(+), 52 deletions(-) create mode 100644 public/icons/cross.svg create mode 100644 public/icons/plus-button.svg create mode 100644 public/icons/plus.svg create mode 100644 src/components/Inbox/CreateModalContent.module.scss create mode 100644 src/components/Inbox/CreateModalContent.tsx create mode 100644 src/components/Inbox/InviteUser.module.scss create mode 100644 src/components/Inbox/InviteUser.tsx rename src/components/Nav/{Modal.scss => Modal.module.scss} (75%) diff --git a/public/icons/cross.svg b/public/icons/cross.svg new file mode 100644 index 00000000..0ded326d --- /dev/null +++ b/public/icons/cross.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/plus-button.svg b/public/icons/plus-button.svg new file mode 100644 index 00000000..90a81b02 --- /dev/null +++ b/public/icons/plus-button.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/plus.svg b/public/icons/plus.svg new file mode 100644 index 00000000..d9f84d23 --- /dev/null +++ b/public/icons/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Inbox/CreateModalContent.module.scss b/src/components/Inbox/CreateModalContent.module.scss new file mode 100644 index 00000000..fc1b2f1b --- /dev/null +++ b/src/components/Inbox/CreateModalContent.module.scss @@ -0,0 +1,9 @@ +.CreateModalContent { + padding: 24px; + .footer { + padding-top: 12px; + display: flex; + justify-content: space-around; + gap: 12px; + } +} diff --git a/src/components/Inbox/CreateModalContent.tsx b/src/components/Inbox/CreateModalContent.tsx new file mode 100644 index 00000000..c1f927c8 --- /dev/null +++ b/src/components/Inbox/CreateModalContent.tsx @@ -0,0 +1,112 @@ +import { createSignal, For, createEffect } from 'solid-js' +import styles from './CreateModalContent.module.scss' +import { t } from '../../utils/intl' +import InviteUser from './InviteUser' +import type { Author } from '../../graphql/types.gen' +import { hideModal } from '../../stores/ui' +import { useInbox } from '../../context/inbox' + +type inviteUser = Author & { selected: boolean } +type query = + | { + theme: string + members: string[] + } + | undefined +type Props = { + users: Author[] +} + +const CreateModalContent = (props: Props) => { + const inviteUsers: inviteUser[] = props.users.map((user) => ({ ...user, selected: false })) + const [theme, setTheme] = createSignal('') + const [slugs, setSlugs] = createSignal([]) + const [collectionToInvite, setCollectionToInvite] = createSignal(inviteUsers) + let textInput: HTMLInputElement + + const reset = () => { + setTheme('') + setSlugs([]) + hideModal() + } + + createEffect(() => { + setSlugs(() => { + return collectionToInvite() + .filter((user) => { + return user.selected === true + }) + .map((user) => { + return user['slug'] + }) + }) + if (slugs().length > 2 && theme().length === 0) { + setTheme(t('group_chat')) + } + }) + + const handleSetTheme = () => { + setTheme(textInput.value.length > 0 && textInput.value) + } + + const handleClick = (user) => { + setCollectionToInvite((userCollection) => { + return userCollection.map((clickedUser) => + user.slug === clickedUser.slug ? { ...clickedUser, selected: !clickedUser.selected } : clickedUser + ) + }) + } + + const { chatEntities, actions } = useInbox() + + console.log('!!! chatEntities:', chatEntities) + + const handleCreate = async () => { + try { + const initChat = await actions.createChat(slugs(), theme()) + console.debug('[initChat]', initChat) + } catch (error) { + console.error(error) + } + } + + return ( +
+

{t('create_chat')}

+ {slugs().length > 2 && ( + + )} + +
+ + {(author) => ( + handleClick(author)} author={author} selected={author.selected} /> + )} + +
+ +
+ + +
+
+ ) +} + +export default CreateModalContent diff --git a/src/components/Inbox/DialogCard.tsx b/src/components/Inbox/DialogCard.tsx index dcfb7021..a9a4dcd3 100644 --- a/src/components/Inbox/DialogCard.tsx +++ b/src/components/Inbox/DialogCard.tsx @@ -14,19 +14,9 @@ type DialogProps = { } const DialogCard = (props: DialogProps) => { - const { chatEntities, actions } = useInbox() - const handleOpenChat = async () => { - try { - const initChat = await actions.createChat([props.author.slug, props.ownSlug]) - console.debug('[initChat]', initChat) - } catch (error) { - console.error(error) - } - } - return ( //DialogCardView - подумать -
+
diff --git a/src/components/Inbox/InviteUser.module.scss b/src/components/Inbox/InviteUser.module.scss new file mode 100644 index 00000000..6f4804ac --- /dev/null +++ b/src/components/Inbox/InviteUser.module.scss @@ -0,0 +1,35 @@ +.InviteUser { + display: flex; + align-items: center; + justify-content: space-between; + flex-basis: 0; + flex-grow: 1; + min-width: 0; + padding: 12px; + gap: 10px; + cursor: pointer; + transition: all 0.3s ease-in-out; + + &:hover { + background: rgba(#f7f7f7, 0.65); + } + + .name { + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: 500; + font-size: 14px; + } + + .action { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + background: #f7f7f7; + border-radius: 6px; + } +} diff --git a/src/components/Inbox/InviteUser.tsx b/src/components/Inbox/InviteUser.tsx new file mode 100644 index 00000000..4fe85ed8 --- /dev/null +++ b/src/components/Inbox/InviteUser.tsx @@ -0,0 +1,22 @@ +import styles from './InviteUser.module.scss' +import DialogAvatar from './DialogAvatar' +import type { Author } from '../../graphql/types.gen' +import { Icon } from '../_shared/Icon' + +type DialogProps = { + author: Author + selected: boolean + onClick: () => void +} + +const InviteUser = (props: DialogProps) => { + return ( +
+ +
{props.author.name}
+
{props.selected ? : }
+
+ ) +} + +export default InviteUser diff --git a/src/components/Inbox/Search.module.scss b/src/components/Inbox/Search.module.scss index 207ad6a6..a92c66bc 100644 --- a/src/components/Inbox/Search.module.scss +++ b/src/components/Inbox/Search.module.scss @@ -8,7 +8,7 @@ input { display: block; - height: 40px; + height: 36px; border: none; box-shadow: none; padding: 10px 36px 10px 12px; @@ -38,7 +38,7 @@ position: absolute; width: 16px; height: 16px; - top: 12px; + top: 10px; right: 12px; opacity: 0.5; } diff --git a/src/components/Nav/Header.tsx b/src/components/Nav/Header.tsx index d90cd6df..d1262857 100644 --- a/src/components/Nav/Header.tsx +++ b/src/components/Nav/Header.tsx @@ -84,7 +84,7 @@ export const Header = (props: Props) => { [styles.headerWithTitle]: Boolean(props.title) }} > - + diff --git a/src/components/Nav/Modal.scss b/src/components/Nav/Modal.module.scss similarity index 75% rename from src/components/Nav/Modal.scss rename to src/components/Nav/Modal.module.scss index 8c7bc2f0..746202e0 100644 --- a/src/components/Nav/Modal.scss +++ b/src/components/Nav/Modal.module.scss @@ -1,4 +1,4 @@ -.modalwrap { +.backdrop { align-items: center; background: rgb(20 20 20 / 70%); display: flex; @@ -10,9 +10,16 @@ position: fixed; top: 0; width: 100%; - z-index: 10; + z-index: 100; +} - .close-control { +.modal { + background: #fff; + max-width: 1000px; + position: relative; + width: 80%; + + .close { position: absolute; top: 1em; cursor: pointer; @@ -41,19 +48,16 @@ // top: 11em; } } -} -.modalwrap__inner { - background: #fff; - max-width: 1000px; - position: relative; - width: 80%; -} - -.modalwrap__content { - padding: $container-padding-x; - - @media (min-width: 800px) and (max-width: 991px) { - padding: 10rem 6rem; + &.narrow { + max-width: 460px; + width: 50%; + @media (min-width: 800px) and (max-width: 991px) { + width: 80%; + } + .close { + right: 12px; + top: 12px; + } } } diff --git a/src/components/Nav/Modal.tsx b/src/components/Nav/Modal.tsx index 2ebe709d..51b55eac 100644 --- a/src/components/Nav/Modal.tsx +++ b/src/components/Nav/Modal.tsx @@ -1,22 +1,24 @@ import { createEffect, createSignal, Show } from 'solid-js' import type { JSX } from 'solid-js' import { getLogger } from '../../utils/logger' -import './Modal.scss' import { hideModal, useModalStore } from '../../stores/ui' import { useEscKeyDownHandler } from '../../utils/useEscKeyDownHandler' +import { clsx } from 'clsx' +import styles from './Modal.module.scss' const log = getLogger('modal') interface ModalProps { name: string + variant: 'narrow' | 'wide' children: JSX.Element } export const Modal = (props: ModalProps) => { const { modal } = useModalStore() - const wrapClick = (event: { target: Element }) => { - if (event.target.classList.contains('modalwrap')) hideModal() + const backdropClick = (event: Event) => { + hideModal() } useEscKeyDownHandler(() => hideModal()) @@ -30,10 +32,16 @@ export const Modal = (props: ModalProps) => { return ( -
-
+
+
event.stopPropagation()} + > {props.children} -
+
{ return ( - + - +
diff --git a/src/components/Views/Inbox.tsx b/src/components/Views/Inbox.tsx index 18095ecc..ca076a1b 100644 --- a/src/components/Views/Inbox.tsx +++ b/src/components/Views/Inbox.tsx @@ -12,6 +12,10 @@ import { loadRecipients, loadChats } from '../../stores/inbox' import { t } from '../../utils/intl' import '../../styles/Inbox.scss' import { useInbox } from '../../context/inbox' +import { Modal } from '../Nav/Modal' +import { showModal } from '../../stores/ui' +import InviteUser from '../Inbox/InviteUser' +import CreateModalContent from '../Inbox/CreateModalContent' const OWNER_ID = '501' const client = createClient({ @@ -133,21 +137,30 @@ export const InboxView = () => { formParent.dataset.replicatedValue = postMessageText() }) - // FIXME: прописать типы - // const { chatEntitieies, actions: { createCaht }} = useInbox() - // const { actions: { createCaht }} = useInbox() - + const handleOpenInviteModal = (event: Event) => { + event.preventDefault() + showModal('inviteToChat') + } return (
+ + +
- + +
  • {t('All')}
  • -
  • {t('Conversations')}
  • +
  • {t('Personal')}
  • {t('Groups')}
diff --git a/src/context/inbox.tsx b/src/context/inbox.tsx index f111d126..e3b41d44 100644 --- a/src/context/inbox.tsx +++ b/src/context/inbox.tsx @@ -7,7 +7,7 @@ import { createStore } from 'solid-js/store' type InboxContextType = { chatEntities: { [chatId: string]: Message[] } actions: { - createChat: (members: string[], title?: string) => Promise + createChat: (members: string[], title: string) => Promise } } @@ -20,8 +20,8 @@ export function useInbox() { export const InboxProvider = (props: { children: JSX.Element }) => { const [chatEntities, setChatEntities] = createStore({}) - const createChat = async (members: string[]) => { - const chat = await apiClient.createChat({ members }) + const createChat = async (members: string[], title: string) => { + const chat = await apiClient.createChat({ members, title }) setChatEntities((s) => { s[chat.id] = chat }) @@ -31,6 +31,7 @@ export const InboxProvider = (props: { children: JSX.Element }) => { const actions = { createChat } + const value: InboxContextType = { chatEntities, actions } return {props.children} } diff --git a/src/locales/ru.json b/src/locales/ru.json index 9d544a05..49cd9358 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -181,7 +181,12 @@ "zine": "журнал", "shout": "пост", "discussion": "дискурс", - "Conversations": "Переписки", + "Personal": "Личные", "Groups": "Группы", - "All": "Все" + "All": "Все", + "create_chat": "Создать чат", + "create_group": "Создать группу", + "discourse_theme": "Тема дискурса", + "cancel": "Отмена", + "group_chat": "Общий чат" } diff --git a/src/stores/ui.ts b/src/stores/ui.ts index cbff3f54..79730db3 100644 --- a/src/stores/ui.ts +++ b/src/stores/ui.ts @@ -4,7 +4,7 @@ import type { AuthModalSearchParams, ConfirmEmailSearchParams } from '../compone import type { RootSearchParams } from '../components/types' export const [locale, setLocale] = createSignal('ru') -export type ModalType = 'auth' | 'subscribe' | 'feedback' | 'thank' | 'donate' +export type ModalType = 'auth' | 'subscribe' | 'feedback' | 'thank' | 'donate' | 'inviteToChat' type WarnKind = 'error' | 'warn' | 'info' export interface Warning { @@ -18,7 +18,8 @@ export const MODALS: Record = { subscribe: 'subscribe', feedback: 'feedback', thank: 'thank', - donate: 'donate' + donate: 'donate', + inviteToChat: 'inviteToChat' } const [modal, setModal] = createSignal(null) diff --git a/src/styles/Inbox.scss b/src/styles/Inbox.scss index 57709423..ea766bbe 100644 --- a/src/styles/Inbox.scss +++ b/src/styles/Inbox.scss @@ -17,7 +17,7 @@ main { flex: 1; flex-direction: column; position: fixed; - z-index: 9; + z-index: 900; .row { flex: 1; @@ -41,6 +41,12 @@ main { $fade-height: 10px; + .sidebar-header { + display: flex; + align-items: center; + gap: 10px; + } + .holder { overflow: hidden; flex: 1; @@ -105,11 +111,13 @@ main { li { margin-right: 1em; + color: #696969; } strong { border-bottom: 3px solid; font-weight: normal; + color: #000; } } diff --git a/src/styles/app.scss b/src/styles/app.scss index 34ae8298..f568686d 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -4,6 +4,8 @@ @import 'bootstrap/scss/containers'; @import 'bootstrap/scss/grid'; @import 'bootstrap/scss/bootstrap-utilities'; +@import 'bootstrap/scss/forms'; +@import 'bootstrap/scss/buttons'; :root { --background-color: #fff;