Merge remote-tracking branch 'origin/prepare-inbox' into testing
This commit is contained in:
commit
d68cb92462
3
public/icons/cross.svg
Normal file
3
public/icons/cross.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.94025 5L0.227936 1.28769L1.2886 0.227029L5.00091 3.93934L8.71322 0.227029L9.77388 1.28769L6.06157 5L9.77388 8.71231L8.71322 9.77297L5.00091 6.06066L1.2886 9.77297L0.227936 8.71231L3.94025 5Z" fill="#D00820"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 364 B |
4
public/icons/plus-button.svg
Normal file
4
public/icons/plus-button.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M19 19V12H21V19H28V21H21L21 28H19L19 21H12V19H19Z" fill="#404040"/>
|
||||||
|
<rect x="1" y="1" width="38" height="38" rx="11" stroke="#404040" stroke-width="2"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 305 B |
3
public/icons/plus.svg
Normal file
3
public/icons/plus.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.25098 5.25V0L6.75098 0V5.25L12.001 5.25V6.75L6.75098 6.75L6.75098 12H5.25098L5.25098 6.75H0.000976563L0.000976563 5.25H5.25098Z" fill="#141414"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 301 B |
9
src/components/Inbox/CreateModalContent.module.scss
Normal file
9
src/components/Inbox/CreateModalContent.module.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.CreateModalContent {
|
||||||
|
padding: 24px;
|
||||||
|
.footer {
|
||||||
|
padding-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
}
|
112
src/components/Inbox/CreateModalContent.tsx
Normal file
112
src/components/Inbox/CreateModalContent.tsx
Normal file
|
@ -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<string>('')
|
||||||
|
const [slugs, setSlugs] = createSignal<string[]>([])
|
||||||
|
const [collectionToInvite, setCollectionToInvite] = createSignal<inviteUser[]>(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 (
|
||||||
|
<div class={styles.CreateModalContent}>
|
||||||
|
<h4>{t('create_chat')}</h4>
|
||||||
|
{slugs().length > 2 && (
|
||||||
|
<input
|
||||||
|
ref={textInput}
|
||||||
|
onInput={handleSetTheme}
|
||||||
|
type="text"
|
||||||
|
required={true}
|
||||||
|
class="form-control form-control-lg fs-3"
|
||||||
|
placeholder={t('discourse_theme')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div class="invite-recipients" style={{ height: '400px', overflow: 'auto' }}>
|
||||||
|
<For each={collectionToInvite()}>
|
||||||
|
{(author) => (
|
||||||
|
<InviteUser onClick={() => handleClick(author)} author={author} selected={author.selected} />
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.footer}>
|
||||||
|
<button type="button" class="btn btn-lg fs-3 btn-outline-danger" onClick={reset}>
|
||||||
|
{t('cancel')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-lg fs-3 btn-outline-primary"
|
||||||
|
onClick={handleCreate}
|
||||||
|
disabled={slugs().length === 0}
|
||||||
|
>
|
||||||
|
{slugs().length > 2 ? t('create_group') : t('create_chat')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateModalContent
|
|
@ -1,6 +1,6 @@
|
||||||
import styles from './DialogCard.module.scss'
|
import styles from './DialogCard.module.scss'
|
||||||
import DialogAvatar from './DialogAvatar'
|
import DialogAvatar from './DialogAvatar'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author, User } from '../../graphql/types.gen'
|
||||||
import { apiClient } from '../../utils/apiClient'
|
import { apiClient } from '../../utils/apiClient'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import { useInbox } from '../../context/inbox'
|
import { useInbox } from '../../context/inbox'
|
||||||
|
@ -9,29 +9,21 @@ type DialogProps = {
|
||||||
online?: boolean
|
online?: boolean
|
||||||
message?: string
|
message?: string
|
||||||
counter?: number
|
counter?: number
|
||||||
author?: Author
|
users: User[]
|
||||||
ownSlug: Author['slug']
|
ownSlug: User['slug']
|
||||||
}
|
}
|
||||||
|
|
||||||
const DialogCard = (props: DialogProps) => {
|
const DialogCard = (props: DialogProps) => {
|
||||||
const { chatEntities, actions } = useInbox()
|
// @ts-ignore
|
||||||
const handleOpenChat = async () => {
|
const participants = props.users.filter((user) => user !== props.ownSlug)
|
||||||
try {
|
console.log('!!! participants:', participants)
|
||||||
const initChat = await actions.createChat([props.author.slug, props.ownSlug])
|
// @ts-ignore
|
||||||
console.debug('[initChat]', initChat)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//DialogCardView - подумать
|
//DialogCardView - подумать
|
||||||
<div class={styles.DialogCard} onClick={handleOpenChat}>
|
<div class={styles.DialogCard}>
|
||||||
<div class={styles.avatar}>
|
<div class={styles.avatar}>{/*<DialogAvatar name={participants[0]} online={props.online} />*/}</div>
|
||||||
<DialogAvatar name={props.author.name} url={props.author.userpic} online={props.online} />
|
|
||||||
</div>
|
|
||||||
<div class={styles.row}>
|
<div class={styles.row}>
|
||||||
<div class={styles.name}>{props.author.name}</div>
|
{/*<div class={styles.name}>{participants[0]}</div>*/}
|
||||||
<div class={styles.message}>
|
<div class={styles.message}>
|
||||||
Указать предпочтительные языки для результатов поиска можно в разделе
|
Указать предпочтительные языки для результатов поиска можно в разделе
|
||||||
</div>
|
</div>
|
||||||
|
|
35
src/components/Inbox/InviteUser.module.scss
Normal file
35
src/components/Inbox/InviteUser.module.scss
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
22
src/components/Inbox/InviteUser.tsx
Normal file
22
src/components/Inbox/InviteUser.tsx
Normal file
|
@ -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 (
|
||||||
|
<div class={styles.InviteUser} onClick={props.onClick}>
|
||||||
|
<DialogAvatar name={props.author.name} url={props.author.userpic} />
|
||||||
|
<div class={styles.name}>{props.author.name}</div>
|
||||||
|
<div class={styles.action}>{props.selected ? <Icon name="cross" /> : <Icon name="plus" />}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InviteUser
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
input {
|
input {
|
||||||
display: block;
|
display: block;
|
||||||
height: 40px;
|
height: 36px;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: 10px 36px 10px 12px;
|
padding: 10px 36px 10px 12px;
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
top: 12px;
|
top: 10px;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ export const Header = (props: Props) => {
|
||||||
[styles.headerWithTitle]: Boolean(props.title)
|
[styles.headerWithTitle]: Boolean(props.title)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal name="auth">
|
<Modal variant="wide" name="auth">
|
||||||
<AuthModal />
|
<AuthModal />
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.modalwrap {
|
.backdrop {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: rgb(20 20 20 / 70%);
|
background: rgb(20 20 20 / 70%);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -10,9 +10,16 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 10;
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
.close-control {
|
.modal {
|
||||||
|
background: #fff;
|
||||||
|
max-width: 1000px;
|
||||||
|
position: relative;
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
|
.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 1em;
|
top: 1em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -41,19 +48,16 @@
|
||||||
// top: 11em;
|
// top: 11em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.modalwrap__inner {
|
|
||||||
background: #fff;
|
|
||||||
max-width: 1000px;
|
|
||||||
position: relative;
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modalwrap__content {
|
|
||||||
padding: $container-padding-x;
|
|
||||||
|
|
||||||
|
&.narrow {
|
||||||
|
max-width: 460px;
|
||||||
|
width: 50%;
|
||||||
@media (min-width: 800px) and (max-width: 991px) {
|
@media (min-width: 800px) and (max-width: 991px) {
|
||||||
padding: 10rem 6rem;
|
width: 80%;
|
||||||
|
}
|
||||||
|
.close {
|
||||||
|
right: 12px;
|
||||||
|
top: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,22 +1,24 @@
|
||||||
import { createEffect, createSignal, Show } from 'solid-js'
|
import { createEffect, createSignal, Show } from 'solid-js'
|
||||||
import type { JSX } from 'solid-js'
|
import type { JSX } from 'solid-js'
|
||||||
import { getLogger } from '../../utils/logger'
|
import { getLogger } from '../../utils/logger'
|
||||||
import './Modal.scss'
|
|
||||||
import { hideModal, useModalStore } from '../../stores/ui'
|
import { hideModal, useModalStore } from '../../stores/ui'
|
||||||
import { useEscKeyDownHandler } from '../../utils/useEscKeyDownHandler'
|
import { useEscKeyDownHandler } from '../../utils/useEscKeyDownHandler'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './Modal.module.scss'
|
||||||
|
|
||||||
const log = getLogger('modal')
|
const log = getLogger('modal')
|
||||||
|
|
||||||
interface ModalProps {
|
interface ModalProps {
|
||||||
name: string
|
name: string
|
||||||
|
variant: 'narrow' | 'wide'
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Modal = (props: ModalProps) => {
|
export const Modal = (props: ModalProps) => {
|
||||||
const { modal } = useModalStore()
|
const { modal } = useModalStore()
|
||||||
|
|
||||||
const wrapClick = (event: { target: Element }) => {
|
const backdropClick = (event: Event) => {
|
||||||
if (event.target.classList.contains('modalwrap')) hideModal()
|
hideModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
useEscKeyDownHandler(() => hideModal())
|
useEscKeyDownHandler(() => hideModal())
|
||||||
|
@ -30,10 +32,16 @@ export const Modal = (props: ModalProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={visible()}>
|
<Show when={visible()}>
|
||||||
<div class="modalwrap" onClick={wrapClick}>
|
<div class={styles.backdrop} onClick={backdropClick}>
|
||||||
<div class="modalwrap__inner">
|
<div
|
||||||
|
class={clsx(styles.modal, {
|
||||||
|
[styles.wide]: props.variant === 'wide',
|
||||||
|
[styles.narrow]: props.variant === 'narrow'
|
||||||
|
})}
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
<div class="close-control" onClick={hideModal}>
|
<div class={styles.close} onClick={hideModal}>
|
||||||
<svg width="16" height="18" viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="18" viewBox="0 0 16 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
d="M7.99987 7.52552L14.1871 0.92334L15.9548 2.80968L9.76764 9.41185L15.9548 16.014L14.1871 17.9004L7.99987 11.2982L1.81269 17.9004L0.0449219 16.014L6.23211 9.41185L0.0449225 2.80968L1.81269 0.92334L7.99987 7.52552Z"
|
d="M7.99987 7.52552L14.1871 0.92334L15.9548 2.80968L9.76764 9.41185L15.9548 16.014L14.1871 17.9004L7.99987 11.2982L1.81269 17.9004L0.0449219 16.014L6.23211 9.41185L0.0449225 2.80968L1.81269 0.92334L7.99987 7.52552Z"
|
||||||
|
|
|
@ -15,10 +15,10 @@ export const ManifestPage = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrap>
|
<PageWrap>
|
||||||
<Modal name="feedback">
|
<Modal variant="wide" name="feedback">
|
||||||
<Feedback />
|
<Feedback />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal name="subscribe">
|
<Modal variant="wide" name="subscribe">
|
||||||
<Subscribe />
|
<Subscribe />
|
||||||
</Modal>
|
</Modal>
|
||||||
<article class="wide-container container--static-page">
|
<article class="wide-container container--static-page">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { For, createSignal, Show, onMount, createEffect, createMemo } from 'solid-js'
|
import { For, createSignal, Show, onMount, createEffect, createMemo } from 'solid-js'
|
||||||
import type { Author } from '../../graphql/types.gen'
|
import type { Author, Chat } from '../../graphql/types.gen'
|
||||||
import { AuthorCard } from '../Author/Card'
|
import { AuthorCard } from '../Author/Card'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
import { Loading } from '../Loading'
|
import { Loading } from '../Loading'
|
||||||
|
@ -12,6 +12,10 @@ import { loadRecipients, loadChats } from '../../stores/inbox'
|
||||||
import { t } from '../../utils/intl'
|
import { t } from '../../utils/intl'
|
||||||
import '../../styles/Inbox.scss'
|
import '../../styles/Inbox.scss'
|
||||||
import { useInbox } from '../../context/inbox'
|
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 OWNER_ID = '501'
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
|
@ -54,18 +58,10 @@ const postMessage = async (msg: string) => {
|
||||||
return response.data.createComment
|
return response.data.createComment
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGetChats = async () => {
|
|
||||||
try {
|
|
||||||
const response = await loadChats()
|
|
||||||
console.log('!!! handleGetChats:', response)
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const InboxView = () => {
|
export const InboxView = () => {
|
||||||
const [messages, setMessages] = createSignal([])
|
const [messages, setMessages] = createSignal([])
|
||||||
const [recipients, setRecipients] = createSignal<Author[]>([])
|
const [recipients, setRecipients] = createSignal<Author[]>([])
|
||||||
|
const [chats, setChats] = createSignal<Chat[]>([])
|
||||||
const [cashedRecipients, setCashedRecipients] = createSignal<Author[]>([])
|
const [cashedRecipients, setCashedRecipients] = createSignal<Author[]>([])
|
||||||
const [postMessageText, setPostMessageText] = createSignal('')
|
const [postMessageText, setPostMessageText] = createSignal('')
|
||||||
const [loading, setLoading] = createSignal<boolean>(false)
|
const [loading, setLoading] = createSignal<boolean>(false)
|
||||||
|
@ -112,6 +108,13 @@ export const InboxView = () => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await loadChats()
|
||||||
|
setChats(response as unknown as Chat[])
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
|
@ -125,36 +128,46 @@ export const InboxView = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let formParent // autoresize ghost element
|
let textareaParent // textarea autoresize ghost element
|
||||||
const handleChangeMessage = (event) => {
|
const handleChangeMessage = (event) => {
|
||||||
setPostMessageText(event.target.value)
|
setPostMessageText(event.target.value)
|
||||||
}
|
}
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
formParent.dataset.replicatedValue = postMessageText()
|
textareaParent.dataset.replicatedValue = postMessageText()
|
||||||
})
|
})
|
||||||
|
|
||||||
// FIXME: прописать типы
|
const handleOpenInviteModal = (event: Event) => {
|
||||||
// const { chatEntitieies, actions: { createCaht }} = useInbox()
|
event.preventDefault()
|
||||||
// const { actions: { createCaht }} = useInbox()
|
showModal('inviteToChat')
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="messages container">
|
<div class="messages container">
|
||||||
|
<Modal variant="narrow" name="inviteToChat">
|
||||||
|
<CreateModalContent users={recipients()} />
|
||||||
|
</Modal>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="chat-list col-md-4">
|
<div class="chat-list col-md-4">
|
||||||
|
<div class="sidebar-header">
|
||||||
<Search placeholder="Поиск" onChange={getQuery} />
|
<Search placeholder="Поиск" onChange={getQuery} />
|
||||||
|
<div onClick={handleOpenInviteModal}>
|
||||||
|
<Icon name="plus-button" style={{ width: '40px', height: '40px' }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="chat-list__types">
|
<div class="chat-list__types">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<strong>{t('All')}</strong>
|
<strong>{t('All')}</strong>
|
||||||
</li>
|
</li>
|
||||||
<li onClick={handleGetChats}>{t('Conversations')}</li>
|
<li>{t('Personal')}</li>
|
||||||
<li>{t('Groups')}</li>
|
<li>{t('Groups')}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="holder">
|
<div class="holder">
|
||||||
<div class="dialogs">
|
<div class="dialogs">
|
||||||
<For each={recipients()}>
|
<For each={chats()}>
|
||||||
{(author) => <DialogCard ownSlug={currentSlug()} author={author} online={true} />}
|
{(chat) => <DialogCard users={chat.users} ownSlug={currentSlug()} />}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,7 +198,7 @@ export const InboxView = () => {
|
||||||
|
|
||||||
<div class="message-form">
|
<div class="message-form">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="grow-wrap" ref={formParent}>
|
<div class="grow-wrap" ref={textareaParent}>
|
||||||
<textarea
|
<textarea
|
||||||
value={postMessageText()}
|
value={postMessageText()}
|
||||||
rows={1}
|
rows={1}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { createStore } from 'solid-js/store'
|
||||||
type InboxContextType = {
|
type InboxContextType = {
|
||||||
chatEntities: { [chatId: string]: Message[] }
|
chatEntities: { [chatId: string]: Message[] }
|
||||||
actions: {
|
actions: {
|
||||||
createChat: (members: string[], title?: string) => Promise<void>
|
createChat: (members: string[], title: string) => Promise<void>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ export function useInbox() {
|
||||||
export const InboxProvider = (props: { children: JSX.Element }) => {
|
export const InboxProvider = (props: { children: JSX.Element }) => {
|
||||||
const [chatEntities, setChatEntities] = createStore({})
|
const [chatEntities, setChatEntities] = createStore({})
|
||||||
|
|
||||||
const createChat = async (members: string[]) => {
|
const createChat = async (members: string[], title: string) => {
|
||||||
const chat = await apiClient.createChat({ members })
|
const chat = await apiClient.createChat({ members, title })
|
||||||
setChatEntities((s) => {
|
setChatEntities((s) => {
|
||||||
s[chat.id] = chat
|
s[chat.id] = chat
|
||||||
})
|
})
|
||||||
|
@ -31,6 +31,7 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
|
||||||
const actions = {
|
const actions = {
|
||||||
createChat
|
createChat
|
||||||
}
|
}
|
||||||
|
|
||||||
const value: InboxContextType = { chatEntities, actions }
|
const value: InboxContextType = { chatEntities, actions }
|
||||||
return <InboxContext.Provider value={value}>{props.children}</InboxContext.Provider>
|
return <InboxContext.Provider value={value}>{props.children}</InboxContext.Provider>
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,12 @@
|
||||||
"zine": "журнал",
|
"zine": "журнал",
|
||||||
"shout": "пост",
|
"shout": "пост",
|
||||||
"discussion": "дискурс",
|
"discussion": "дискурс",
|
||||||
"Conversations": "Переписки",
|
"Personal": "Личные",
|
||||||
"Groups": "Группы",
|
"Groups": "Группы",
|
||||||
"All": "Все"
|
"All": "Все",
|
||||||
|
"create_chat": "Создать чат",
|
||||||
|
"create_group": "Создать группу",
|
||||||
|
"discourse_theme": "Тема дискурса",
|
||||||
|
"cancel": "Отмена",
|
||||||
|
"group_chat": "Общий чат"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { AuthModalSearchParams, ConfirmEmailSearchParams } from '../compone
|
||||||
import type { RootSearchParams } from '../components/types'
|
import type { RootSearchParams } from '../components/types'
|
||||||
|
|
||||||
export const [locale, setLocale] = createSignal('ru')
|
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'
|
type WarnKind = 'error' | 'warn' | 'info'
|
||||||
|
|
||||||
export interface Warning {
|
export interface Warning {
|
||||||
|
@ -18,7 +18,8 @@ export const MODALS: Record<ModalType, ModalType> = {
|
||||||
subscribe: 'subscribe',
|
subscribe: 'subscribe',
|
||||||
feedback: 'feedback',
|
feedback: 'feedback',
|
||||||
thank: 'thank',
|
thank: 'thank',
|
||||||
donate: 'donate'
|
donate: 'donate',
|
||||||
|
inviteToChat: 'inviteToChat'
|
||||||
}
|
}
|
||||||
|
|
||||||
const [modal, setModal] = createSignal<ModalType | null>(null)
|
const [modal, setModal] = createSignal<ModalType | null>(null)
|
||||||
|
|
|
@ -17,7 +17,7 @@ main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 9;
|
z-index: 900;
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -41,6 +41,12 @@ main {
|
||||||
|
|
||||||
$fade-height: 10px;
|
$fade-height: 10px;
|
||||||
|
|
||||||
|
.sidebar-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.holder {
|
.holder {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -105,11 +111,13 @@ main {
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
|
color: #696969;
|
||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
border-bottom: 3px solid;
|
border-bottom: 3px solid;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
color: #000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
@import 'bootstrap/scss/containers';
|
@import 'bootstrap/scss/containers';
|
||||||
@import 'bootstrap/scss/grid';
|
@import 'bootstrap/scss/grid';
|
||||||
@import 'bootstrap/scss/bootstrap-utilities';
|
@import 'bootstrap/scss/bootstrap-utilities';
|
||||||
|
@import 'bootstrap/scss/forms';
|
||||||
|
@import 'bootstrap/scss/buttons';
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background-color: #fff;
|
--background-color: #fff;
|
||||||
|
|
|
@ -272,8 +272,7 @@ export const apiClient = {
|
||||||
// inbox
|
// inbox
|
||||||
getChats: async (options: QueryLoadChatsArgs) => {
|
getChats: async (options: QueryLoadChatsArgs) => {
|
||||||
const resp = await privateGraphQLClient.query(myChats, options).toPromise()
|
const resp = await privateGraphQLClient.query(myChats, options).toPromise()
|
||||||
console.debug('[loadChats]', resp)
|
return resp.data.loadChats.chats
|
||||||
return resp.data.loadChats
|
|
||||||
},
|
},
|
||||||
|
|
||||||
createChat: async (options: MutationCreateChatArgs) => {
|
createChat: async (options: MutationCreateChatArgs) => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user