From c9a8c1aa8ec44a721feff9589eda0fd6dc9ade40 Mon Sep 17 00:00:00 2001 From: Untone Date: Thu, 4 Jul 2024 00:25:03 +0300 Subject: [PATCH] routing-structure-fin --- src/components/Discours/Banner.tsx | 4 +- src/components/Discours/Footer.tsx | 18 +- src/components/Discours/Hero.tsx | 4 +- src/components/Feed/Beside.tsx | 4 +- .../Feed/Placeholder/Placeholder.tsx | 10 +- src/components/Nav/AuthModal/index.tsx | 2 +- src/components/Nav/Header/Header.tsx | 26 +- src/components/Nav/HeaderAuth.tsx | 4 +- src/components/Nav/ProfilePopup.tsx | 2 +- src/components/Nav/Topics/Topics.tsx | 2 +- .../ProfileSettings/ProfileSettings.tsx | 3 +- .../ProfileSettings}/Settings.module.scss | 0 src/components/Topic/Full.tsx | 2 +- .../Views/AllAuthors/AllAuthors.tsx | 8 +- src/components/Views/AllTopics/AllTopics.tsx | 8 +- src/components/Views/Author/Author.tsx | 21 +- .../Views/DraftsView/DraftsView.tsx | 47 +--- .../Views/EditView/EditSettingsView.tsx | 204 +++++++++++++++ src/components/Views/EditView/EditView.tsx | 11 +- src/components/Views/Expo/Expo.tsx | 3 +- src/components/Views/Feed/Feed.tsx | 2 +- src/config/routes.ts | 22 +- src/context/editor.tsx | 2 +- src/lib/api/private.ts | 1 + src/pages/about/discussionRules.page.route.ts | 4 - src/pages/about/dogma.page.route.ts | 4 - src/pages/about/dogma.page.tsx | 73 ------ src/pages/about/guide.page.tsx | 247 ------------------ src/pages/about/guilde.page.route.ts | 4 - src/pages/about/help.page.route.ts | 4 - src/pages/about/manifest.page.route.ts | 4 - src/pages/about/partners.page.route.ts | 4 - src/pages/about/principles.page.route.ts | 4 - src/pages/about/principles.page.tsx | 188 ------------- src/pages/about/projects.page.route.ts | 4 - src/pages/about/termsOfUse.page.route.ts | 4 - src/pages/about/thanks.page.route.ts | 4 - src/pages/about/thanks.page.tsx | 95 ------- src/pages/allAuthors.page.route.ts | 4 - src/pages/allAuthors.page.server.ts | 26 -- src/pages/allAuthors.page.tsx | 41 --- src/pages/allTopics.page.route.ts | 4 - src/pages/allTopics.page.server.ts | 16 -- src/pages/allTopics.page.tsx | 17 -- src/pages/article.page.route.ts | 4 - src/pages/article.page.server.ts | 23 -- src/pages/article.page.tsx | 69 ----- src/pages/author.page.route.ts | 4 - src/pages/author.page.server.ts | 29 -- src/pages/author.page.tsx | 51 ---- src/pages/authorAbout.page.route.ts | 4 - src/pages/authorComment.page.route.ts | 4 - src/pages/connect.page.route.ts | 4 - src/pages/connect.page.tsx | 107 -------- src/pages/create.page.route.ts | 4 - src/pages/drafts.page.route.ts | 6 - src/pages/drafts.page.tsx | 15 -- src/pages/edit.page.route.ts | 4 - src/pages/edit.page.tsx | 102 -------- src/pages/editSettings.page.route.ts | 4 - src/pages/expo/expo.page.route.ts | 5 - src/pages/expo/expo.page.server.ts | 22 -- src/pages/expo/expo.page.tsx | 46 ---- src/pages/feed.page.route.ts | 4 - src/pages/feed.page.tsx | 40 --- src/pages/feedMy.page.route.ts | 4 - src/pages/fourOuFour.page.route.ts | 4 - src/pages/fourOuFour.page.tsx | 15 -- src/pages/inbox.page.route.ts | 4 - src/pages/inbox.page.tsx | 32 --- src/pages/index.page.route.ts | 4 - src/pages/index.page.server.ts | 20 -- src/pages/index.page.tsx | 39 --- .../profile/profileSecurity.page.route.ts | 4 - .../profile/profileSettings.page.route.ts | 4 - .../profileSubscriptions.page.route.ts | 4 - src/pages/search.page.route.ts | 4 - src/pages/search.page.server.ts | 17 -- src/pages/search.page.tsx | 42 --- src/pages/topic.page.route.ts | 4 - src/pages/topic.page.server.ts | 30 --- src/pages/topic.page.tsx | 69 ----- src/pages/types.ts | 56 ---- src/routes/[...slug].tsx | 2 +- src/routes/{authors.tsx => author/(all).tsx} | 8 +- src/routes/author/[slug]/[...tab].tsx | 74 ++++++ .../{connect.tsx => connect/(connect).tsx} | 4 +- src/routes/edit/(all).tsx | 31 +++ src/routes/edit/[id]/(draft).tsx | 75 ++++++ src/routes/edit/[id]/settings.tsx | 36 +++ .../create.page.tsx => routes/edit/new.tsx} | 72 ++--- src/routes/expo/[...layout].tsx | 70 +++++ src/routes/feed/[...feed].tsx | 2 +- src/routes/guide/(guide).tsx | 18 +- .../guide/debate.tsx} | 6 +- .../guide/manifest.tsx} | 14 +- .../guide/partners.tsx} | 3 +- src/routes/guide/principles.tsx | 2 +- .../guide/projects.tsx} | 0 .../guide/support.tsx} | 7 +- .../guide/terms.tsx} | 6 +- .../profile/(settings).tsx} | 8 +- .../profile/security.tsx} | 63 ++--- .../profile/subs.tsx} | 2 +- .../{search.tsx => search/(search).tsx} | 12 +- src/routes/{topics.tsx => topic/(all).tsx} | 10 +- src/routes/topic/[...slug].tsx | 2 +- tests/basic-routes.spec.ts | 8 +- tests/basic-routes.test.js | 8 +- 109 files changed, 695 insertions(+), 1916 deletions(-) rename src/{pages/profile => components/ProfileSettings}/Settings.module.scss (100%) create mode 100644 src/components/Views/EditView/EditSettingsView.tsx delete mode 100644 src/pages/about/discussionRules.page.route.ts delete mode 100644 src/pages/about/dogma.page.route.ts delete mode 100644 src/pages/about/dogma.page.tsx delete mode 100644 src/pages/about/guide.page.tsx delete mode 100644 src/pages/about/guilde.page.route.ts delete mode 100644 src/pages/about/help.page.route.ts delete mode 100644 src/pages/about/manifest.page.route.ts delete mode 100644 src/pages/about/partners.page.route.ts delete mode 100644 src/pages/about/principles.page.route.ts delete mode 100644 src/pages/about/principles.page.tsx delete mode 100644 src/pages/about/projects.page.route.ts delete mode 100644 src/pages/about/termsOfUse.page.route.ts delete mode 100644 src/pages/about/thanks.page.route.ts delete mode 100644 src/pages/about/thanks.page.tsx delete mode 100644 src/pages/allAuthors.page.route.ts delete mode 100644 src/pages/allAuthors.page.server.ts delete mode 100644 src/pages/allAuthors.page.tsx delete mode 100644 src/pages/allTopics.page.route.ts delete mode 100644 src/pages/allTopics.page.server.ts delete mode 100644 src/pages/allTopics.page.tsx delete mode 100644 src/pages/article.page.route.ts delete mode 100644 src/pages/article.page.server.ts delete mode 100644 src/pages/article.page.tsx delete mode 100644 src/pages/author.page.route.ts delete mode 100644 src/pages/author.page.server.ts delete mode 100644 src/pages/author.page.tsx delete mode 100644 src/pages/authorAbout.page.route.ts delete mode 100644 src/pages/authorComment.page.route.ts delete mode 100644 src/pages/connect.page.route.ts delete mode 100644 src/pages/connect.page.tsx delete mode 100644 src/pages/create.page.route.ts delete mode 100644 src/pages/drafts.page.route.ts delete mode 100644 src/pages/drafts.page.tsx delete mode 100644 src/pages/edit.page.route.ts delete mode 100644 src/pages/edit.page.tsx delete mode 100644 src/pages/editSettings.page.route.ts delete mode 100644 src/pages/expo/expo.page.route.ts delete mode 100644 src/pages/expo/expo.page.server.ts delete mode 100644 src/pages/expo/expo.page.tsx delete mode 100644 src/pages/feed.page.route.ts delete mode 100644 src/pages/feed.page.tsx delete mode 100644 src/pages/feedMy.page.route.ts delete mode 100644 src/pages/fourOuFour.page.route.ts delete mode 100644 src/pages/fourOuFour.page.tsx delete mode 100644 src/pages/inbox.page.route.ts delete mode 100644 src/pages/inbox.page.tsx delete mode 100644 src/pages/index.page.route.ts delete mode 100644 src/pages/index.page.server.ts delete mode 100644 src/pages/index.page.tsx delete mode 100644 src/pages/profile/profileSecurity.page.route.ts delete mode 100644 src/pages/profile/profileSettings.page.route.ts delete mode 100644 src/pages/profile/profileSubscriptions.page.route.ts delete mode 100644 src/pages/search.page.route.ts delete mode 100644 src/pages/search.page.server.ts delete mode 100644 src/pages/search.page.tsx delete mode 100644 src/pages/topic.page.route.ts delete mode 100644 src/pages/topic.page.server.ts delete mode 100644 src/pages/topic.page.tsx delete mode 100644 src/pages/types.ts rename src/routes/{authors.tsx => author/(all).tsx} (89%) create mode 100644 src/routes/author/[slug]/[...tab].tsx rename src/routes/{connect.tsx => connect/(connect).tsx} (74%) create mode 100644 src/routes/edit/(all).tsx create mode 100644 src/routes/edit/[id]/(draft).tsx create mode 100644 src/routes/edit/[id]/settings.tsx rename src/{pages/create.page.tsx => routes/edit/new.tsx} (53%) create mode 100644 src/routes/expo/[...layout].tsx rename src/{pages/about/discussionRules.page.tsx => routes/guide/debate.tsx} (99%) rename src/{pages/about/manifest.page.tsx => routes/guide/manifest.tsx} (93%) rename src/{pages/about/partners.page.tsx => routes/guide/partners.tsx} (96%) rename src/{pages/about/projects.page.tsx => routes/guide/projects.tsx} (100%) rename src/{pages/about/help.page.tsx => routes/guide/support.tsx} (98%) rename src/{pages/about/termsOfUse.page.tsx => routes/guide/terms.tsx} (99%) rename src/{pages/profile/profileSettings.page.tsx => routes/profile/(settings).tsx} (73%) rename src/{pages/profile/profileSecurity.page.tsx => routes/profile/security.tsx} (89%) rename src/{pages/profile/profileSubscriptions.page.tsx => routes/profile/subs.tsx} (91%) rename src/routes/{search.tsx => search/(search).tsx} (87%) rename src/routes/{topics.tsx => topic/(all).tsx} (77%) diff --git a/src/components/Discours/Banner.tsx b/src/components/Discours/Banner.tsx index a2d819a8..125cf5ca 100644 --- a/src/components/Discours/Banner.tsx +++ b/src/components/Discours/Banner.tsx @@ -16,8 +16,8 @@ export default () => {

{t('Discours exists because of our common effort')}

- {t('Support us')} - {t('Become an author')} + {t('Support us')} + {t('Become an author')} showModal('auth')}> {t('Join the community')} diff --git a/src/components/Discours/Footer.tsx b/src/components/Discours/Footer.tsx index ca20cb74..37c4a424 100644 --- a/src/components/Discours/Footer.tsx +++ b/src/components/Discours/Footer.tsx @@ -25,9 +25,9 @@ export const FooterView = () => { { header: t('About the project'), items: [ - { title: t('Discours Manifest'), slug: '/about/manifest' }, - { title: t('How it works'), slug: '/about/guide' }, - { title: t('Dogma'), slug: '/about/dogma' }, + { title: t('Discours Manifest'), slug: '/guide/manifest' }, + { title: t('How it works'), slug: '/guide' }, + { title: t('Dogma'), slug: '/guide/dogma' }, { title: t('Our principles'), slug: '/guide/principles' }, { title: t('How to write an article'), slug: '/how-to-write-a-good-article' } ] @@ -36,8 +36,8 @@ export const FooterView = () => { header: t('Participating'), items: [ { title: t('Suggest an idea'), slug: '/connect' }, - { title: t('Become an author'), slug: '/create' }, - { title: t('Support Discours'), slug: '/about/help' }, + { title: t('Become an author'), slug: '/edit/new' }, + { title: t('Support Discours'), slug: '/guide/support' }, { title: t('Work with us'), slug: 'https://docs.google.com/forms/d/e/1FAIpQLSeNNvIzKlXElJtkPkYiXl-jQjlvsL9u4-kpnoRjz1O8Wo40xQ/viewform' @@ -47,10 +47,10 @@ export const FooterView = () => { { header: t('Sections'), items: [ - { title: t('Authors'), slug: '/authors' }, + { title: t('Authors'), slug: '/author' }, { title: t('Communities'), slug: '/community' }, - { title: t('Partners'), slug: '/about/partners' }, - { title: t('Special projects'), slug: '/about/projects' }, + { title: t('Partners'), slug: '/guide/partners' }, + { title: t('Special projects'), slug: '/guide/projects' }, { title: lang() === 'ru' ? 'English' : 'Русский', slug: `?lng=${lang() === 'ru' ? 'en' : 'ru'}`, @@ -97,7 +97,7 @@ export const FooterView = () => { 'Independant magazine with an open horizontal cooperation about culture, science and society' )} . {t('Discours')} © 2015–{new Date().getFullYear()}{' '} - {t('Terms of use')} + {t('Terms of use')}

diff --git a/src/components/Discours/Hero.tsx b/src/components/Discours/Hero.tsx index 0d605058..29f32c20 100644 --- a/src/components/Discours/Hero.tsx +++ b/src/components/Discours/Hero.tsx @@ -20,7 +20,7 @@ export default () => { )} /> diff --git a/src/components/Feed/Beside.tsx b/src/components/Feed/Beside.tsx index 8995eec0..7c1e01fe 100644 --- a/src/components/Feed/Beside.tsx +++ b/src/components/Feed/Beside.tsx @@ -48,14 +48,14 @@ export const Beside = (props: Props) => {

{props.title}

- + {t('All authors')} - + {t('All topics')} diff --git a/src/components/Feed/Placeholder/Placeholder.tsx b/src/components/Feed/Placeholder/Placeholder.tsx index 6f608dd3..5d0e6d1d 100644 --- a/src/components/Feed/Placeholder/Placeholder.tsx +++ b/src/components/Feed/Placeholder/Placeholder.tsx @@ -36,14 +36,14 @@ const data: PlaceholderData = { text: 'Placeholder feed', buttonLabelAuthor: 'Popular authors', buttonLabelFeed: 'Create own feed', - href: '/authors?by=followers' + href: '/author?by=followers' }, feedCollaborations: { image: 'placeholder-experts.webp', header: 'Find collaborators', text: 'Placeholder feedCollaborations', buttonLabel: 'Find co-authors', - href: '/authors?by=name' + href: '/author?by=name' }, feedDiscussions: { image: 'placeholder-discussions.webp', @@ -58,7 +58,7 @@ const data: PlaceholderData = { header: 'Join our team of authors', text: 'Join our team of authors text', buttonLabel: 'Create post', - href: '/create', + href: '/edit/new', profileLinks: [ { href: '/how-to-write-a-good-article', @@ -74,11 +74,11 @@ const data: PlaceholderData = { href: '/feed?by=last_comment', profileLinks: [ { - href: '/about/discussion-rules', + href: '/guide/debate', label: 'Discussion rules' }, { - href: '/about/discussion-rules#ban', + href: '/guide/debate#ban', label: 'Block rules' } ] diff --git a/src/components/Nav/AuthModal/index.tsx b/src/components/Nav/AuthModal/index.tsx index 21a33f4d..cc88dff9 100644 --- a/src/components/Nav/AuthModal/index.tsx +++ b/src/components/Nav/AuthModal/index.tsx @@ -82,7 +82,7 @@ export const AuthModal = () => {

{t('By signing up you agree with our')}{' '} { hideModal() }} diff --git a/src/components/Nav/Header/Header.tsx b/src/components/Nav/Header/Header.tsx index cd119219..a95b78d3 100644 --- a/src/components/Nav/Header/Header.tsx +++ b/src/components/Nav/Header/Header.tsx @@ -119,7 +119,7 @@ export const Header = (props: Props) => { requireAuthentication(() => { ev?.preventDefault() - navigate('/create') + navigate('/edit/new') }, 'create') } @@ -216,13 +216,13 @@ export const Header = (props: Props) => {

{t('Participating')}

@@ -280,8 +280,8 @@ export const Header = (props: Props) => { @@ -342,22 +342,22 @@ export const Header = (props: Props) => { >
diff --git a/src/components/Views/AllAuthors/AllAuthors.tsx b/src/components/Views/AllAuthors/AllAuthors.tsx index 2f9d963d..3a818e09 100644 --- a/src/components/Views/AllAuthors/AllAuthors.tsx +++ b/src/components/Views/AllAuthors/AllAuthors.tsx @@ -91,21 +91,21 @@ export const AllAuthors = (props: Props) => { ['view-switcher__item--selected']: !searchParams?.by || searchParams?.by === 'shouts' })} > - {t('By shouts')} + {t('By shouts')}
  • - {t('By popularity')} + {t('By popularity')}
  • - {t('By name')} + {t('By name')}
  • { event.preventDefault() scrollHandler(`letter-${index()}`) diff --git a/src/components/Views/AllTopics/AllTopics.tsx b/src/components/Views/AllTopics/AllTopics.tsx index 77f18bc4..b3ed309e 100644 --- a/src/components/Views/AllTopics/AllTopics.tsx +++ b/src/components/Views/AllTopics/AllTopics.tsx @@ -80,13 +80,13 @@ export const AllTopics = (props: Props) => {
    • - {t('By shouts')} + {t('By shouts')}
    • - {t('By authors')} + {t('By authors')}
    • - {t('By title')} + {t('By title')}
    • { event.preventDefault() scrollHandler(`letter-${index()}`) diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx index cc61737c..16041cc1 100644 --- a/src/components/Views/Author/Author.tsx +++ b/src/components/Views/Author/Author.tsx @@ -1,5 +1,5 @@ import { Meta, Title } from '@solidjs/meta' -import { A, useLocation, useMatch } from '@solidjs/router' +import { A, useLocation, useParams } from '@solidjs/router' import { clsx } from 'clsx' import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on, onMount } from 'solid-js' import { useAuthors } from '~/context/authors' @@ -34,6 +34,7 @@ type Props = { authorSlug: string shouts?: Shout[] author?: Author + selectedTab: string } export const PRERENDERED_ARTICLES_COUNT = 12 @@ -41,6 +42,7 @@ const LOAD_MORE_PAGE_SIZE = 9 export const AuthorView = (props: Props) => { const { t } = useLocalize() + const params = useParams() const { followers: myFollowers, follows: myFollows } = useFollowing() const { session } = useSession() const me = createMemo(() => session()?.user?.app_data?.profile as Author) @@ -48,9 +50,6 @@ export const AuthorView = (props: Props) => { const { sortedFeed } = useFeed() const { modal, hideModal } = useUI() const loc = useLocation() - const matchAuthor = useMatch(() => '/author') - const matchComments = useMatch(() => '/author/:authorId/comments') - const matchAbout = useMatch(() => '/author/:authorId/about') const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false) const [isBioExpanded, setIsBioExpanded] = createSignal(false) const { loadAuthor, authorsEntities } = useAuthors() @@ -189,20 +188,20 @@ export const AuthorView = (props: Props) => {
      - +
      @@ -246,7 +245,7 @@ export const AuthorView = (props: Props) => {
      - +
      @@ -272,7 +271,7 @@ export const AuthorView = (props: Props) => {
      - +
      diff --git a/src/components/Views/DraftsView/DraftsView.tsx b/src/components/Views/DraftsView/DraftsView.tsx index dc569bc2..cdaae1e1 100644 --- a/src/components/Views/DraftsView/DraftsView.tsx +++ b/src/components/Views/DraftsView/DraftsView.tsx @@ -1,46 +1,19 @@ -import { clsx } from 'clsx' -import { For, Show, createEffect, createMemo, createSignal, on } from 'solid-js' - import { useNavigate } from '@solidjs/router' -import { useGraphQL } from '~/context/graphql' -import getDraftsQuery from '~/graphql/query/core/articles-load-drafts' -import { useEditorContext } from '../../../context/editor' -import { useSession } from '../../../context/session' -import { Shout } from '../../../graphql/schema/core.gen' -import { Draft } from '../../Draft' -import { Loading } from '../../_shared/Loading' +import { clsx } from 'clsx' +import { For, Show, createMemo, createSignal } from 'solid-js' +import { Draft } from '~/components/Draft' +import { Loading } from '~/components/_shared/Loading' +import { useEditorContext } from '~/context/editor' +import { useSession } from '~/context/session' +import { Shout } from '~/graphql/schema/core.gen' import styles from './DraftsView.module.scss' -export const DraftsView = () => { +export const DraftsView = (props: { drafts: Shout[] }) => { + const [drafts, setDrafts] = createSignal(props.drafts || []) const { session } = useSession() const authorized = createMemo(() => Boolean(session()?.access_token)) const navigate = useNavigate() - const [drafts, setDrafts] = createSignal([]) - const [loading, setLoading] = createSignal(false) - const { query } = useGraphQL() - - createEffect( - on( - () => Boolean(session()?.access_token), - async (s) => { - if (s) { - setLoading(true) - const resp = await query(getDraftsQuery, {}).toPromise() - const result = resp?.data?.get_shouts_drafts - if (result) { - const { error, drafts: loadedDrafts } = result - if (error) console.warn(error) - if (loadedDrafts) setDrafts(loadedDrafts) - } - setLoading(false) - } - }, - { defer: true } - ) - ) - const { publishShoutById, deleteShout } = useEditorContext() - const handleDraftDelete = async (shout: Shout) => { const success = await deleteShout(shout.id) if (success) { @@ -55,7 +28,7 @@ export const DraftsView = () => { return (
      - }> + }>
      diff --git a/src/components/Views/EditView/EditSettingsView.tsx b/src/components/Views/EditView/EditSettingsView.tsx new file mode 100644 index 00000000..a9427af7 --- /dev/null +++ b/src/components/Views/EditView/EditSettingsView.tsx @@ -0,0 +1,204 @@ +import { clsx } from 'clsx' +import deepEqual from 'fast-deep-equal' +import { Show, createEffect, createSignal, on, onCleanup, onMount } from 'solid-js' +import { createStore } from 'solid-js/store' +import { debounce } from 'throttle-debounce' +import { useGraphQL } from '~/context/graphql' +import getMyShoutQuery from '~/graphql/query/core/article-my' +import { ShoutForm, useEditorContext } from '../../../context/editor' +import { useLocalize } from '../../../context/localize' +import type { Shout, Topic } from '../../../graphql/schema/core.gen' +import { clone } from '../../../utils/clone' +import { isDesktop } from '../../../utils/media-query' +import { Panel } from '../../Editor' +import { AutoSaveNotice } from '../../Editor/AutoSaveNotice' +import { Modal } from '../../Nav/Modal' +import { TableOfContents } from '../../TableOfContents' +import { Icon } from '../../_shared/Icon' +import { InviteMembers } from '../../_shared/InviteMembers' +import { PublishSettings } from '../PublishSettings' +import styles from './EditView.module.scss' + +type Props = { + shout: Shout +} + +export const MAX_HEADER_LIMIT = 100 +export const EMPTY_TOPIC: Topic = { + id: -1, + slug: '' +} + +const AUTO_SAVE_DELAY = 3000 + +const handleScrollTopButtonClick = (ev: MouseEvent | TouchEvent) => { + ev.preventDefault() + window.scrollTo({ + top: 0, + behavior: 'smooth' + }) +} + +export const EditSettingsView = (props: Props) => { + const { t } = useLocalize() + const [isScrolled, setIsScrolled] = createSignal(false) + const { query } = useGraphQL() + const { form, setForm, saveDraft, saveDraftToLocalStorage, getDraftFromLocalStorage } = useEditorContext() + const [shoutTopics, setShoutTopics] = createSignal([]) + const [draft, setDraft] = createSignal() + const [prevForm, setPrevForm] = createStore(clone(form)) + const [saving, setSaving] = createSignal(false) + + createEffect( + on( + () => props.shout, + (shout) => { + if (shout) { + console.debug(`[EditView] shout is loaded: ${shout}`) + setShoutTopics((shout.topics as Topic[]) || []) + const stored = getDraftFromLocalStorage(shout.id) + if (stored) { + console.info(`[EditView] got stored shout: ${stored}`) + setDraft(stored) + } else { + if (!shout.slug) { + console.warn(`[EditView] shout has no slug! ${shout}`) + } + const draftForm = { + slug: shout.slug || '', + shoutId: shout.id || 0, + title: shout.title || '', + lead: shout.lead || '', + description: shout.description || '', + subtitle: shout.subtitle || '', + selectedTopics: (shoutTopics() || []) as Topic[], + mainTopic: shoutTopics()[0] || '', + body: shout.body || '', + coverImageUrl: shout.cover || '', + media: shout.media || '', + layout: shout.layout + } + setForm((_) => draftForm) + console.debug('draft from props data: ', draftForm) + } + } + }, + { defer: true } + ) + ) + + createEffect( + on( + draft, + (d) => { + if (d) { + const draftForm = Object.keys(d).length !== 0 ? d : { shoutId: props.shout.id } + setForm(draftForm) + console.debug('draft from localstorage: ', draftForm) + } + }, + { defer: true } + ) + ) + + createEffect( + on( + () => props.shout?.id, + async (shoutId) => { + if (shoutId) { + const resp = await query(getMyShoutQuery, { shout_id: shoutId }) + const result = resp?.data?.get_my_shout + if (result) { + console.debug('[EditView] getMyShout result: ', result) + const { shout: loadedShout, error } = result + setDraft(loadedShout) + console.debug('[EditView] loadedShout:', loadedShout) + console.log(error) + } + } + }, + { defer: true } + ) + ) + + onMount(() => { + const handleScroll = () => { + setIsScrolled(window.scrollY > 0) + } + + window.addEventListener('scroll', handleScroll, { passive: true }) + onCleanup(() => { + window.removeEventListener('scroll', handleScroll) + }) + + const handleBeforeUnload = (event: BeforeUnloadEvent) => { + if (!deepEqual(prevForm, form)) { + event.returnValue = t( + 'There are unsaved changes in your publishing settings. Are you sure you want to leave the page without saving?' + ) + } + } + + window.addEventListener('beforeunload', handleBeforeUnload) + onCleanup(() => window.removeEventListener('beforeunload', handleBeforeUnload)) + }) + const [hasChanges, setHasChanges] = createSignal(false) + const autoSave = async () => { + console.log('autoSave called') + if (hasChanges()) { + console.debug('saving draft', form) + setSaving(true) + saveDraftToLocalStorage(form) + await saveDraft(form) + setPrevForm(clone(form)) + setSaving(false) + setHasChanges(false) + } + } + + const debouncedAutoSave = debounce(AUTO_SAVE_DELAY, autoSave) + + onMount(() => { + onCleanup(() => { + debouncedAutoSave.cancel() + }) + }) + + return ( + <> +
      +
      +
      + + + + +
      + + + +
      +
      +
      +
      + + + + + + + + + + ) +} + +export default EditSettingsView diff --git a/src/components/Views/EditView/EditView.tsx b/src/components/Views/EditView/EditView.tsx index 1bc25c72..24310641 100644 --- a/src/components/Views/EditView/EditView.tsx +++ b/src/components/Views/EditView/EditView.tsx @@ -1,4 +1,3 @@ -import { useMatch } from '@solidjs/router' import { clsx } from 'clsx' import deepEqual from 'fast-deep-equal' import { @@ -37,7 +36,6 @@ import { InviteMembers } from '../../_shared/InviteMembers' import { Loading } from '../../_shared/Loading' import { Popover } from '../../_shared/Popover' import { EditorSwiper } from '../../_shared/SolidSwiper' -import { PublishSettings } from '../PublishSettings' import styles from './EditView.module.scss' const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor')) @@ -272,8 +270,6 @@ export const EditView = (props: Props) => { setIsLeadVisible(true) } - const matchEdit = useMatch(() => 'edit') - const matchEditSettings = useMatch(() => 'edit/:shoutId/settings') return ( <>
      @@ -299,7 +295,7 @@ export const EditView = (props: Props) => {
      - +
      @@ -455,7 +451,7 @@ export const EditView = (props: Props) => {
      - }> + }> {
      - - - diff --git a/src/components/Views/Expo/Expo.tsx b/src/components/Views/Expo/Expo.tsx index ff6281ce..4ad9cd95 100644 --- a/src/components/Views/Expo/Expo.tsx +++ b/src/components/Views/Expo/Expo.tsx @@ -5,6 +5,7 @@ import { A } from '@solidjs/router' import { useGraphQL } from '~/context/graphql' import getShoutsQuery from '~/graphql/query/core/articles-load-by' import getRandomTopShoutsQuery from '~/graphql/query/core/articles-load-random-top' +import { LayoutType } from '~/types/common' import { useLocalize } from '../../../context/localize' import { LoadShoutsFilters, LoadShoutsOptions, Shout } from '../../../graphql/schema/core.gen' import { getUnixtime } from '../../../utils/getServerDate' @@ -16,8 +17,6 @@ import { Loading } from '../../_shared/Loading' import { ArticleCardSwiper } from '../../_shared/SolidSwiper/ArticleCardSwiper' import styles from './Expo.module.scss' -export type LayoutType = 'music' | 'literature' | 'video' | 'article' | 'image' - type Props = { shouts: Shout[] layout: LayoutType diff --git a/src/components/Views/Feed/Feed.tsx b/src/components/Views/Feed/Feed.tsx index a574b72a..6e571463 100644 --- a/src/components/Views/Feed/Feed.tsx +++ b/src/components/Views/Feed/Feed.tsx @@ -182,7 +182,7 @@ export const FeedView = (props: FeedProps) => {

      {t('Popular authors')}

      - + {t('All authors')} diff --git a/src/config/routes.ts b/src/config/routes.ts index af122c7d..bf80c3c9 100644 --- a/src/config/routes.ts +++ b/src/config/routes.ts @@ -2,13 +2,13 @@ export const ROUTES = { home: '/', inbox: '/inbox', connect: '/connect', - create: '/create', + create: '/edit/new', edit: '/edit/:shoutId', editSettings: '/edit/:shoutId/settings', - drafts: '/drafts', + drafts: '/edit', topics: '/topics', topic: '/topic/:slug', - authors: '/authors', + authors: '/author', author: '/author/:slug', authorComments: '/author/:slug/comments', authorAbout: '/author/:slug/about', @@ -19,16 +19,16 @@ export const ROUTES = { feedBookmarks: '/feed/bookmarked', feedCollaborations: '/feed/coauthored', search: '/search/:q?', - dogma: '/about/dogma', + dogma: '/guide/dogma', discussionRules: '/about/discussion-rules', - guide: '/about/guide', - help: '/about/help', - manifest: '/about/manifest', - partners: '/about/partners', - principles: '/about/principles', - projects: '/about/projects', + guide: '/guide', + help: '/guide/support', + manifest: '/guide/manifest', + partners: '/guide/partners', + principles: '/guide/principles', + projects: '/guide/projects', termsOfUse: '/about/terms-of-use', - thanks: '/about/thanks', + thanks: '/guide/thanks', expo: '/expo/:layout?', profileSettings: '/profile/settings', profileSecurity: '/profile/security', diff --git a/src/context/editor.tsx b/src/context/editor.tsx index 57cb1bcf..48b93fb2 100644 --- a/src/context/editor.tsx +++ b/src/context/editor.tsx @@ -179,7 +179,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => { if (shout?.published_at) { navigate(`/article/${shout.slug}`) } else { - navigate('/drafts') + navigate('/edit') } } catch (error) { console.error('[saveShout]', error) diff --git a/src/lib/api/private.ts b/src/lib/api/private.ts index cdfd4c3d..390740f4 100644 --- a/src/lib/api/private.ts +++ b/src/lib/api/private.ts @@ -5,6 +5,7 @@ import loadShoutsCoauthoredQuery from '~/graphql/query/core/articles-load-coauth import loadShoutsDiscussedQuery from '~/graphql/query/core/articles-load-discussed' import loadShoutsFollowedQuery from '~/graphql/query/core/articles-load-followed' import loadShoutsUnratedQuery from '~/graphql/query/core/articles-load-unrated' + import { QueryLoad_Shouts_FollowedArgs, QueryLoad_Shouts_UnratedArgs, diff --git a/src/pages/about/discussionRules.page.route.ts b/src/pages/about/discussionRules.page.route.ts deleted file mode 100644 index f8907219..00000000 --- a/src/pages/about/discussionRules.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.discussionRules) diff --git a/src/pages/about/dogma.page.route.ts b/src/pages/about/dogma.page.route.ts deleted file mode 100644 index 2c63adf0..00000000 --- a/src/pages/about/dogma.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.dogma) diff --git a/src/pages/about/dogma.page.tsx b/src/pages/about/dogma.page.tsx deleted file mode 100644 index ada689a5..00000000 --- a/src/pages/about/dogma.page.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Meta } from '../../context/meta' - -import { StaticPage } from '../../components/Views/StaticPage' -import { useLocalize } from '../../context/localize' -import { getImageUrl } from '../../utils/getImageUrl' - -export const DogmaPage = () => { - const { t } = useLocalize() - - const ogImage = getImageUrl('production/image/logo_image.png') - const ogTitle = t('Dogma') - const description = t('Professional principles that the open editorial team follows in its work') - - return ( - - - - - - - - - - - - -

      - Редакционные принципы -

      - -

      - Дискурс — журнал с открытой горизонтальной редакцией. Содержание журнала определяется прямым - голосованием его авторов. Мы нередко занимаем различные позиции по разным проблемам, но - придерживаемся общих профессиональных принципов: -

      -
        -
      1. - На первое место ставим факты. Наша задача — не судить, а наблюдать и непредвзято - фиксировать происходящее. Все утверждения и выводы, которые мы делаем, подтверждаются фактами, - цифрами, мнениями экспертов или ссылками на авторитетные источники. -
      2. -
      3. - Ответственно относимся к источникам. - Мы выбираем только надежные источники, проверяем информацию и рассказываем, как и откуда мы её - получили, кроме случаев, когда это может нанести вред источникам. Тогда мы не раскроем их, даже в - суде. -
      4. -
      5. - Выбираем компетентных и независимых экспертов, понимая всю степень ответственности перед - аудиторией. -
      6. -
      7. - - Даем возможность высказаться всем заинтересованным сторонам, но не присоединяемся ни к чьему - лагерю. - - Ко всем событиям, компаниям и людям мы относимся с одинаковым скептицизмом. -
      8. -
      9. - Всегда исправляем ошибки, если мы их допустили. - Никто не безгрешен, иногда и мы ошибаемся. Заметили ошибку — отправьте{' '} - ремарку автору или напишите нам на{' '} - - welcome@discours.io - - . -
      10. -
      -
      - ) -} - -export const Page = DogmaPage diff --git a/src/pages/about/guide.page.tsx b/src/pages/about/guide.page.tsx deleted file mode 100644 index c2cba232..00000000 --- a/src/pages/about/guide.page.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { Meta } from '../../context/meta' - -import { StaticPage } from '../../components/Views/StaticPage' -import { useLocalize } from '../../context/localize' -import { getImageUrl } from '../../utils/getImageUrl' - -export const GuidePage = () => { - const { t } = useLocalize() - - const ogImage = getImageUrl('production/image/logo_image.png') - const ogTitle = t('How Discours works') - const description = t('A guide to horizontal editorial: how an open journal works') - - return ( - - <> - - - - - - - - - - - -

      - {ogTitle} -

      - -

      - Дискурс — независимый журнал о культуре, науке, искусстве и обществе с  - открытой редакцией. У нас нет главного редактора, инвестора - и вообще никого, кто бы принимал единоличные решения. Вместо традиционных иерархий - Дискурс основан на принципах прямой демократии: в нашем горизонтальном сообществе все - редакционные вопросы решаются открытым голосованием авторов журнала. Вот как это работает. -

      -

      Как устроен сайт Дискурса

      -

      Дискурс состоит из четырех основных разделов:

      -
        -
      • -

        - Темы -  — у нас публикуются исследования, обзоры, эссе, интервью, репортажи, - аналитика и другие материалы о культуре, науке, искусстве и обществе. -

        -
      • -
      • -

        - Искусство -  — здесь, например, представлены художественные произведения: литература, живопись, - музыка, фотографии, видео. Этот раздел помогает прозвучать новому искусству, которое создают - российские художники, писатели, режиссёры и музыканты. -

        -
      • - {/* -
      • -

        - События — в этом разделе - публикуются самые важные, по мнению редакции, культурные - события России — выставки, лекции, концерты, кинопоказы, фестивали, - художественные и политические акции. Напишите нам - на почту, если вы - хотите разместить объявление. Мы делаем это - на безвозмездной основе. -

        -
      • -
      • -

        - Редакция — - это внутренний раздел, где появляются новые материалы, которые присылают - в редакцию. Здесь авторы обсуждают, редактируют и оценивают - публикации, определяя таким образом содержание журнала. -

        -
      • -*/} -
      -

      - Материалы в Дискурсе объединяются по темам - — ключевым словам, которые располагаются в конце материалов и связывают - материалы по жанрам (например, интервью,{' '} - репортажи, эссе,{' '} - ликбезы - ), по тематике (кино, философия,{' '} - история, абсурдизм,{' '} - секс и т.д.) или в серии (как « - Законы мира» или « - За линией Маннергейма - »). Темы объединяют сотни публикаций, помогают ориентироваться в журнале и следить - за интересными материалами. -

      - -
      -

      Как стать автором журнала

      -

      - Дискурс объединяет журналистов, активистов, музыкантов, художников, фотографов, режиссеров, - философов, ученых и других замечательных людей. Каждый может прислать{' '} - свой материал в журнал. Формат и тематика не имеют значения, единственное, что - важно — хороший ли материал. Если - сообщество поддержит вашу публикацию, она выйдет в журнале и станет доступна тысячам - наших читателей. -

      -
      - -

      Как проходит голосование

      -

      - Все присылаемые в Дискурс материалы попадают в  - «Редакцию». Это внутренний раздел сайта, где участники сообщества - решают, что будет опубликовано в Дискурсе. Как только работа получает одобрение как минимум - пятерых авторов открытой редакции, она немедленно публикуется в журнале. Если же - материал набирает более 20% голосов «против», он не выходит - и может быть отправлен на доработку. Жестких сроков рассмотрения материалов у нас - нет, иногда это занимает час, иногда месяц, обычно — несколько дней. -

      -
      -

      - Как только сообщество поддержит публикацию, вы получите приглашение - в интернет-редакцию и сможете голосовать за новые материалы. -

      -
      - -

      Как мы делаем тексты друг друга лучше

      -

      - Дискурс — журнал с совместным редактированием. Совершенствовать тексты нам - помогает система ремарок. Вы можете выделить часть текста в любой статье - и оставить к ней замечание, вопрос или предложение — автор текста получит - совет на почту и сможет его учесть. Так мы устраняем опечатки, неточности - и советуем друг другу, как сделать тексты качественнее и интереснее. -

      -

      - Среди участников сообщества есть профессиональные редакторы, которые помогают авторам делать - тексты лучше. Если вашему материалу потребуется доработка, они помогут отредактировать текст, - подобрать иллюстрации, придумать заголовок и красиво сверстать публикацию. Если - вы хотите обсудить текст, прежде чем загрузить материал в интернет-редакцию — - разместите его в google-документе, откройте доступ к редактированию по ссылке - и напишите нам на  - - welcome@discours.io - - . -

      -

      - Если у вас возникают трудности с тем, чтобы подобрать к своему материалу - иллюстрации, тоже пишите на  - - почту - - — наши коллеги-художники могут вам помочь{' '} - - в режиме совместного редактирования - - . -

      - -

      Что сообщество дает авторам

      -
        -
      • -

        - Право определять, каким будет журнал. Дискурс — это общественная - институция, созданная людьми и ради людей, функционирующая на условиях прямой - демократии. Авторы публикуют статьи и художественные проекты, участвуют - в обсуждениях, голосуют за работы коллег и таким образом вносят свой вклад - в развитие проекта, определяя содержание и направление журнала. -

        -
      • -
      • -

        - Возможность обратиться к широкой аудитории. Дискурс читают десятки тысяч - людей, и с каждым днем их становится больше. -

        -
      • -
      • -

        - Поддержка редакции. Дискурс предоставляет авторам аккредитацию - на мероприятия, базу контактов, юридическую поддержку, ознакомление с книжными, - кино- и музыкальными новинками до их выхода в свет. Если что-то - из этого вам понадобится, пишите на почту{' '} - - welcome@discours.io - -  — поможем. -

        -
      • -
      • -

        - Пресс-карты для корреспондентов. Три опубликованные статьи позволяют авторам - Дискурса получить официальные удостоверения журналистов (пресс-карты) на следующий год. - Пресс-карты удостоверяют, что вы журналист и можете пользоваться всеми теми правами, - которые гарантирует Закон о СМИ. Кроме того, многие культурные институции (музеи, галереи - и др.) предоставляют журналистам право свободного входа. -

        -
      • -
      • -

        - Помощь сотен специалистов в разных областях. В основе Дискурса - лежит идея совместного редактирования. Участники редакционного сообщества — - несколько сотен журналистов, исследователей, художников, литераторов из разных стран - — изучают материалы друг друга до публикации и помогают сделать - их качественнее и интереснее. Так, в редакции нередко складываются творческие - союзы: например, авторов текстов и художников, создающих для них иллюстрации. -

        -
      • -
      • -

        - Пространство общения полное выдающихся людей. Дискурс — большое - живое сообщество интеллектуалов, разбросанных по всему земному шару. Вступив - в редакцию, вы сможете познакомиться со множеством интересных людей, которые - определяют повестку завтрашнего дня, вдохновляют окружающих, создают новое и изучают - старое, ищут знания и готовы ими делиться, чтобы менять мир в соответствии - со своими идеалами. -

        -
      • -
      - -

      Как быть в курсе

      -

      - За свежими публикациями Дискурса можно следить не только на сайте, - но и на страницах в  - - Фейсбуке - - ,{' '} - - ВКонтакте - {' '} - и  - - Телеграме - - . А ещё раз в месяц мы отправляем почтовую рассылку{' '} - с дайджестом лучших материалов. -

      -

      - Если вы хотите сотрудничать, что-то обсудить или предложить — пожалуйста, пишите - на  - - welcome@discours.io - - . Мы обязательно ответим. -

      - -
      - ) -} - -export const Page = GuidePage diff --git a/src/pages/about/guilde.page.route.ts b/src/pages/about/guilde.page.route.ts deleted file mode 100644 index 2d86c1b5..00000000 --- a/src/pages/about/guilde.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.guide) diff --git a/src/pages/about/help.page.route.ts b/src/pages/about/help.page.route.ts deleted file mode 100644 index 48288617..00000000 --- a/src/pages/about/help.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.help) diff --git a/src/pages/about/manifest.page.route.ts b/src/pages/about/manifest.page.route.ts deleted file mode 100644 index 70a30fba..00000000 --- a/src/pages/about/manifest.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.manifest) diff --git a/src/pages/about/partners.page.route.ts b/src/pages/about/partners.page.route.ts deleted file mode 100644 index 5e588193..00000000 --- a/src/pages/about/partners.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.partners) diff --git a/src/pages/about/principles.page.route.ts b/src/pages/about/principles.page.route.ts deleted file mode 100644 index 4995eb09..00000000 --- a/src/pages/about/principles.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.principles) diff --git a/src/pages/about/principles.page.tsx b/src/pages/about/principles.page.tsx deleted file mode 100644 index d8b10b63..00000000 --- a/src/pages/about/principles.page.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { Meta } from '../../context/meta' - -import { StaticPage } from '../../components/Views/StaticPage' -import { useLocalize } from '../../context/localize' -import { getImageUrl } from '../../utils/getImageUrl' - -export const PrinciplesPage = () => { - const { t } = useLocalize() - - const ogImage = getImageUrl('production/image/logo_image.png') - const ogTitle = t('Community Principles') - const description = t('Community values and rules of engagement for the open editorial team') - - return ( - - - - - - - - - - - - -

      - {ogTitle} -

      - -
        -
      1. -

        - Горизонтальность. Мы все разные, и это классно. Вертикалей - в мире достаточно, мы — горизонтальное сообщество и ценим наши различия, - потому что знаем — в них наша сила. Благодаря разнообразию сотен голосов, - усиливающих друг друга, в сообществе складывается неповторимая синергия, которая помогает - вместе достигать большего. -

        -
      2. -
      3. -

        - Многоголосие. Мы ценим свободу слова и аргументированные мнения. - Предоставляя трибуну каждому, кому есть что сказать, самиздат отражает полифонию позиций, знаний - и опыта, которые открывают более полную картину реальности. -

        -
      4. -
      5. -

        - Взаимопомощь. Мы помогаем друг другу, потому что хотим, чтобы в мире - было еще больше хорошего. Обсуждая что-то, мы всегда интересуемся, чем можем помочь. - В самиздате можно найти специалистов практически в любых сферах и получить - поддержку от сотен людей. Благодаря коллективной экспертизе глобального сообщества - в самиздате выходят крутейшие публикации, которыми можно вечно гордиться. -

        -
      6. -
      7. -

        - Взаимоуважение. Мы ценим, искренне уважаем друг друга и вместо - борщевиков враждебности культивируем цветы добра, мира, знания и юмора. Нам некогда - доказывать друг другу, кто круче. Гораздо приятнее сотрудничать, помогать и создавать - что-то важное, интересное и полезное. -

        -
      8. -
      9. -

        - Созидание. Мы создаем, потому что любим создавать. Мы открыто делимся - опытом, дарим идеи, обмениваемся мнениями и благодарим за критику, используя - ее для совершенствования мастерства и саморазвития. Мы знаем, что мир - не идеальное место, и делаем всё возможное, чтобы он стал лучше. -

        -
      10. -
      - -

      - Как у нас принято себя вести -

      - -

      - Открытая редакция объединяет сотни потрясающих людей со всего мира, которые делают крутейшие - вещи. Это пространство, где доверяют, вдохновляют, исследуют и создают новое вместе. Поскольку - все в сообществе очень разные, как-то мы собрались и решили зафиксировать базовые - ценности открытой редакции, а заодно придумали универсальные правила взаимодействия, чтобы - общение было не только плодотворным, но и приятным для всех участников сообщества. -

      -
        -
      1. -

        - Действуем, помогаем и делимся. В редакции мы создаем свои - проекты и помогаем другим создавать свои — советами, делом, участием, - вовлеченностью. Мы открыто делимся опытом, мнениями и идеями, потому что ценим силу - сотрудничества и знаем, что идеи реализуются скорее, лучше и веселее, если над ними - трудиться сообща. -

        -
      2. - -
      3. -

        - Общаемся дружелюбно. Помните, по ту сторону монитора находятся - реальные люди. Неуважение ранит других так же, как ранило бы вас самих. Поэтому - не стоит кричать (даже капслоком), заполнять эфир желчью и бросаться - грубостями — так вы рискуете не только растерять доверие окружающих, - но и остаться непонятым. -

        -
      4. - -
      5. -

        - Критикуем и реагируем конструктивно. Самиздат про то, чтобы - разбираться в сложных вещах всем сообществом, поэтому мы тактично и без агрессии - делимся мнениями, стараясь убедительно аргументировать позиции. И с благодарностью - принимаем критику, используя ее для улучшения наших проектов. Мы верим, что каждый - участник сообщества имеет добрые намерения, и придерживаемся принципов доброжелательной - критики, стараемся делиться советами — лучшим средством для самосовершенствования. - Обоснованная критика помогает и адресату, и всем участникам сообщества досконально - изучить тему и глубже разобраться в проблеме. -

        -
      6. - -
      7. -

        - Решаем трудности не агрессией, а диалогом. Обесценивать мнения - и оскорблять других людей только потому, что вы с ними - не согласны, — не лучший способ донести свою точку зрения. Конечно, важно - высказаться, если вас что-то не устраивает и откровенно бесит. Но прежде чем - сжигать оппонента гневом, попробуйте понять, почему этот «нехороший человек» так - поступает. Возможно, аргументы собеседника окажутся убедительными или вам удастся изменить его - мнение. В любом случае конфликты решаются в диалогах и проходят, - а налаженное взаимопонимание останется надолго. -

        -
      8. - -
      9. -

        - Не переходим на личности — это признак токсичности. Всегда - мудрее обсуждать точку зрения человека, а не его самого, даже если он вам - не импонирует. Предвзятое отношение ограничивает кругозор, добавляет преждевременные - морщины и не помогает окружающим стать лучше. Вежливость - и взаимоуважение — краеугольная основа вдумчивых и осмысленных дискуссий. -

        -
      10. - -
      11. -

        - Благодарим за помощь. Благодарите коллег даже за самые, - казалось бы, простые вещи. «Спасибо» не зря называют волшебным - словом — на искренней благодарности держится любое подлинное сотрудничество. - Поддержка воодушевляет на новые подвиги и напоминает, что мир делают прекрасным - не машины, а живые люди. -

        -
      12. - -
      13. -

        - Даем еще один шанс. Все совершают ошибки, и за один проступок - не стоит вычеркивать людей из жизни. Ошибки нужны, чтобы на них учиться - и делать выводы. Однако если многократно и систематически нарушать правила сообщества, - наверняка можно заслужить минусы в карму от других участников и потерять доступ - к сообществу. -

        -
      14. - -
      15. -

        - Вместе создаем идеальную среду общения. Открытая редакция — это - утопическое пространство обогащающей и осмысленной коммуникации. Атмосфера горизонтального - сообщества складывается из действий каждого, поэтому мы действуем так, чтобы - способствовать сотворчеству, коллективному познанию и развитию самиздата и нашей - альтернативной интеллектуальной медиасреды. -

        -
      16. - -
      17. -

        - Помним, что всё в сообществе зависит от нас. Если нам чего-то - не хватает, мы начинаем действовать — рассказываем об идее, находим - единомышленников, готовим и запускаем проект. Так в сообществе становится на одну - крутую активность больше. Так появилось наше сообщество. Так появился самиздат и все - проекты открытой редакции. Чтобы в сообществе случилось что-то прекрасное, достаточно - просто положить этому начало. -

        -
      18. -
      -
      - ) -} - -export const Page = PrinciplesPage diff --git a/src/pages/about/projects.page.route.ts b/src/pages/about/projects.page.route.ts deleted file mode 100644 index b8ab171a..00000000 --- a/src/pages/about/projects.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.projects) diff --git a/src/pages/about/termsOfUse.page.route.ts b/src/pages/about/termsOfUse.page.route.ts deleted file mode 100644 index 301475fd..00000000 --- a/src/pages/about/termsOfUse.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.termsOfUse) diff --git a/src/pages/about/thanks.page.route.ts b/src/pages/about/thanks.page.route.ts deleted file mode 100644 index 2db639c8..00000000 --- a/src/pages/about/thanks.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../../stores/router' -import { getServerRoute } from '../../utils/getServerRoute' - -export default getServerRoute(ROUTES.thanks) diff --git a/src/pages/about/thanks.page.tsx b/src/pages/about/thanks.page.tsx deleted file mode 100644 index 24b740d3..00000000 --- a/src/pages/about/thanks.page.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { Meta } from '../../context/meta' - -import { StaticPage } from '../../components/Views/StaticPage' -import { useLocalize } from '../../context/localize' -import { getImageUrl } from '../../utils/getImageUrl' - -export const ThanksPage = () => { - const { t } = useLocalize() - const ogImage = getImageUrl('production/image/logo_image.png') - const ogTitle = t('Thank you') - const description = t( - 'Self-publishing exists thanks to the help of wonderful people from all over the world. Thank you!' - ) - - return ( - - - - - - - - - - - - -

      - {ogTitle} -

      - {/* -

      Команда

      -

      - Константин Ворович — исполнительный директор, - welcome@discours.io
      - Александр Гусев — технический архитектор, - services@discours.io
      - Екатерина Ильина — шеф-редактор проекта, - letter@discours.io
      - Яна Климова — редактор сайта и соцсетей, - letter@discours.io
      - Николай Носачевский — голос и душа подкаста, - podcast@discours.io -

      - */} -

      Неоценимый вклад в Дискурс внесли и вносят

      -

      - Мария Бессмертная, Дамир Бикчурин, Константин Ворович, Ян Выговский, Эльдар Гариффулин, Павел - Гафаров, Виктория Гендлина, Александр Гусев, Данила Давыдов, Константин Дубовик, Вячеслав Еременко, - Кристина Ибрагим, Екатерина Ильина, Анна Капаева, Яна Климова, Александр Коренков, Ирэна Лесневская, - Игорь Лобанов, Анастасия Лозовая, Григорий Ломизе, Евгений Медведев, Павел Никулин, Николай - Носачевский, Андрей Орловский, Михаил Панин, Антон Панов, Павел Пепперштейн, Любовь Покровская, Илья - Розовский, Денис Светличный, Павел Соколов, Сергей Стрельников, Глеб Струнников, Николай Тарковский, - Кирилл Филимонов, Алексей Хапов, Екатерина Харитонова -

      -

      Авторы

      -

      - Мы безмерно благодарны{' '} - - каждому автору - {' '} - за участие и поддержку проекта. Сегодня, когда для большинства деньги стали целью - и основным источником мотивации, бескорыстная помощь и основанный на энтузиазме труд - бесценны. Именно вы своим трудом каждый день делаете Дискурс таким, какой он есть. -

      -

      Иллюстраторы

      -

      - Ольга Аверинова, Регина Акчурина, Айгуль Берхеева, Екатерина Вакуленко, Анастасия Викулова, Мария - Власенко, Ванесса Гаврилова, Ольга Горше, Ксения Горшкова, Ангелина Гребенюкова, Илья Diliago, Антон - Жаголкин, Саша Керова, Ольга Машинец, Злата Мечетина, Тала Никитина, Никита Поздняков, Матвей - Сапегин, Татьяна Сафонова, Виктория Шибаева -

      -

      Меценаты

      -

      - Дискурс существует исключительно на пожертвования читателей. Мы бесконечно признательны - всем, кто нас поддерживает. Ваши пожертвования — финансовый фундамент журнала. Благодаря - вам мы развиваем платформу качественной журналистики, которая помогает самым разным авторам - быть услышанными. Стать нашим меценатом и подписаться на ежемесячную поддержку проекта - можно здесь. -

      -
      - ) -} - -export const Page = ThanksPage diff --git a/src/pages/allAuthors.page.route.ts b/src/pages/allAuthors.page.route.ts deleted file mode 100644 index 7edd2f2f..00000000 --- a/src/pages/allAuthors.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.authors) diff --git a/src/pages/allAuthors.page.server.ts b/src/pages/allAuthors.page.server.ts deleted file mode 100644 index 1450723c..00000000 --- a/src/pages/allAuthors.page.server.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { PageContext } from '../renderer/types' -import type { PageProps } from './types' - -import { PAGE_SIZE } from '../components/Views/AllTopics/AllTopics' -import { apiClient } from '../graphql/client/core' - -export const onBeforeRender = async (_pageContext: PageContext) => { - const allAuthors = await apiClient.getAllAuthors() - const topWritingAuthors = await apiClient.loadAuthorsBy({ - by: { order: 'shouts' }, - limit: PAGE_SIZE, - offset: 0 - }) - const topFollowedAuthors = await apiClient.loadAuthorsBy({ - by: { order: 'followers' }, - limit: PAGE_SIZE, - offset: 0 - }) - const pageProps: PageProps = { allAuthors, seo: { title: '' }, topWritingAuthors, topFollowedAuthors } - - return { - pageContext: { - pageProps - } - } -} diff --git a/src/pages/allAuthors.page.tsx b/src/pages/allAuthors.page.tsx deleted file mode 100644 index 3086cef1..00000000 --- a/src/pages/allAuthors.page.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import type { PageProps } from './types' - -import { createSignal, onMount } from 'solid-js' - -import { AllAuthors } from '../components/Views/AllAuthors/' -import { PAGE_SIZE } from '../components/Views/AllTopics/AllTopics' -import { PageLayout } from '../components/_shared/PageLayout' -import { useLocalize } from '../context/localize' -import { loadAllAuthors, loadAuthors } from '../stores/zine/authors' - -export const AllAuthorsPage = (props: PageProps) => { - const [isLoaded, setIsLoaded] = createSignal( - Boolean(props.allAuthors && props.topFollowedAuthors && props.topWritingAuthors) - ) - - const { t } = useLocalize() - - onMount(async () => { - if (isLoaded()) { - return - } - - await loadAllAuthors() - await loadAuthors({ by: { order: 'shouts' }, limit: PAGE_SIZE, offset: 0 }) - await loadAuthors({ by: { order: 'followers' }, limit: PAGE_SIZE, offset: 0 }) - setIsLoaded(true) - }) - - return ( - - - - ) -} - -export const Page = AllAuthorsPage diff --git a/src/pages/allTopics.page.route.ts b/src/pages/allTopics.page.route.ts deleted file mode 100644 index e7019ef7..00000000 --- a/src/pages/allTopics.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.topics) diff --git a/src/pages/allTopics.page.server.ts b/src/pages/allTopics.page.server.ts deleted file mode 100644 index d9cfd2a5..00000000 --- a/src/pages/allTopics.page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { PageContext } from '../renderer/types' -import type { PageProps } from './types' - -import { apiClient } from '../graphql/client/core' - -export const onBeforeRender = async (_pageContext: PageContext) => { - const allTopics = await apiClient.getAllTopics() - - const pageProps: PageProps = { allTopics, seo: { title: '' } } - - return { - pageContext: { - pageProps - } - } -} diff --git a/src/pages/allTopics.page.tsx b/src/pages/allTopics.page.tsx deleted file mode 100644 index 5b440afe..00000000 --- a/src/pages/allTopics.page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { AllTopics } from '../components/Views/AllTopics' -import { PageLayout } from '../components/_shared/PageLayout' -import { useLocalize } from '../context/localize' -import { useTopics } from '../context/topics' - -export const AllTopicsPage = () => { - const { t } = useLocalize() - const { sortedTopics } = useTopics() - - return ( - - - - ) -} - -export const Page = AllTopicsPage diff --git a/src/pages/article.page.route.ts b/src/pages/article.page.route.ts deleted file mode 100644 index 9b9f5ecb..00000000 --- a/src/pages/article.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.article) diff --git a/src/pages/article.page.server.ts b/src/pages/article.page.server.ts deleted file mode 100644 index 3c41e14a..00000000 --- a/src/pages/article.page.server.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { PageContext } from '../renderer/types' -import type { PageProps } from './types' - -import { render } from 'vike/abort' - -import { apiClient } from '../graphql/client/core' - -export const onBeforeRender = async (pageContext: PageContext) => { - const { slug } = pageContext.routeParams - const article = await apiClient.getShoutBySlug(slug) - - if (!article) { - throw render(404) - } - - const pageProps: PageProps = { article, seo: { title: article.title } } - - return { - pageContext: { - pageProps - } - } -} diff --git a/src/pages/article.page.tsx b/src/pages/article.page.tsx deleted file mode 100644 index 439da28e..00000000 --- a/src/pages/article.page.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import type { Shout } from '../graphql/schema/core.gen' -import type { PageProps } from './types' - -import { redirectPage } from '@nanostores/router' -import { Show, createMemo, createSignal, onMount } from 'solid-js' - -import { FullArticle } from '../components/Article/FullArticle' -import { Loading } from '../components/_shared/Loading' -import { PageLayout } from '../components/_shared/PageLayout' -import { ReactionsProvider } from '../context/reactions' -import { router, useRouter } from '../stores/router' -import { loadShout, useArticlesStore } from '../stores/zine/articles' -import { setPageLoadManagerPromise } from '../utils/pageLoadManager' - -export const ArticlePage = (props: PageProps) => { - const shouts = props.article ? [props.article] : [] - const { page } = useRouter() - - const slug = createMemo(() => page().params['slug'] as string) - - const { articleEntities } = useArticlesStore({ - shouts - }) - - const article = createMemo(() => articleEntities()[slug()]) - - onMount(async () => { - if (!article()?.body) { - const loadShoutPromise = loadShout(slug()) - setPageLoadManagerPromise(loadShoutPromise) - await loadShoutPromise - - if (!article()) { - redirectPage(router, 'fourOuFour') - } - } - }) - - onMount(() => { - try { - // document.body.appendChild(script) - console.debug('TODO: connect ga') - } catch (error) { - console.warn(error) - } - }) - const [scrollToComments, setScrollToComments] = createSignal(false) - - return ( - { - setScrollToComments(value) - }} - > - - }> - - - - - ) -} - -export const Page = ArticlePage diff --git a/src/pages/author.page.route.ts b/src/pages/author.page.route.ts deleted file mode 100644 index a6b7653a..00000000 --- a/src/pages/author.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.author) diff --git a/src/pages/author.page.server.ts b/src/pages/author.page.server.ts deleted file mode 100644 index dca92742..00000000 --- a/src/pages/author.page.server.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { PageContext } from '../renderer/types' -import type { PageProps } from './types' - -import { render } from 'vike/abort' - -import { PRERENDERED_ARTICLES_COUNT } from '../components/Views/Author' -import { apiClient } from '../graphql/client/core' - -export const onBeforeRender = async (pageContext: PageContext) => { - const { slug } = pageContext.routeParams - console.debug(`[author.page] detected author in route: @${slug}`) - const author = await apiClient.getAuthor({ slug }) - - if (!author) { - throw render(404) - } - - const authorShouts = await apiClient.getShouts({ - filters: { author: slug, featured: false }, - limit: PRERENDERED_ARTICLES_COUNT - }) - const pageProps: PageProps = { author, authorShouts, seo: { title: author.name } } - - return { - pageContext: { - pageProps - } - } -} diff --git a/src/pages/author.page.tsx b/src/pages/author.page.tsx deleted file mode 100644 index a2e9bd70..00000000 --- a/src/pages/author.page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import type { PageProps } from './types' - -import { Show, createEffect, createMemo, createSignal, on, onCleanup } from 'solid-js' - -import { AuthorView, PRERENDERED_ARTICLES_COUNT } from '../components/Views/Author' -import { Loading } from '../components/_shared/Loading' -import { PageLayout } from '../components/_shared/PageLayout' -import { useLocalize } from '../context/localize' -import { ReactionsProvider } from '../context/reactions' -import { useRouter } from '../stores/router' -import { loadShouts, resetSortedArticles } from '../stores/zine/articles' -import { loadAuthor } from '../stores/zine/authors' - -export const AuthorPage = (props: PageProps) => { - const { t } = useLocalize() - const { page } = useRouter() - const slug = createMemo(() => page().params['slug'] as string) - - const [isLoaded, setIsLoaded] = createSignal( - Boolean(props.authorShouts) && Boolean(props.author) && props.author.slug === slug() - ) - - createEffect( - on(slug, async (s) => { - if (s) { - setIsLoaded(false) - resetSortedArticles() - await loadShouts({ - filters: { author: s, featured: false }, - limit: PRERENDERED_ARTICLES_COUNT - }) - await loadAuthor({ slug: s }) - setIsLoaded(true) - } - }) - ) - - onCleanup(() => resetSortedArticles()) - - return ( - - - }> - - - - - ) -} - -export const Page = AuthorPage diff --git a/src/pages/authorAbout.page.route.ts b/src/pages/authorAbout.page.route.ts deleted file mode 100644 index 4bcf9ef3..00000000 --- a/src/pages/authorAbout.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.authorAbout) diff --git a/src/pages/authorComment.page.route.ts b/src/pages/authorComment.page.route.ts deleted file mode 100644 index 10ff3029..00000000 --- a/src/pages/authorComment.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.authorComments) diff --git a/src/pages/connect.page.route.ts b/src/pages/connect.page.route.ts deleted file mode 100644 index 264948b8..00000000 --- a/src/pages/connect.page.route.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { ROUTES } from '../stores/router' -import { getServerRoute } from '../utils/getServerRoute' - -export default getServerRoute(ROUTES.connect) diff --git a/src/pages/connect.page.tsx b/src/pages/connect.page.tsx deleted file mode 100644 index 23ff0d75..00000000 --- a/src/pages/connect.page.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Show, createSignal } from 'solid-js' - -import { PageLayout } from '../components/_shared/PageLayout' - -export const ConnectPage = () => { - const [state, setState] = createSignal<'initial' | 'loading' | 'success' | 'error'>('initial') - - const formRef: { current: HTMLFormElement } = { current: null } - const handleFormSubmit = async (e) => { - e.preventDefault() - setState('loading') - - // eslint-disable-next-line unicorn/prefer-spread - const postData = Array.from(formRef.current.elements).reduce( - (acc, element) => { - const formField = element as unknown as { name: string; value: string } - if (formField.name) { - acc[formField.name] = formField.value - } - - return acc - }, - {} as Record - ) - - const requestOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(postData) - } - - const result = await fetch('/api/feedback', requestOptions) - - if (!result.ok) { - console.error('[handleFormSubmit]', result) - setState('error') - return - } - - setState('success') - window.scrollTo({ - top: 0 - }) - } - - // TODO: l10n - return ( - -
      -
      -
      - -

      - Предложить идею -

      - -

      - Хотите что-то предложить, обсудить или посоветовать? Поделиться темой или идеей? Напишите - нам скорее! Если укажете свою почту, мы обязательно ответим. -

      - -
      (formRef.current = el)}> -
      - -
      -
      - - -
      -
      -