parent
f22d6c60c5
commit
87a99d9e8d
|
@ -122,12 +122,31 @@
|
|||
@include font-size(1.5rem);
|
||||
|
||||
line-height: 1.47;
|
||||
|
||||
blockquote {
|
||||
margin-left: 0;
|
||||
padding-left: 10px;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
color: #9fa1a7;
|
||||
border-left: 2px solid #696969;
|
||||
|
||||
p,
|
||||
.paragraph {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.commentAuthor,
|
||||
.commentDate,
|
||||
.commentRating {
|
||||
@include font-size(1.2rem);
|
||||
}
|
||||
.articleAuthor {
|
||||
color: #2638d9;
|
||||
font-size: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
.commentDate {
|
||||
color: rgb(0 0 0 / 30%);
|
||||
flex: 1;
|
||||
|
|
|
@ -13,18 +13,20 @@ import stylesHeader from '../Nav/Header.module.scss'
|
|||
import Userpic from '../Author/Userpic'
|
||||
import { useSession } from '../../context/session'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
import GrowingTextarea from '../_shared/GrowingTextarea'
|
||||
import CommentEditor from '../_shared/CommentEditor'
|
||||
import { ShowOnlyOnClient } from '../_shared/ShowOnlyOnClient'
|
||||
|
||||
type Props = {
|
||||
comment: Reaction
|
||||
compact?: boolean
|
||||
reactions?: Reaction[]
|
||||
isArticleAuthor?: boolean
|
||||
}
|
||||
|
||||
const Comment = (props: Props) => {
|
||||
const [isReplyVisible, setIsReplyVisible] = createSignal(false)
|
||||
const [loading, setLoading] = createSignal(false)
|
||||
const [errorMessage, setErrorMessage] = createSignal<string | null>(null)
|
||||
const [loading, setLoading] = createSignal<boolean>(false)
|
||||
const [submitted, setSubmitted] = createSignal<boolean>(false)
|
||||
const { session } = useSession()
|
||||
|
||||
const canEdit = createMemo(() => props.comment.createdBy?.slug === session()?.user?.slug)
|
||||
|
@ -58,12 +60,13 @@ const Comment = (props: Props) => {
|
|||
}
|
||||
)
|
||||
setIsReplyVisible(false)
|
||||
setSubmitted(true)
|
||||
setLoading(false)
|
||||
} catch (error) {
|
||||
console.error('[handleCreate reaction]:', error)
|
||||
setErrorMessage(t('Something went wrong, please try again'))
|
||||
}
|
||||
}
|
||||
|
||||
const formattedDate = createMemo(() =>
|
||||
formatDate(new Date(comment()?.createdAt), { hour: 'numeric', minute: 'numeric' })
|
||||
)
|
||||
|
@ -94,6 +97,10 @@ const Comment = (props: Props) => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<Show when={props.isArticleAuthor}>
|
||||
<div class={styles.articleAuthor}>{t('Author')}</div>
|
||||
</Show>
|
||||
|
||||
<div class={styles.commentDate}>{formattedDate()}</div>
|
||||
<div
|
||||
class={styles.commentRating}
|
||||
|
@ -164,14 +171,13 @@ const Comment = (props: Props) => {
|
|||
</div>
|
||||
|
||||
<Show when={isReplyVisible()}>
|
||||
<GrowingTextarea
|
||||
placeholder={t('Write comment')}
|
||||
submitButtonText={t('Send')}
|
||||
cancelButtonText={t('cancel')}
|
||||
submit={(value) => handleCreate(value)}
|
||||
loading={loading()}
|
||||
errorMessage={errorMessage()}
|
||||
/>
|
||||
<ShowOnlyOnClient>
|
||||
<CommentEditor
|
||||
initialValue={''}
|
||||
clear={submitted()}
|
||||
onSubmit={(value) => handleCreate(value)}
|
||||
/>
|
||||
</ShowOnlyOnClient>
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
|
@ -179,7 +185,13 @@ const Comment = (props: Props) => {
|
|||
<Show when={props.reactions}>
|
||||
<ul>
|
||||
<For each={props.reactions.filter((r) => r.replyTo === props.comment.id)}>
|
||||
{(reaction) => <Comment reactions={props.reactions} comment={reaction} />}
|
||||
{(reaction) => (
|
||||
<Comment
|
||||
isArticleAuthor={props.isArticleAuthor}
|
||||
reactions={props.reactions}
|
||||
comment={reaction}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
</Show>
|
||||
|
|
|
@ -7,14 +7,21 @@ import type { Reaction } from '../../graphql/types.gen'
|
|||
import { clsx } from 'clsx'
|
||||
import { byCreated, byStat } from '../../utils/sortby'
|
||||
import { Loading } from '../Loading'
|
||||
import GrowingTextarea from '../_shared/GrowingTextarea'
|
||||
import { ReactionKind } from '../../graphql/types.gen'
|
||||
import { Author, ReactionKind } from '../../graphql/types.gen'
|
||||
import { useSession } from '../../context/session'
|
||||
import CommentEditor from '../_shared/CommentEditor'
|
||||
import { ShowOnlyOnClient } from '../_shared/ShowOnlyOnClient'
|
||||
|
||||
const ARTICLE_COMMENTS_PAGE_SIZE = 50
|
||||
const MAX_COMMENT_LEVEL = 6
|
||||
|
||||
export const CommentsTree = (props: { shoutSlug: string; shoutId: number }) => {
|
||||
type Props = {
|
||||
commentAuthors: Author[]
|
||||
shoutSlug: string
|
||||
shoutId: number
|
||||
}
|
||||
|
||||
export const CommentsTree = (props: Props) => {
|
||||
const [getCommentsPage, setCommentsPage] = createSignal(0)
|
||||
const [commentsOrder, setCommentsOrder] = createSignal<'rating' | 'createdAt'>('createdAt')
|
||||
const [isCommentsLoading, setIsCommentsLoading] = createSignal(false)
|
||||
|
@ -47,11 +54,9 @@ export const CommentsTree = (props: { shoutSlug: string; shoutId: number }) => {
|
|||
}
|
||||
onMount(async () => await loadMore())
|
||||
|
||||
const [loading, setLoading] = createSignal<boolean>(false)
|
||||
const [errorMessage, setErrorMessage] = createSignal<string | null>(null)
|
||||
const [submitted, setSubmitted] = createSignal<boolean>(false)
|
||||
const handleSubmitComment = async (value) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
await createReaction(
|
||||
{
|
||||
kind: ReactionKind.Comment,
|
||||
|
@ -64,12 +69,12 @@ export const CommentsTree = (props: { shoutSlug: string; shoutId: number }) => {
|
|||
slug: session().user.slug
|
||||
}
|
||||
)
|
||||
setLoading(false)
|
||||
setSubmitted(true)
|
||||
} catch (error) {
|
||||
setErrorMessage(t('Something went wrong, please try again'))
|
||||
console.error('[handleCreate reaction]:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Show when={!isCommentsLoading()} fallback={<Loading />}>
|
||||
|
@ -109,20 +114,25 @@ export const CommentsTree = (props: { shoutSlug: string; shoutId: number }) => {
|
|||
.reverse()
|
||||
.filter((r) => !r.replyTo)}
|
||||
>
|
||||
{(reaction) => <Comment reactions={reactions()} comment={reaction} />}
|
||||
{(reaction) => (
|
||||
<Comment
|
||||
isArticleAuthor={Boolean(props.commentAuthors.some((a) => a.slug === session()?.user.slug))}
|
||||
reactions={reactions()}
|
||||
comment={reaction}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
<Show when={isLoadMoreButtonVisible()}>
|
||||
<button onClick={loadMore}>{t('Load more')}</button>
|
||||
</Show>
|
||||
<GrowingTextarea
|
||||
placeholder={t('Write comment')}
|
||||
submitButtonText={t('Send')}
|
||||
cancelButtonText={t('cancel')}
|
||||
submit={(value) => handleSubmitComment(value)}
|
||||
loading={loading()}
|
||||
errorMessage={errorMessage()}
|
||||
/>
|
||||
<ShowOnlyOnClient>
|
||||
<CommentEditor
|
||||
initialValue={t('Write a comment...')}
|
||||
clear={submitted()}
|
||||
onSubmit={(value) => handleSubmitComment(value)}
|
||||
/>
|
||||
</ShowOnlyOnClient>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -224,7 +224,11 @@ export const FullArticle = (props: ArticleProps) => {
|
|||
)}
|
||||
</For>
|
||||
</div>
|
||||
<CommentsTree shoutSlug={props.article?.slug} shoutId={props.article?.id} />
|
||||
<CommentsTree
|
||||
shoutId={props.article?.id}
|
||||
shoutSlug={props.article?.slug}
|
||||
commentAuthors={props.article?.authors}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -35,7 +35,7 @@ const CreateModalContent = (props: Props) => {
|
|||
})
|
||||
})
|
||||
if (usersId().length > 1 && theme().length === 1) {
|
||||
setTheme(t('group_chat'))
|
||||
setTheme(t('Group Chat'))
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ import GroupDialogAvatar from './GroupDialogAvatar'
|
|||
import formattedTime from '../../utils/formatDateTime'
|
||||
import { clsx } from 'clsx'
|
||||
import styles from './DialogCard.module.scss'
|
||||
import { t } from '../../utils/intl'
|
||||
|
||||
type DialogProps = {
|
||||
online?: boolean
|
||||
message?: string
|
||||
counter?: number
|
||||
title?: string
|
||||
ownId: number
|
||||
members: ChatMember[]
|
||||
onClick?: () => void
|
||||
|
@ -23,6 +23,7 @@ const DialogCard = (props: DialogProps) => {
|
|||
const companions = createMemo(
|
||||
() => props.members && props.members.filter((member) => member.id !== props.ownId)
|
||||
)
|
||||
|
||||
const names = createMemo(() =>
|
||||
companions()
|
||||
?.map((companion) => companion.name)
|
||||
|
@ -40,14 +41,16 @@ const DialogCard = (props: DialogProps) => {
|
|||
onClick={props.onClick}
|
||||
>
|
||||
<div class={styles.avatar}>
|
||||
<Switch fallback={<DialogAvatar name={props.members[0].name} url={props.members[0].userpic} />}>
|
||||
<Switch fallback={<DialogAvatar name={props.members[0].slug} url={props.members[0].userpic} />}>
|
||||
<Match when={props.members.length >= 3}>
|
||||
<GroupDialogAvatar users={props.members} />
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<div class={styles.row}>
|
||||
<div class={styles.name}>{props.title}</div>
|
||||
<div class={styles.name}>
|
||||
{companions()?.length > 1 ? t('Group Chat') : companions()[0]?.name}
|
||||
</div>
|
||||
<div class={styles.message}>
|
||||
<Switch>
|
||||
<Match when={props.message && !props.isChatHeader}>{props.message}</Match>
|
||||
|
|
|
@ -9,7 +9,7 @@ import MessagesFallback from '../Inbox/MessagesFallback'
|
|||
import QuotedMessage from '../Inbox/QuotedMessage'
|
||||
import { Icon } from '../_shared/Icon'
|
||||
import { useSession } from '../../context/session'
|
||||
import { loadMessages, loadRecipients } from '../../stores/inbox'
|
||||
import { loadRecipients } from '../../stores/inbox'
|
||||
import { t } from '../../utils/intl'
|
||||
import { Modal } from '../Nav/Modal'
|
||||
import { showModal } from '../../stores/ui'
|
||||
|
@ -155,7 +155,7 @@ export const InboxView = () => {
|
|||
return messages().find((message) => message.id === messageId)
|
||||
}
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
const handleKeyDown = async (event) => {
|
||||
if (event.keyCode === 13 && event.shiftKey) return
|
||||
if (event.keyCode === 13 && !event.shiftKey && postMessageText().trim().length > 0) {
|
||||
event.preventDefault()
|
||||
|
@ -217,7 +217,6 @@ export const InboxView = () => {
|
|||
<DialogCard
|
||||
onClick={() => handleOpenChat(chat)}
|
||||
isOpened={chat.id === currentDialog()?.id}
|
||||
title={chat.title || chat.members[0].name}
|
||||
members={chat.members}
|
||||
ownId={currentUserId()}
|
||||
lastUpdate={chat.updatedAt}
|
||||
|
|
91
src/components/_shared/CommentEditor/CommentEditor.tsx
Normal file
91
src/components/_shared/CommentEditor/CommentEditor.tsx
Normal file
|
@ -0,0 +1,91 @@
|
|||
import styles from './styles/CommentEditor.module.scss'
|
||||
import './styles/ProseMirrorOverrides.scss'
|
||||
import { clsx } from 'clsx'
|
||||
import Button from '../Button'
|
||||
import { createEffect, createMemo, onMount } from 'solid-js'
|
||||
import { t } from '../../../utils/intl'
|
||||
//ProseMirror deps
|
||||
import { schema } from './schema'
|
||||
import { EditorState } from 'prosemirror-state'
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
import { DOMSerializer } from 'prosemirror-model'
|
||||
import { renderGrouped } from 'prosemirror-menu'
|
||||
import { buildMenuItems } from './menu'
|
||||
import { keymap } from 'prosemirror-keymap'
|
||||
import { baseKeymap } from 'prosemirror-commands'
|
||||
import { customKeymap } from '../../EditorNew/prosemirror/plugins/customKeymap'
|
||||
import { placeholder } from '../../EditorNew/prosemirror/plugins/placeholder'
|
||||
import { undo, redo, history } from 'prosemirror-history'
|
||||
|
||||
type Props = {
|
||||
initialValue: string
|
||||
onSubmit: (value: string) => void
|
||||
clear?: boolean
|
||||
}
|
||||
|
||||
const htmlContainer = typeof document === 'undefined' ? null : document.createElement('div')
|
||||
const getHtml = (state: EditorState) => {
|
||||
const fragment = DOMSerializer.fromSchema(schema).serializeFragment(state.doc.content)
|
||||
htmlContainer.replaceChildren(fragment)
|
||||
return htmlContainer.innerHTML
|
||||
}
|
||||
|
||||
const CommentEditor = (props: Props) => {
|
||||
const editorElRef: { current: HTMLDivElement } = { current: null }
|
||||
const menuElRef: { current: HTMLDivElement } = { current: null }
|
||||
const editorViewRef: { current: EditorView } = { current: null }
|
||||
const initEditor = () => {
|
||||
editorViewRef.current = new EditorView(editorElRef.current, {
|
||||
state: EditorState.create({
|
||||
schema,
|
||||
plugins: [
|
||||
history(),
|
||||
customKeymap(),
|
||||
placeholder(props.initialValue),
|
||||
keymap({ 'Mod-z': undo, 'Mod-y': redo }),
|
||||
keymap(baseKeymap)
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
initEditor()
|
||||
const { dom } = renderGrouped(editorViewRef.current, buildMenuItems(schema))
|
||||
menuElRef.current.appendChild(dom)
|
||||
})
|
||||
|
||||
const handleSubmitButtonClick = () => {
|
||||
props.onSubmit(getHtml(editorViewRef.current.state))
|
||||
}
|
||||
|
||||
const clearEditor = () => {
|
||||
editorViewRef.current.destroy()
|
||||
initEditor()
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
if (props.clear) clearEditor()
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<div class={styles.commentEditor}>
|
||||
<div
|
||||
class={clsx('ProseMirrorOverrides', styles.textarea)}
|
||||
ref={(el) => (editorElRef.current = el)}
|
||||
/>
|
||||
<div class={styles.actions}>
|
||||
<div class={styles.menu} ref={(el) => (menuElRef.current = el)} />
|
||||
<div class={styles.buttons}>
|
||||
<Button value={t('Send')} variant="primary" onClick={handleSubmitButtonClick} />
|
||||
<Button value="Cancel" variant="secondary" onClick={clearEditor} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class={styles.helpText}>{'"Cmd-Z": Undo, "Cmd-Y": Redo'}</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CommentEditor
|
1
src/components/_shared/CommentEditor/index.ts
Normal file
1
src/components/_shared/CommentEditor/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { default } from './CommentEditor'
|
72
src/components/_shared/CommentEditor/menu/index.ts
Normal file
72
src/components/_shared/CommentEditor/menu/index.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { blockTypeItem, icons, MenuItem, wrapItem } from 'prosemirror-menu'
|
||||
import { toggleMark } from 'prosemirror-commands'
|
||||
|
||||
const markActive = (state, type) => {
|
||||
const { from, $from, to, empty } = state.selection
|
||||
|
||||
if (empty) return type.isInSet(state.storedMarks || $from.marks())
|
||||
|
||||
return state.doc.rangeHasMark(from, to, type)
|
||||
}
|
||||
|
||||
const cmdItem = (cmd, options) => {
|
||||
const passedOptions = {
|
||||
label: options.title,
|
||||
run: cmd
|
||||
}
|
||||
|
||||
for (const prop in options) passedOptions[prop] = options[prop]
|
||||
|
||||
if ((!options.enable || options.enable === true) && !options.select) {
|
||||
passedOptions[options.enable ? 'enable' : 'select'] = (state) => cmd(state)
|
||||
}
|
||||
|
||||
return new MenuItem(passedOptions)
|
||||
}
|
||||
|
||||
const markItem = (markType, options) => {
|
||||
const passedOptions = {
|
||||
active(state) {
|
||||
return markActive(state, markType)
|
||||
},
|
||||
enable: true
|
||||
}
|
||||
|
||||
for (const prop in options) passedOptions[prop] = options[prop]
|
||||
|
||||
return cmdItem(toggleMark(markType), passedOptions)
|
||||
}
|
||||
|
||||
//TODO: вывести тип для схемы
|
||||
export const buildMenuItems = (schema) => {
|
||||
const toggleStrong = markItem(schema.marks.strong, {
|
||||
title: 'Toggle strong style',
|
||||
icon: {
|
||||
width: 14,
|
||||
height: 16,
|
||||
path: 'M9.82857 7.76C10.9371 6.99429 11.7143 5.73714 11.7143 4.57143C11.7143 1.98857 9.71428 0 7.14286 0H0V16H8.04571C10.4343 16 12.2857 14.0571 12.2857 11.6686C12.2857 9.93143 11.3029 8.44571 9.82857 7.76ZM3.42799 2.85708H6.85656C7.80513 2.85708 8.57085 3.6228 8.57085 4.57137C8.57085 5.51994 7.80513 6.28565 6.85656 6.28565H3.42799V2.85708ZM3.42799 13.1429H7.42799C8.37656 13.1429 9.14228 12.3772 9.14228 11.4286C9.14228 10.4801 8.37656 9.71434 7.42799 9.71434H3.42799V13.1429Z'
|
||||
}
|
||||
})
|
||||
|
||||
const toggleEm = markItem(schema.marks.em, {
|
||||
title: 'Toggle emphasis',
|
||||
icon: {
|
||||
width: 14,
|
||||
height: 16,
|
||||
path: 'M4.39216 0V3.42857H6.81882L3.06353 12.5714H0V16H8.78431V12.5714H6.35765L10.1129 3.42857H13.1765V0H4.39216Z'
|
||||
}
|
||||
})
|
||||
|
||||
// const toggleLink = linkItem(schema.marks.link)
|
||||
|
||||
// const insertImage = insertImageItem(schema.nodes.image)
|
||||
|
||||
const wrapBlockQuote = wrapItem(schema.nodes.blockquote, {
|
||||
title: 'Wrap in block quote',
|
||||
icon: icons.blockquote
|
||||
})
|
||||
|
||||
const inlineMenu = [toggleStrong, toggleEm, wrapBlockQuote]
|
||||
|
||||
return [inlineMenu]
|
||||
}
|
21
src/components/_shared/CommentEditor/plugins/placeholder.ts
Normal file
21
src/components/_shared/CommentEditor/plugins/placeholder.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Plugin } from 'prosemirror-state'
|
||||
import { DecorationSet, Decoration } from 'prosemirror-view'
|
||||
|
||||
export const placeholder = (text: string): Plugin =>
|
||||
new Plugin({
|
||||
props: {
|
||||
decorations(state) {
|
||||
const { doc } = state
|
||||
|
||||
if (doc.childCount > 1 || !doc.firstChild.isTextblock || doc.firstChild.content.size > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.setAttribute('contenteditable', 'false')
|
||||
div.textContent = text
|
||||
|
||||
return DecorationSet.create(doc, [Decoration.widget(1, div)])
|
||||
}
|
||||
}
|
||||
})
|
43
src/components/_shared/CommentEditor/schema.ts
Normal file
43
src/components/_shared/CommentEditor/schema.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { Schema } from 'prosemirror-model'
|
||||
|
||||
export const schema = new Schema({
|
||||
nodes: {
|
||||
doc: {
|
||||
content: 'block+'
|
||||
},
|
||||
text: {
|
||||
group: 'inline',
|
||||
inline: true
|
||||
},
|
||||
paragraph: {
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
toDOM: function toDOM(node) {
|
||||
return ['p', { class: 'paragraph' }, 0]
|
||||
}
|
||||
},
|
||||
blockquote: {
|
||||
content: 'block+',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
parseDOM: [{ tag: 'blockquote' }],
|
||||
toDOM() {
|
||||
return ['blockquote', 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
marks: {
|
||||
strong: {
|
||||
toDOM() {
|
||||
return ['strong', 0]
|
||||
},
|
||||
parseDOM: [{ tag: 'strong' }, { tag: 'b' }, { style: 'font-weight=bold' }]
|
||||
},
|
||||
em: {
|
||||
toDOM() {
|
||||
return ['em', 0]
|
||||
},
|
||||
parseDOM: [{ tag: 'em' }, { tag: 'i' }, { style: 'font-style=italic' }]
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,32 @@
|
|||
.commentEditor {
|
||||
border: 2px solid #e8e8e8;
|
||||
border-radius: 8px;
|
||||
padding: 0 16px 16px;
|
||||
|
||||
.textarea {
|
||||
min-height: 1em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.menu,
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.buttons {
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.helpText {
|
||||
font-size: 12px;
|
||||
color: #696969;
|
||||
margin: 12px 0;
|
||||
font-style: italic;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
.ProseMirrorOverrides > .ProseMirror {
|
||||
.paragraph {
|
||||
font-size: 15px;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding-left: 10px;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
color: #9fa1a7;
|
||||
border-left: 2px solid #696969;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default } from './GrowingTextarea'
|
|
@ -1,4 +1,4 @@
|
|||
import { createContext, createSignal, useContext } from 'solid-js'
|
||||
import { createContext, createEffect, createSignal, useContext } from 'solid-js'
|
||||
import type { Accessor, JSX } from 'solid-js'
|
||||
// import { createChatClient } from '../graphql/privateGraphQLClient'
|
||||
import type { Chat, Message, MutationCreateMessageArgs } from '../graphql/types.gen'
|
||||
|
|
|
@ -6,6 +6,10 @@ export default gql`
|
|||
error
|
||||
chat {
|
||||
id
|
||||
members {
|
||||
id
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ export default gql`
|
|||
id
|
||||
slug
|
||||
userpic
|
||||
online
|
||||
}
|
||||
error
|
||||
}
|
||||
|
|
|
@ -84,14 +84,6 @@ export type ChatMember = {
|
|||
userpic?: Maybe<Scalars['String']>
|
||||
}
|
||||
|
||||
export type Collab = {
|
||||
authors: Array<Maybe<Scalars['String']>>
|
||||
chat?: Maybe<Chat>
|
||||
createdAt: Scalars['Int']
|
||||
invites?: Maybe<Array<Maybe<Scalars['String']>>>
|
||||
shout?: Maybe<Shout>
|
||||
}
|
||||
|
||||
export type Collection = {
|
||||
amount?: Maybe<Scalars['Int']>
|
||||
createdAt: Scalars['DateTime']
|
||||
|
@ -113,6 +105,30 @@ export type Community = {
|
|||
slug: Scalars['String']
|
||||
}
|
||||
|
||||
export type DraftCollab = {
|
||||
authors: Array<Maybe<Scalars['Int']>>
|
||||
body?: Maybe<Scalars['String']>
|
||||
chat?: Maybe<Chat>
|
||||
cover?: Maybe<Scalars['String']>
|
||||
createdAt: Scalars['Int']
|
||||
layout?: Maybe<Scalars['String']>
|
||||
slug?: Maybe<Scalars['String']>
|
||||
subtitle?: Maybe<Scalars['String']>
|
||||
title?: Maybe<Scalars['String']>
|
||||
topics?: Maybe<Array<Maybe<Scalars['String']>>>
|
||||
updatedAt?: Maybe<Scalars['Int']>
|
||||
}
|
||||
|
||||
export type DraftInput = {
|
||||
authors?: InputMaybe<Array<InputMaybe<Scalars['Int']>>>
|
||||
body?: InputMaybe<Scalars['String']>
|
||||
cover?: InputMaybe<Scalars['String']>
|
||||
slug?: InputMaybe<Scalars['String']>
|
||||
subtitle?: InputMaybe<Scalars['String']>
|
||||
title?: InputMaybe<Scalars['String']>
|
||||
topics?: InputMaybe<Array<InputMaybe<Scalars['Int']>>>
|
||||
}
|
||||
|
||||
export enum FollowingEntity {
|
||||
Author = 'AUTHOR',
|
||||
Community = 'COMMUNITY',
|
||||
|
@ -167,28 +183,30 @@ export type MessagesBy = {
|
|||
}
|
||||
|
||||
export type Mutation = {
|
||||
acceptCoauthor: Result
|
||||
confirmEmail: AuthResult
|
||||
createChat: Result
|
||||
createDraft: Result
|
||||
createMessage: Result
|
||||
createReaction: Result
|
||||
createShout: Result
|
||||
createTopic: Result
|
||||
deleteChat: Result
|
||||
deleteDraft: Result
|
||||
deleteMessage: Result
|
||||
deleteReaction: Result
|
||||
deleteShout: Result
|
||||
destroyTopic: Result
|
||||
follow: Result
|
||||
getSession: AuthResult
|
||||
inviteCoauthor: Result
|
||||
inviteAccept: Result
|
||||
inviteAuthor: Result
|
||||
markAsRead: Result
|
||||
rateUser: Result
|
||||
registerUser: AuthResult
|
||||
removeCoauthor: Result
|
||||
sendLink: Result
|
||||
unfollow: Result
|
||||
updateChat: Result
|
||||
updateDraft: Result
|
||||
updateMessage: Result
|
||||
updateOnlineStatus: Result
|
||||
updateProfile: Result
|
||||
|
@ -197,10 +215,6 @@ export type Mutation = {
|
|||
updateTopic: Result
|
||||
}
|
||||
|
||||
export type MutationAcceptCoauthorArgs = {
|
||||
shout: Scalars['Int']
|
||||
}
|
||||
|
||||
export type MutationConfirmEmailArgs = {
|
||||
token: Scalars['String']
|
||||
}
|
||||
|
@ -210,6 +224,10 @@ export type MutationCreateChatArgs = {
|
|||
title?: InputMaybe<Scalars['String']>
|
||||
}
|
||||
|
||||
export type MutationCreateDraftArgs = {
|
||||
draft: DraftInput
|
||||
}
|
||||
|
||||
export type MutationCreateMessageArgs = {
|
||||
body: Scalars['String']
|
||||
chat: Scalars['String']
|
||||
|
@ -232,6 +250,10 @@ export type MutationDeleteChatArgs = {
|
|||
chatId: Scalars['String']
|
||||
}
|
||||
|
||||
export type MutationDeleteDraftArgs = {
|
||||
draft: Scalars['Int']
|
||||
}
|
||||
|
||||
export type MutationDeleteMessageArgs = {
|
||||
chatId: Scalars['String']
|
||||
id: Scalars['Int']
|
||||
|
@ -254,9 +276,13 @@ export type MutationFollowArgs = {
|
|||
what: FollowingEntity
|
||||
}
|
||||
|
||||
export type MutationInviteCoauthorArgs = {
|
||||
author: Scalars['String']
|
||||
shout: Scalars['Int']
|
||||
export type MutationInviteAcceptArgs = {
|
||||
draft: Scalars['Int']
|
||||
}
|
||||
|
||||
export type MutationInviteAuthorArgs = {
|
||||
author: Scalars['Int']
|
||||
draft: Scalars['Int']
|
||||
}
|
||||
|
||||
export type MutationMarkAsReadArgs = {
|
||||
|
@ -275,11 +301,6 @@ export type MutationRegisterUserArgs = {
|
|||
password?: InputMaybe<Scalars['String']>
|
||||
}
|
||||
|
||||
export type MutationRemoveCoauthorArgs = {
|
||||
author: Scalars['String']
|
||||
shout: Scalars['Int']
|
||||
}
|
||||
|
||||
export type MutationSendLinkArgs = {
|
||||
email: Scalars['String']
|
||||
lang?: InputMaybe<Scalars['String']>
|
||||
|
@ -295,6 +316,10 @@ export type MutationUpdateChatArgs = {
|
|||
chat: ChatInput
|
||||
}
|
||||
|
||||
export type MutationUpdateDraftArgs = {
|
||||
draft: DraftInput
|
||||
}
|
||||
|
||||
export type MutationUpdateMessageArgs = {
|
||||
body: Scalars['String']
|
||||
chatId: Scalars['String']
|
||||
|
@ -345,11 +370,11 @@ export type ProfileInput = {
|
|||
export type Query = {
|
||||
authorsAll: Array<Maybe<Author>>
|
||||
getAuthor?: Maybe<User>
|
||||
getCollabs: Array<Maybe<Collab>>
|
||||
getTopic?: Maybe<Topic>
|
||||
isEmailUsed: Scalars['Boolean']
|
||||
loadAuthorsBy: Array<Maybe<Author>>
|
||||
loadChats: Result
|
||||
loadDrafts: Array<Maybe<DraftCollab>>
|
||||
loadMessagesBy: Result
|
||||
loadReactionsBy: Array<Maybe<Reaction>>
|
||||
loadRecipients: Result
|
||||
|
@ -545,6 +570,7 @@ export type Result = {
|
|||
chats?: Maybe<Array<Maybe<Chat>>>
|
||||
communities?: Maybe<Array<Maybe<Community>>>
|
||||
community?: Maybe<Community>
|
||||
drafts?: Maybe<Array<Maybe<DraftCollab>>>
|
||||
error?: Maybe<Scalars['String']>
|
||||
members?: Maybe<Array<Maybe<ChatMember>>>
|
||||
message?: Maybe<Message>
|
||||
|
|
|
@ -166,6 +166,7 @@
|
|||
"actions": "действия",
|
||||
"all topics": "все темы",
|
||||
"author": "автор",
|
||||
"Author": "Автор",
|
||||
"authors": "авторы",
|
||||
"collections": "коллекции",
|
||||
"community": "сообщество",
|
||||
|
@ -192,7 +193,7 @@
|
|||
"discourse_theme": "Тема дискурса",
|
||||
"cancel": "Отмена",
|
||||
"Send": "Отправить",
|
||||
"group_chat": "Общий чат",
|
||||
"Group Chat": "Общий чат",
|
||||
"Choose who you want to write to": "Выберите кому хотите написать",
|
||||
"Start conversation": "Начать беседу",
|
||||
"Profile settings": "Настройки профиля",
|
||||
|
@ -218,6 +219,7 @@
|
|||
"It does not look like url": "Это не похоже на ссылку",
|
||||
"Something went wrong, please try again": "Что-то пошло не так, попробуйте еще раз",
|
||||
"To write a comment, you must": "Чтобы написать комментарий, необходимо",
|
||||
"Write a comment...": "Написать комментарий..."
|
||||
"Add comment": "Комментировать",
|
||||
"My subscriptions": "Подписки"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Reaction, ReactionInput, User } from '../../graphql/types.gen'
|
||||
import type { Reaction, ReactionInput } from '../../graphql/types.gen'
|
||||
import { apiClient } from '../../utils/apiClient'
|
||||
import { createSignal } from 'solid-js'
|
||||
// TODO: import { roomConnect } from '../../utils/p2p'
|
||||
|
|
|
@ -296,12 +296,12 @@ export const apiClient = {
|
|||
|
||||
createMessage: async (options: MutationCreateMessageArgs) => {
|
||||
const resp = await privateGraphQLClient.mutation(createMessage, options).toPromise()
|
||||
return resp.data.createMessage
|
||||
return resp.data.createMessage.message
|
||||
},
|
||||
|
||||
getChatMessages: async (options: QueryLoadMessagesByArgs) => {
|
||||
const resp = await privateGraphQLClient.query(chatMessagesLoadBy, options).toPromise()
|
||||
return resp.data.loadChat
|
||||
return resp.data.loadMessagesBy.messages
|
||||
},
|
||||
getRecipients: async (options: QueryLoadRecipientsArgs) => {
|
||||
const resp = await privateGraphQLClient.query(loadRecipients, options).toPromise()
|
||||
|
|
Loading…
Reference in New Issue
Block a user