From 01f33413eb4c2a0f05eebef94298351f5c9a61a5 Mon Sep 17 00:00:00 2001 From: bniwredyc Date: Wed, 10 May 2023 22:20:53 +0200 Subject: [PATCH 1/3] main topic select, save topics WiP --- package-lock.json | 14 ++-- package.json | 2 +- .../TopicSelect/TopicSelect.module.scss | 13 +++ .../Editor/TopicSelect/TopicSelect.tsx | 45 ++++++++++- src/components/Views/Edit.module.scss | 4 + src/components/Views/Edit.tsx | 39 +++++++-- src/context/editor.tsx | 79 ++++++++++--------- src/graphql/query/topics-all.ts | 1 + src/graphql/types.gen.ts | 7 +- src/pages/edit.page.tsx | 11 ++- src/utils/slugify.ts | 7 ++ 11 files changed, 165 insertions(+), 57 deletions(-) create mode 100644 src/components/Editor/TopicSelect/TopicSelect.module.scss create mode 100644 src/utils/slugify.ts diff --git a/package-lock.json b/package-lock.json index 5253f9c8..06b8997b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "@solid-primitives/storage": "1.3.9", "@solid-primitives/upload": "0.0.110", "@solidjs/meta": "0.28.2", - "@thisbeyond/solid-select": "0.13.0", + "@thisbeyond/solid-select": "0.14.0", "@tiptap/core": "2.0.3", "@tiptap/extension-blockquote": "2.0.3", "@tiptap/extension-bold": "2.0.3", @@ -5745,9 +5745,9 @@ } }, "node_modules/@thisbeyond/solid-select": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.13.0.tgz", - "integrity": "sha512-eION+Xf8TGLs1NZrvRo1NRKOl4plYMbY7UswHhh5bEUY8oMltjrBhUWF0hzaFViEc1zZpkCQyafaD89iofG6Tg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz", + "integrity": "sha512-ecq4U3Vnc/nJbU84ARuPg2scNuYt994ljF5AmBlzuZW87x43mWiGJ5hEWufIJJMpDT6CcnCIx/xbrdDkaDEHQw==", "dev": true, "peerDependencies": { "solid-js": "^1.5" @@ -24828,9 +24828,9 @@ "requires": {} }, "@thisbeyond/solid-select": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.13.0.tgz", - "integrity": "sha512-eION+Xf8TGLs1NZrvRo1NRKOl4plYMbY7UswHhh5bEUY8oMltjrBhUWF0hzaFViEc1zZpkCQyafaD89iofG6Tg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@thisbeyond/solid-select/-/solid-select-0.14.0.tgz", + "integrity": "sha512-ecq4U3Vnc/nJbU84ARuPg2scNuYt994ljF5AmBlzuZW87x43mWiGJ5hEWufIJJMpDT6CcnCIx/xbrdDkaDEHQw==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index 0bd48e79..a5cb701f 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@solid-primitives/storage": "1.3.9", "@solid-primitives/upload": "0.0.110", "@solidjs/meta": "0.28.2", - "@thisbeyond/solid-select": "0.13.0", + "@thisbeyond/solid-select": "0.14.0", "@tiptap/core": "2.0.3", "@tiptap/extension-blockquote": "2.0.3", "@tiptap/extension-bold": "2.0.3", diff --git a/src/components/Editor/TopicSelect/TopicSelect.module.scss b/src/components/Editor/TopicSelect/TopicSelect.module.scss new file mode 100644 index 00000000..2cc41d65 --- /dev/null +++ b/src/components/Editor/TopicSelect/TopicSelect.module.scss @@ -0,0 +1,13 @@ +.selectedItem { + cursor: pointer; + + &.mainTopic { + cursor: default; + background: #000; + color: #ccc; + + + :global(.solid-select-multi-value-remove) { + background: #000; + } + } +} diff --git a/src/components/Editor/TopicSelect/TopicSelect.tsx b/src/components/Editor/TopicSelect/TopicSelect.tsx index 924a4e40..b84984aa 100644 --- a/src/components/Editor/TopicSelect/TopicSelect.tsx +++ b/src/components/Editor/TopicSelect/TopicSelect.tsx @@ -3,32 +3,73 @@ import { createOptions, Select } from '@thisbeyond/solid-select' import { useLocalize } from '../../../context/localize' import '@thisbeyond/solid-select/style.css' import './TopicSelect.scss' +import styles from './TopicSelect.module.scss' +import { clsx } from 'clsx' +import { createSignal } from 'solid-js' +import { slugify } from '../../../utils/slugify' type TopicSelectProps = { topics: Topic[] selectedTopics: Topic[] onChange: (selectedTopics: Topic[]) => void + mainTopic?: Topic + onMainTopicChange: (mainTopic: Topic) => void } export const TopicSelect = (props: TopicSelectProps) => { const { t } = useLocalize() + const [isDisabled, setIsDisabled] = createSignal(false) + + const createValue = (title): Topic => { + const minId = Math.min(...props.selectedTopics.map((topic) => topic.id)) + const id = minId < 0 ? minId - 1 : -2 + return { id, title, slug: slugify(title) } + } + const selectProps = createOptions(props.topics, { key: 'title', disable: (topic) => { - // console.log({ selectedTopics: clone(props.selectedTopics) }) return props.selectedTopics.some((selectedTopic) => selectedTopic.slug === topic.slug) - } + }, + createable: createValue }) const handleChange = (selectedTopics: Topic[]) => { props.onChange(selectedTopics) } + const handleSelectedItemClick = (topic: Topic) => { + setIsDisabled(true) + props.onMainTopicChange(topic) + setIsDisabled(false) + } + + const format = (item, type) => { + if (type === 'option') { + return item.label + } + + const isMainTopic = item.id === props.mainTopic.id + + return ( +
handleSelectedItemClick(item)} + > + {item.title} +
+ ) + } + return ( */} + +
{formErrors.selectedTopics}
+
{/*

Соавторы

*/} {/*

У каждого соавтора можно добавить роль

*/} diff --git a/src/context/editor.tsx b/src/context/editor.tsx index 221b912a..9f5a3a04 100644 --- a/src/context/editor.tsx +++ b/src/context/editor.tsx @@ -5,9 +5,9 @@ import { Topic } from '../graphql/types.gen' import { apiClient } from '../utils/apiClient' import { useLocalize } from './localize' import { useSnackbar } from './snackbar' -import { translit } from '../utils/ru2en' import { openPage } from '@nanostores/router' import { router, useRouter } from '../stores/router' +import { slugify } from '../utils/slugify' type WordCounter = { characters: number @@ -20,7 +20,7 @@ type ShoutForm = { title: string subtitle: string selectedTopics: Topic[] - mainTopic: string + mainTopic?: Topic body: string coverImageUrl: string } @@ -29,7 +29,7 @@ type EditorContextType = { isEditorPanelVisible: Accessor wordCounter: Accessor form: ShoutForm - formErrors: Partial + formErrors: Record actions: { saveShout: () => Promise publishShout: () => Promise @@ -38,7 +38,7 @@ type EditorContextType = { toggleEditorPanel: () => void countWords: (value: WordCounter) => void setForm: SetStoreFunction - setFormErrors: SetStoreFunction> + setFormErrors: SetStoreFunction> } } @@ -60,7 +60,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => { const [isEditorPanelVisible, setIsEditorPanelVisible] = createSignal(false) const [form, setForm] = createStore(null) - const [formErrors, setFormErrors] = createStore>(null) + const [formErrors, setFormErrors] = createStore>(null) const [wordCounter, setWordCounter] = createSignal({ characters: 0, @@ -79,31 +79,48 @@ export const EditorProvider = (props: { children: JSX.Element }) => { return true } + const validateSettings = () => { + if (form.selectedTopics.length === 0) { + setFormErrors('selectedTopics', t('Required')) + return false + } + + return true + } + + const updateShout = async ({ publish }: { publish: boolean }) => { + return apiClient.updateArticle({ + shoutId: form.shoutId, + shoutInput: { + body: form.body, + topics: form.selectedTopics, + // authors?: InputMaybe>> + // community?: InputMaybe + mainTopic: form.mainTopic, + slug: form.slug, + subtitle: form.subtitle, + title: form.title, + cover: form.coverImageUrl + }, + publish + }) + } + const saveShout = async () => { if (isEditorPanelVisible()) { toggleEditorPanel() } - if (!validate()) { + if (page().route === 'edit' && !validate()) { + return + } + + if (page().route === 'editSettings' && !validateSettings()) { return } try { - const shout = await apiClient.updateArticle({ - shoutId: form.shoutId, - shoutInput: { - body: form.body, - topics: form.selectedTopics.map((topic) => topic.slug), - // authors?: InputMaybe>> - // community?: InputMaybe - mainTopic: form.selectedTopics[0]?.slug || 'society', - slug: form.slug, - subtitle: form.subtitle, - title: form.title, - cover: form.coverImageUrl - }, - publish: false - }) + const shout = await updateShout({ publish: false }) if (shout.visibility === 'owner') { openPage(router, 'drafts') @@ -120,32 +137,20 @@ export const EditorProvider = (props: { children: JSX.Element }) => { if (isEditorPanelVisible()) { toggleEditorPanel() } + if (!validate()) { return } + if (page().route === 'edit') { - const slug = translit(form.title.toLowerCase()).replaceAll(' ', '-') + const slug = slugify(form.title) setForm('slug', slug) openPage(router, 'editSettings', { shoutId: form.shoutId.toString() }) return } try { - await apiClient.updateArticle({ - shoutId: form.shoutId, - shoutInput: { - body: form.body, - topics: form.selectedTopics.map((topic) => topic.slug), - // authors?: InputMaybe>> - // community?: InputMaybe - mainTopic: form.selectedTopics[0]?.slug || 'society', - slug: form.slug, - subtitle: form.subtitle, - title: form.title, - cover: form.coverImageUrl - }, - publish: true - }) + await updateShout({ publish: true }) openPage(router, 'feed') } catch (error) { console.error('[publishShout]', error) diff --git a/src/graphql/query/topics-all.ts b/src/graphql/query/topics-all.ts index d2ab1263..467ac999 100644 --- a/src/graphql/query/topics-all.ts +++ b/src/graphql/query/topics-all.ts @@ -3,6 +3,7 @@ import { gql } from '@urql/core' export default gql` query TopicsAllQuery { topicsAll { + id title body slug diff --git a/src/graphql/types.gen.ts b/src/graphql/types.gen.ts index 37795c5b..9bab98ee 100644 --- a/src/graphql/types.gen.ts +++ b/src/graphql/types.gen.ts @@ -582,11 +582,11 @@ export type ShoutInput = { body?: InputMaybe community?: InputMaybe cover?: InputMaybe - mainTopic?: InputMaybe + mainTopic?: InputMaybe slug?: InputMaybe subtitle?: InputMaybe title?: InputMaybe - topics?: InputMaybe>> + topics?: InputMaybe>> } export type ShoutsFilterBy = { @@ -628,7 +628,6 @@ export type Token = { export type Topic = { body?: Maybe - community: Community id: Scalars['Int'] oid?: Maybe pic?: Maybe @@ -639,7 +638,7 @@ export type Topic = { export type TopicInput = { body?: InputMaybe - community: Scalars['String'] + id?: InputMaybe pic?: InputMaybe slug: Scalars['String'] title?: InputMaybe diff --git a/src/pages/edit.page.tsx b/src/pages/edit.page.tsx index 1d60ac44..a0d66a58 100644 --- a/src/pages/edit.page.tsx +++ b/src/pages/edit.page.tsx @@ -25,7 +25,16 @@ export const EditPage = () => { return ( - + +
+
Давайте авторизуемся
+
+ + } + > }> diff --git a/src/utils/slugify.ts b/src/utils/slugify.ts new file mode 100644 index 00000000..5edb0c94 --- /dev/null +++ b/src/utils/slugify.ts @@ -0,0 +1,7 @@ +import { translit } from './ru2en' + +export const slugify = (text) => { + return translit(text.toLowerCase()) + .replaceAll(/[^\da-z]/g, '') + .replaceAll(' ', '-') +} From 887c7487eece007f7294707d059020d1361d32c2 Mon Sep 17 00:00:00 2001 From: kvakazyambra Date: Wed, 10 May 2023 23:52:11 +0300 Subject: [PATCH 2/3] Fixed main topic style on the article settings page --- .../Editor/TopicSelect/TopicSelect.module.scss | 14 ++++++++++++-- src/pages/profile/Settings.module.scss | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/components/Editor/TopicSelect/TopicSelect.module.scss b/src/components/Editor/TopicSelect/TopicSelect.module.scss index 2cc41d65..a2a15c1b 100644 --- a/src/components/Editor/TopicSelect/TopicSelect.module.scss +++ b/src/components/Editor/TopicSelect/TopicSelect.module.scss @@ -3,11 +3,21 @@ &.mainTopic { cursor: default; - background: #000; - color: #ccc; + &, + :global(.solid-select-multi-value-remove) { + color: #ccc; + } + + &:before { background: #000; + content: ''; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + z-index: -1; } } } diff --git a/src/pages/profile/Settings.module.scss b/src/pages/profile/Settings.module.scss index 24faffb6..3c3302bb 100644 --- a/src/pages/profile/Settings.module.scss +++ b/src/pages/profile/Settings.module.scss @@ -204,9 +204,23 @@ h5 { } :global(.solid-select-multi-value) { + background: none; margin: 0 0.5rem 0.5rem 0; + overflow: hidden; padding-left: 0.8rem; padding-bottom: 0.2rem; + position: relative; + + &:before { + background: rgb(243, 244, 246); + content: ''; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + z-index: -1; + } } :global(.solid-select-multi-value-remove) { From 835e6fcee982d27bac9eeab768e1714bfc531aa5 Mon Sep 17 00:00:00 2001 From: bniwredyc Date: Thu, 11 May 2023 13:06:29 +0200 Subject: [PATCH 3/3] save topics fixes --- .../Editor/TopicSelect/TopicSelect.tsx | 4 +++ src/components/Views/Edit.tsx | 26 +++++++++-------- src/context/editor.tsx | 28 ++++++++++++++----- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/components/Editor/TopicSelect/TopicSelect.tsx b/src/components/Editor/TopicSelect/TopicSelect.tsx index b84984aa..ebab3d12 100644 --- a/src/components/Editor/TopicSelect/TopicSelect.tsx +++ b/src/components/Editor/TopicSelect/TopicSelect.tsx @@ -7,6 +7,7 @@ import styles from './TopicSelect.module.scss' import { clsx } from 'clsx' import { createSignal } from 'solid-js' import { slugify } from '../../../utils/slugify' +import { clone } from '../../../utils/clone' type TopicSelectProps = { topics: Topic[] @@ -64,10 +65,13 @@ export const TopicSelect = (props: TopicSelectProps) => { ) } + const initialValue = clone(props.selectedTopics) + return (