Merge remote-tracking branch 'my/feature/see_connect_merge_with_main' into feature/sse-connect

This commit is contained in:
Untone 2023-11-16 17:53:59 +03:00
commit 46810edaae
8 changed files with 108 additions and 48 deletions

View File

@ -452,5 +452,6 @@
"video": "video", "video": "video",
"view": "view", "view": "view",
"viewsWithCount": "{count} {count, plural, one {view} other {views}}", "viewsWithCount": "{count} {count, plural, one {view} other {views}}",
"yesterday": "yesterday" "yesterday": "yesterday",
"To new messages": "To new messages"
} }

View File

@ -476,5 +476,6 @@
"video": "видео", "video": "видео",
"view": "просмотр", "view": "просмотр",
"viewsWithCount": "{count} {count, plural, one {просмотр} few {просмотрa} other {просмотров}}", "viewsWithCount": "{count} {count, plural, one {просмотр} few {просмотрa} other {просмотров}}",
"yesterday": "вчера" "yesterday": "вчера",
"To new messages": "К новым сообщениям"
} }

View File

@ -1,4 +1,4 @@
import { Show, Switch, Match, createMemo } from 'solid-js' import { Show, Switch, Match, createMemo, createEffect } from 'solid-js'
import DialogAvatar from './DialogAvatar' import DialogAvatar from './DialogAvatar'
import type { ChatMember } from '../../graphql/types.gen' import type { ChatMember } from '../../graphql/types.gen'
import GroupDialogAvatar from './GroupDialogAvatar' import GroupDialogAvatar from './GroupDialogAvatar'
@ -41,23 +41,20 @@ const DialogCard = (props: DialogProps) => {
})} })}
onClick={props.onClick} onClick={props.onClick}
> >
<Switch <Switch>
fallback={ <Match when={props.members.length === 2}>
<div class={styles.avatar}>
<Show <Show
when={props.isChatHeader} when={props.isChatHeader}
fallback={ fallback={<DialogAvatar name={companions()[0].slug} url={companions()[0].userpic} />}
<div class={styles.avatar}>
<DialogAvatar name={props.members[0].slug} url={props.members[0].userpic} />
</div>
}
> >
<AuthorBadge nameOnly={true} author={props.members[0]} /> <AuthorBadge nameOnly={true} author={companions()[0]} />
</Show> </Show>
} </div>
> </Match>
<Match when={props.members.length >= 3}> <Match when={props.members.length >= 3}>
<div class={styles.avatar}> <div class={styles.avatar}>
<GroupDialogAvatar users={props.members} /> <GroupDialogAvatar users={companions()} />
</div> </div>
</Match> </Match>
</Switch> </Switch>

View File

@ -25,7 +25,7 @@ export const Message = (props: Props) => {
return ( return (
<div class={clsx(styles.Message, isOwn && styles.own)}> <div class={clsx(styles.Message, isOwn && styles.own)}>
<Show when={!isOwn}> <Show when={!isOwn && user}>
<div class={styles.author}> <div class={styles.author}>
<DialogAvatar size="small" name={user.name} url={user.userpic} /> <DialogAvatar size="small" name={user.name} url={user.userpic} />
<div class={styles.name}>{user.name}</div> <div class={styles.name}>{user.name}</div>

View File

@ -1,4 +1,4 @@
import { For, createSignal, Show, onMount, createEffect, createMemo } from 'solid-js' import { For, createSignal, Show, onMount, createEffect, createMemo, on } from 'solid-js'
import type { Author, Chat, Message as MessageType } from '../../graphql/types.gen' import type { Author, Chat, Message as MessageType } from '../../graphql/types.gen'
import DialogCard from '../Inbox/DialogCard' import DialogCard from '../Inbox/DialogCard'
import Search from '../Inbox/Search' import Search from '../Inbox/Search'
@ -19,6 +19,7 @@ import { clsx } from 'clsx'
import styles from '../../styles/Inbox.module.scss' import styles from '../../styles/Inbox.module.scss'
import { useLocalize } from '../../context/localize' import { useLocalize } from '../../context/localize'
import SimplifiedEditor from '../Editor/SimplifiedEditor' import SimplifiedEditor from '../Editor/SimplifiedEditor'
import { Popover } from '../_shared/Popover'
type InboxSearchParams = { type InboxSearchParams = {
initChat: string initChat: string
@ -47,9 +48,15 @@ export const InboxView = () => {
const [currentDialog, setCurrentDialog] = createSignal<Chat>() const [currentDialog, setCurrentDialog] = createSignal<Chat>()
const [messageToReply, setMessageToReply] = createSignal<MessageType | null>(null) const [messageToReply, setMessageToReply] = createSignal<MessageType | null>(null)
const [isClear, setClear] = createSignal(false) const [isClear, setClear] = createSignal(false)
const [isScrollToNewVisible, setIsScrollToNewVisible] = createSignal(false)
const { session } = useSession() const { session } = useSession()
const currentUserId = createMemo(() => session()?.user.id) const currentUserId = createMemo(() => session()?.user.id)
const { changeSearchParam, searchParams } = useRouter<InboxSearchParams>() const { changeSearchParam, searchParams } = useRouter<InboxSearchParams>()
const messagesContainerRef: { current: HTMLDivElement } = {
current: null
}
// Поиск по диалогам // Поиск по диалогам
const getQuery = (query) => { const getQuery = (query) => {
if (query().length >= 2) { if (query().length >= 2) {
@ -60,8 +67,6 @@ export const InboxView = () => {
} }
} }
let chatWindow
const handleOpenChat = async (chat: Chat) => { const handleOpenChat = async (chat: Chat) => {
setCurrentDialog(chat) setCurrentDialog(chat)
changeSearchParam({ changeSearchParam({
@ -72,24 +77,13 @@ export const InboxView = () => {
} catch (error) { } catch (error) {
console.error('[getMessages]', error) console.error('[getMessages]', error)
} finally { } finally {
chatWindow.scrollTop = chatWindow.scrollHeight messagesContainerRef.current.scroll({
top: messagesContainerRef.current.scrollHeight,
behavior: 'instant'
})
} }
} }
/*
createEffect(() => {
setInterval(async () => {
if (!currentDialog()) return
try {
await getMessages(currentDialog().id)
} catch (error) {
console.error('[getMessages]', error)
} finally {
chatWindow.scrollTop = chatWindow.scrollHeight
}
}, 2000)
})
*/
onMount(async () => { onMount(async () => {
try { try {
const response = await loadRecipients({ days: 365 }) const response = await loadRecipients({ days: 365 })
@ -108,7 +102,7 @@ export const InboxView = () => {
}) })
setClear(true) setClear(true)
setMessageToReply(null) setMessageToReply(null)
chatWindow.scrollTop = chatWindow.scrollHeight messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight
setClear(false) setClear(false)
} }
@ -152,6 +146,42 @@ export const InboxView = () => {
return messages().find((message) => message.id === messageId) return messages().find((message) => message.id === messageId)
} }
createEffect(
on(
() => messages(),
() => {
if (!messagesContainerRef.current) {
return
}
if (messagesContainerRef.current.scrollTop >= messagesContainerRef.current.scrollHeight) {
return
}
messagesContainerRef.current.scroll({
top: messagesContainerRef.current.scrollHeight,
behavior: 'smooth'
})
}
),
{ defer: true }
)
const handleScrollMessageContainer = () => {
if (
messagesContainerRef.current.scrollHeight - messagesContainerRef.current.scrollTop >
messagesContainerRef.current.clientHeight * 1.5
) {
setIsScrollToNewVisible(true)
} else {
setIsScrollToNewVisible(false)
}
}
const handleScrollToNew = () => {
messagesContainerRef.current.scroll({
top: messagesContainerRef.current.scrollHeight,
behavior: 'smooth'
})
setIsScrollToNewVisible(false)
}
return ( return (
<div class={clsx('container', styles.Inbox)}> <div class={clsx('container', styles.Inbox)}>
<Modal variant="narrow" name="inviteToChat"> <Modal variant="narrow" name="inviteToChat">
@ -221,6 +251,7 @@ export const InboxView = () => {
<div class={clsx('col-md-16', styles.conversation)}> <div class={clsx('col-md-16', styles.conversation)}>
<Show <Show
keyed={true}
when={currentDialog()} when={currentDialog()}
fallback={ fallback={
<MessagesFallback <MessagesFallback
@ -232,7 +263,20 @@ export const InboxView = () => {
> >
<DialogHeader ownId={currentUserId()} chat={currentDialog()} /> <DialogHeader ownId={currentUserId()} chat={currentDialog()} />
<div class={styles.conversationMessages}> <div class={styles.conversationMessages}>
<div class={styles.messagesContainer} ref={chatWindow}> <Show when={isScrollToNewVisible()}>
<Popover content={t('To new messages')}>
{(triggerRef: (el) => void) => (
<div ref={triggerRef} class={styles.scrollToNew} onClick={handleScrollToNew}>
<Icon name="arrow-right" class={styles.icon} />
</div>
)}
</Popover>
</Show>
<div
class={styles.messagesContainer}
ref={(el) => (messagesContainerRef.current = el)}
onScroll={handleScrollMessageContainer}
>
<For each={messages()}> <For each={messages()}>
{(message) => ( {(message) => (
<Message <Message

View File

@ -29,15 +29,15 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
actions: { setMessageHandler } actions: { setMessageHandler }
} = useNotifications() } = useNotifications()
const handleMessage = (m) => { const handleMessage = (sseMessage) => {
console.log('[context.inbox]:', m) console.log('[context.inbox]:', sseMessage)
// TODO: handle all action types: create update delete join left // TODO: handle all action types: create update delete join left
if (['create', 'update', 'delete'].includes(m.action)) { if (['create', 'update', 'delete'].includes(sseMessage.action)) {
const msg = m.payload const relivedMessage = sseMessage.payload
setMessages((mmm) => [msg, ...mmm]) setMessages((prev) => [...prev, relivedMessage])
} else if (['left', 'join'].includes(m.action)) { } else if (['left', 'join'].includes(sseMessage.action)) {
// TODO: set chat members // TODO: set chat members
console.debug(m) console.debug(sseMessage)
} }
} }
@ -50,7 +50,6 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
const loadChats = async () => { const loadChats = async () => {
try { try {
const newChats = await inboxClient.loadChats({ limit: 50, offset: 0 }) const newChats = await inboxClient.loadChats({ limit: 50, offset: 0 })
console.log('!!! newChats:', newChats)
setChats(newChats) setChats(newChats)
} catch (error) { } catch (error) {
console.log('[loadChats]', error) console.log('[loadChats]', error)

View File

@ -114,6 +114,25 @@ main {
overflow: auto; overflow: auto;
position: relative; position: relative;
.scrollToNew {
osition: absolute;
z-index: 2;
bottom: 8px;
overflow: hidden;
right: 0;
width: 40px;
padding: 1rem;
border: 2px solid var(--black-100);
border-radius: 50%;
height: 40px;
cursor: pointer;
background: var(--background-color);
.icon {
rotate: 90deg;
}
}
.messagesContainer { .messagesContainer {
left: 0; left: 0;
height: 100%; height: 100%;

View File

@ -383,7 +383,6 @@ export const apiClient = {
export const inboxClient = { export const inboxClient = {
loadChats: async (options: QueryLoadChatsArgs): Promise<Chat[]> => { loadChats: async (options: QueryLoadChatsArgs): Promise<Chat[]> => {
const resp = await privateInboxGraphQLClient.query(myChats, options).toPromise() const resp = await privateInboxGraphQLClient.query(myChats, options).toPromise()
console.log('!!! resp:', resp)
return resp.data.loadChats.chats return resp.data.loadChats.chats
}, },