+
+ Предложить идею
+
+
Хотите что-то предложить, обсудить или посоветовать? Поделиться темой или идеей? Напишите нам
скорее! Если укажете свою почту, мы обязательно ответим.
diff --git a/src/components/Pages/TopicPage.tsx b/src/components/Pages/TopicPage.tsx
index 16e0c95a..35a94c92 100644
--- a/src/components/Pages/TopicPage.tsx
+++ b/src/components/Pages/TopicPage.tsx
@@ -8,7 +8,7 @@ import { loadTopic } from '../../stores/zine/topics'
import { Loading } from '../Loading'
export const TopicPage = (props: PageProps) => {
- const [isLoaded, setIsLoaded] = createSignal(Boolean(props.authorArticles) && Boolean(props.author))
+ const [isLoaded, setIsLoaded] = createSignal(Boolean(props.topicArticles) && Boolean(props.topic))
const slug = createMemo(() => {
const { page: getPage } = useRouter()
diff --git a/src/components/Pages/about/DiscussionRulesPage.tsx b/src/components/Pages/about/DiscussionRulesPage.tsx
index eb8e89ce..11ee4565 100644
--- a/src/components/Pages/about/DiscussionRulesPage.tsx
+++ b/src/components/Pages/about/DiscussionRulesPage.tsx
@@ -7,112 +7,110 @@ export const DiscussionRulesPage = () => {
-
+
-
-
- Открытая редакция существует благодаря дружному сообществу авторов
- и читателей — вдумчивых и сознательных людей, приверженных ценностям
- гуманизма, демократии и прав человека. Мы очень ценим атмосферу осмысленного
- общения, которая здесь сложилась. Чтобы сохранить ее такой же уютной
- и творческой, мы составили правила общения в сообществе, руководствуясь
- которыми каждый мог бы соучаствовать в плодотворных дискуссиях, не задевая
- других. Ключевой принцип этих правил предельно прост — уважайте ближних,
- постарайтесь не нарушать законы Российской Федерации без крайней
- на то необходимости и помните, что в дискуссиях чутких
- и здравомыслящих людей рождается истина.
-
+
+ Открытая редакция существует благодаря дружному сообществу авторов
+ и читателей — вдумчивых и сознательных людей, приверженных ценностям
+ гуманизма, демократии и прав человека. Мы очень ценим атмосферу осмысленного
+ общения, которая здесь сложилась. Чтобы сохранить ее такой же уютной
+ и творческой, мы составили правила общения в сообществе, руководствуясь
+ которыми каждый мог бы соучаствовать в плодотворных дискуссиях, не задевая
+ других. Ключевой принцип этих правил предельно прост — уважайте ближних,
+ постарайтесь не нарушать законы Российской Федерации без крайней
+ на то необходимости и помните, что в дискуссиях чутких
+ и здравомыслящих людей рождается истина.
+
-
За что можно получить дырку в карме и выиграть бан в сообществе
-
- -
-
- Оскорбления, личные нападки, травля и угрозы. В любом виде. Конкретного
- человека или социальной группы — не суть. Агрессия, переход
- на личности и токсичность едва ли способствуют плодотворному общению.
-
-
+ За что можно получить дырку в карме и выиграть бан в сообществе
+
+ -
+
+ Оскорбления, личные нападки, травля и угрозы. В любом виде. Конкретного человека
+ или социальной группы — не суть. Агрессия, переход на личности
+ и токсичность едва ли способствуют плодотворному общению.
+
+
- -
-
- Шовинизм, расизм, сексизм, гомофобия, пропаганда ненависти, педофилии, суицида,
- распространение детской порнографии и другого человеконенавистнического контента.
-
-
+ -
+
+ Шовинизм, расизм, сексизм, гомофобия, пропаганда ненависти, педофилии, суицида,
+ распространение детской порнографии и другого человеконенавистнического контента.
+
+
- -
-
- Спам, реклама, фейкньюз, ссылки на пропагандистские СМИ, вбросы дезинформации,
- специально уводящий от темы флуд, провокации, разжигание конфликтов, намеренный
- срыв дискуссий.
-
-
+ -
+
+ Спам, реклама, фейкньюз, ссылки на пропагандистские СМИ, вбросы дезинформации,
+ специально уводящий от темы флуд, провокации, разжигание конфликтов, намеренный срыв
+ дискуссий.
+
+
- -
-
- Неаргументированная критика и комментарии вроде «отстой», «зачем
- я это увидел/а», «не читал, но осуждаю»,
- «либераху порвало», «лол», «скатились»,
- «первый нах» и тому подобные. Односложные реплики не подразумевают
- возможность обогащающего диалога, не продуктивны и никак не помогают
- авторам делать материалы лучше, а читателям — разобраться.
-
-
-
+ -
+
+ Неаргументированная критика и комментарии вроде «отстой», «зачем
+ я это увидел/а», «не читал, но осуждаю», «либераху
+ порвало», «лол», «скатились», «первый нах»
+ и тому подобные. Односложные реплики не подразумевают возможность обогащающего
+ диалога, не продуктивны и никак не помогают авторам делать материалы лучше,
+ а читателям — разобраться.
+
+
+
-
За что можно получить лучи добра и благодарности в сообществе
-
- -
-
- Вежливость и конструктивность. Мы выступаем
- за конструктивный диалог, аргументированные комментарии и доброжелательное
- отношение друг к другу. Задавайте содержательные вопросы, пишите развернутые
- комментарии, подкрепляйте их аргументами, чтобы диалог был полезен всем участникам,
- помогая глубже понять тему и разобраться в вопросе. И, пожалуйста, уважайте
- собеседника, даже если он вам лично не импонирует: только так получаются
- продуктивные дискуссии.
-
-
+ За что можно получить лучи добра и благодарности в сообществе
+
+ -
+
+ Вежливость и конструктивность. Мы выступаем
+ за конструктивный диалог, аргументированные комментарии и доброжелательное
+ отношение друг к другу. Задавайте содержательные вопросы, пишите развернутые
+ комментарии, подкрепляйте их аргументами, чтобы диалог был полезен всем участникам,
+ помогая глубже понять тему и разобраться в вопросе. И, пожалуйста, уважайте
+ собеседника, даже если он вам лично не импонирует: только так получаются
+ продуктивные дискуссии.
+
+
- -
-
- Обмен знаниями и историями. Осмысленные высказывания по теме
- поста, оригинальные рассуждения, рассказы о личном опыте и проектах, обмен
- профессиональной экспертизой, наблюдения и реальные истории
- из жизни — чем больше мы делимся друг с другом знаниями, тем
- интереснее и плодотворнее становится наше общение. Помните, что каждый вдумчивый
- ответ повышает качество дискуссий в сообществе и делает чтение самиздата ещё
- интереснее.
-
-
+ -
+
+ Обмен знаниями и историями. Осмысленные высказывания по теме
+ поста, оригинальные рассуждения, рассказы о личном опыте и проектах, обмен
+ профессиональной экспертизой, наблюдения и реальные истории
+ из жизни — чем больше мы делимся друг с другом знаниями, тем
+ интереснее и плодотворнее становится наше общение. Помните, что каждый вдумчивый
+ ответ повышает качество дискуссий в сообществе и делает чтение самиздата ещё
+ интереснее.
+
+
- -
-
- Чувство юмора и добродушие. Остроумие и дружелюбие
- не только направляют дискуссии в продуктивное русло, но и улучшают
- настроение. Не вредите негативом, которого в интернете и без нас хватает,
- и не травите на корню классные инициативы — всё великое
- начинается с малого. Мы за поддерживающую и вдохновляющую атмосферу
- в сообществе. Надеемся, вы тоже.
-
-
+ -
+
+ Чувство юмора и добродушие. Остроумие и дружелюбие
+ не только направляют дискуссии в продуктивное русло, но и улучшают
+ настроение. Не вредите негативом, которого в интернете и без нас хватает,
+ и не травите на корню классные инициативы — всё великое
+ начинается с малого. Мы за поддерживающую и вдохновляющую атмосферу
+ в сообществе. Надеемся, вы тоже.
+
+
- -
-
- Благодарность и поддержка. Если публикация вам зашла,
- не стесняйтесь ставить лайки, делиться понравившимися материалами, благодарить
- авторов, читателей, художников и редакторов в комментариях. Цените
- и поддерживайте классные проекты, сильные тексты, новое искусство, осмысленные
- комментарии и вклад других в самиздат — сотрудничество делает нас
- сильнее и усиливает звучание идей и смыслов, которые помогают лучше понимать
- мир.
-
-
-
-
+
+
+ Благодарность и поддержка. Если публикация вам зашла,
+ не стесняйтесь ставить лайки, делиться понравившимися материалами, благодарить
+ авторов, читателей, художников и редакторов в комментариях. Цените
+ и поддерживайте классные проекты, сильные тексты, новое искусство, осмысленные
+ комментарии и вклад других в самиздат — сотрудничество делает нас
+ сильнее и усиливает звучание идей и смыслов, которые помогают лучше понимать
+ мир.
+
+
+
diff --git a/src/components/Pages/about/DogmaPage.tsx b/src/components/Pages/about/DogmaPage.tsx
index b083987d..1407c73d 100644
--- a/src/components/Pages/about/DogmaPage.tsx
+++ b/src/components/Pages/about/DogmaPage.tsx
@@ -7,8 +7,8 @@ export const DogmaPage = () => {
-
Редакционные принципы
-
+
+
Редакционные принципы
Дискурс - журнал с открытой горизонтальной редакцией. Содержание журнала определяется прямым
голосованием его авторов. Мы нередко занимаем различные позиции по разным проблемам, но
diff --git a/src/components/Pages/about/GuidePage.tsx b/src/components/Pages/about/GuidePage.tsx
index a1cfc21d..b1158c60 100644
--- a/src/components/Pages/about/GuidePage.tsx
+++ b/src/components/Pages/about/GuidePage.tsx
@@ -1,9 +1,15 @@
+import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { t } from '../../../utils/intl'
+import { Icon } from '../../Nav/Icon'
export const GuidePage = () => {
const title = t('How it works')
+ const [indexExpanded, setIndexExpanded] = createSignal(true)
+
+ const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
+
return (
{/**/}
@@ -16,64 +22,76 @@ export const GuidePage = () => {
-
-
+
+
+
+
+
+
-
+
Как устроен Дискурс
-
-
- Дискурс — независимый журнал о культуре, науке, искусстве и обществе
- с открытой редакцией. У нас нет главного редактора,
- инвестора и вообще никого, кто бы принимал единоличные решения. Вместо
- традиционных иерархий Дискурс основан на принципах прямой демократии: в нашем
- горизонтальном сообществе все редакционные вопросы решаются открытым голосованием авторов
- журнала. Вот как это работает.
-
-
Как устроен сайт Дискурса
-
Дискурс состоит из четырех основных разделов:
-
- -
-
- Темы — у нас публикуются исследования, обзоры,
- эссе, интервью, репортажи, аналитика и другие материалы о культуре, науке,
- искусстве и обществе.
-
-
- -
-
- Искусство — здесь, например, представлены
- художественные произведения: литература, живопись, музыка, фотографии, видео. Этот
- раздел помогает прозвучать новому искусству, которое создают российские художники,
- писатели, режиссёры и музыканты.
-
-
- {/*
+
+ Дискурс — независимый журнал о культуре, науке, искусстве и обществе
+ с открытой редакцией. У нас нет главного редактора,
+ инвестора и вообще никого, кто бы принимал единоличные решения. Вместо традиционных
+ иерархий Дискурс основан на принципах прямой демократии: в нашем горизонтальном
+ сообществе все редакционные вопросы решаются открытым голосованием авторов журнала. Вот как
+ это работает.
+
+ Как устроен сайт Дискурса
+ Дискурс состоит из четырех основных разделов:
+
+ -
+
+ Темы — у нас публикуются исследования, обзоры, эссе,
+ интервью, репортажи, аналитика и другие материалы о культуре, науке, искусстве
+ и обществе.
+
+
+ -
+
+ Искусство — здесь, например, представлены
+ художественные произведения: литература, живопись, музыка, фотографии, видео. Этот раздел
+ помогает прозвучать новому искусству, которое создают российские художники, писатели,
+ режиссёры и музыканты.
+
+
+ {/*
-
События — в этом разделе
@@ -94,176 +112,174 @@ export const GuidePage = () => {
*/}
-
-
- Материалы в Дискурсе объединяются по темам
- — ключевым словам, которые располагаются в конце материалов и связывают
- материалы по жанрам (например,
- интервью, репортажи,{' '}
- эссе, ликбезы), по тематике (
- кино, философия,{' '}
- история, абсурдизм,{' '}
- секс и т.д.) или в серии (как «
- Законы мира» или «
- За линией Маннергейма»). Темы
- объединяют сотни публикаций, помогают ориентироваться в журнале и следить
- за интересными материалами.
-
+
+
+ Материалы в Дискурсе объединяются по темам
+ — ключевым словам, которые располагаются в конце материалов и связывают
+ материалы по жанрам (например,
+ интервью, репортажи,{' '}
+ эссе, ликбезы), по тематике (
+ кино, философия,{' '}
+ история, абсурдизм,{' '}
+ секс и т.д.) или в серии (как «
+ Законы мира» или «
+ За линией Маннергейма»). Темы объединяют
+ сотни публикаций, помогают ориентироваться в журнале и следить за интересными
+ материалами.
+
-
- Как стать автором журнала
+
+ Как стать автором журнала
+
+ Дискурс объединяет журналистов, активистов, музыкантов, художников, фотографов, режиссеров,
+ философов, ученых и других замечательных людей. Каждый может{' '}
+ прислать
+ свой материал в журнал. Формат и тематика не имеют значения, единственное,
+ что важно — хороший ли материал. Если
+ сообщество поддержит вашу публикацию, она выйдет в журнале и станет доступна
+ тысячам наших читателей.
+
+
+
+ Как проходит голосование
+
+ Все присылаемые в Дискурс материалы попадают в
+ «Редакцию». Это внутренний раздел сайта, где участники сообщества
+ решают, что будет опубликовано в Дискурсе. Как только работа получает одобрение как
+ минимум пятерых авторов открытой редакции, она немедленно публикуется в журнале.
+ Если же материал набирает более 20% голосов «против»,
+ он не выходит и может быть отправлен на доработку. Жестких сроков
+ рассмотрения материалов у нас нет, иногда это занимает час, иногда месяц,
+ обычно — несколько дней.
+
+
+
+ Как только сообщество поддержит публикацию, вы получите приглашение
+ в интернет-редакцию и сможете голосовать за новые материалы.
+
+
+
+ Как мы делаем тексты друг друга лучше
+
+ Дискурс — журнал с совместным редактированием. Совершенствовать тексты нам
+ помогает система ремарок. Вы можете выделить часть текста в любой статье
+ и оставить к ней замечание, вопрос или предложение — автор текста получит
+ совет на почту и сможет его учесть. Так мы устраняем опечатки, неточности
+ и советуем друг другу, как сделать тексты качественнее и интереснее.
+
+
+ Среди участников сообщества есть профессиональные редакторы, которые помогают авторам делать
+ тексты лучше. Если вашему материалу потребуется доработка, они помогут отредактировать текст,
+ подобрать иллюстрации, придумать заголовок и красиво сверстать публикацию. Если
+ вы хотите обсудить текст, прежде чем загрузить материал в интернет-редакцию —
+ разместите его в google-документе, откройте доступ к редактированию по ссылке
+ и напишите нам на
+
+ welcome@discours.io
+
+ .
+
+
+ Если у вас возникают трудности с тем, чтобы подобрать к своему материалу
+ иллюстрации, тоже пишите на
+
+ почту
+
+ — наши коллеги-художники могут вам помочь{' '}
+
+ в режиме совместного редактирования
+
+ .
+
+
+ Что сообщество дает авторам
+
+ -
- Дискурс объединяет журналистов, активистов, музыкантов, художников, фотографов,
- режиссеров, философов, ученых и других замечательных людей. Каждый может{' '}
- прислать
- свой материал в журнал. Формат и тематика не имеют значения, единственное,
- что важно — хороший ли материал.
- Если сообщество поддержит вашу публикацию, она выйдет в журнале и станет
- доступна тысячам наших читателей.
+ Право определять, каким будет журнал. Дискурс — это
+ общественная институция, созданная людьми и ради людей, функционирующая
+ на условиях прямой демократии. Авторы публикуют статьи и художественные проекты,
+ участвуют в обсуждениях, голосуют за работы коллег и таким образом вносят
+ свой вклад в развитие проекта, определяя содержание и направление журнала.
-
-
-
Как проходит голосование
-
- Все присылаемые в Дискурс материалы попадают в
- «Редакцию». Это внутренний раздел сайта, где участники
- сообщества решают, что будет опубликовано в Дискурсе. Как только работа получает
- одобрение как минимум пятерых авторов открытой редакции, она немедленно публикуется
- в журнале. Если же материал набирает более 20% голосов «против»,
- он не выходит и может быть отправлен на доработку. Жестких сроков
- рассмотрения материалов у нас нет, иногда это занимает час, иногда месяц,
- обычно — несколько дней.
-
-
+
+
- Как только сообщество поддержит публикацию, вы получите приглашение
- в интернет-редакцию и сможете голосовать за новые материалы.
+ Возможность обратиться к широкой аудитории. Дискурс читают десятки
+ тысяч людей, и с каждым днем их становится больше.
-
+
+
+
+ Поддержка редакции. Дискурс предоставляет авторам аккредитацию
+ на мероприятия, базу контактов, юридическую поддержку, ознакомление с книжными,
+ кино- и музыкальными новинками до их выхода в свет. Если что-то
+ из этого вам понадобится, пишите на почту{' '}
+
+ welcome@discours.io
+
+ — поможем.
+
+
+
+
+ Пресс-карты для корреспондентов. Три опубликованные статьи позволяют
+ авторам Дискурса получить официальные удостоверения журналистов (пресс-карты)
+ на следующий год. Пресс-карты удостоверяют, что вы журналист и можете
+ пользоваться всеми теми правами, которые гарантирует Закон о СМИ. Кроме того, многие
+ культурные институции (музеи, галереи и др.) предоставляют журналистам право
+ свободного входа.
+
+
+
+
+ Помощь сотен специалистов в разных областях. В основе Дискурса
+ лежит идея совместного редактирования. Участники редакционного сообщества —
+ несколько сотен журналистов, исследователей, художников, литераторов из разных стран
+ — изучают материалы друг друга до публикации и помогают сделать
+ их качественнее и интереснее. Так, в редакции нередко складываются
+ творческие союзы: например, авторов текстов и художников, создающих для них
+ иллюстрации.
+
+
+
+
+ Пространство общения полное выдающихся людей. Дискурс —
+ большое живое сообщество интеллектуалов, разбросанных по всему земному шару. Вступив
+ в редакцию, вы сможете познакомиться со множеством интересных людей,
+ которые определяют повестку завтрашнего дня, вдохновляют окружающих, создают новое
+ и изучают старое, ищут знания и готовы ими делиться, чтобы менять мир
+ в соответствии со своими идеалами.
+
+
+
-
Как мы делаем тексты друг друга лучше
-
- Дискурс — журнал с совместным редактированием. Совершенствовать тексты нам
- помогает система ремарок. Вы можете выделить часть текста в любой статье
- и оставить к ней замечание, вопрос или предложение — автор текста
- получит совет на почту и сможет его учесть. Так мы устраняем опечатки,
- неточности и советуем друг другу, как сделать тексты качественнее и интереснее.
-
-
- Среди участников сообщества есть профессиональные редакторы, которые помогают авторам делать
- тексты лучше. Если вашему материалу потребуется доработка, они помогут отредактировать
- текст, подобрать иллюстрации, придумать заголовок и красиво сверстать публикацию. Если
- вы хотите обсудить текст, прежде чем загрузить материал в
- интернет-редакцию — разместите его в google-документе, откройте доступ
- к редактированию по ссылке и напишите нам на
-
- welcome@discours.io
-
- .
-
-
- Если у вас возникают трудности с тем, чтобы подобрать к своему материалу
- иллюстрации, тоже пишите на
-
- почту
-
- — наши коллеги-художники могут вам помочь{' '}
-
- в режиме совместного редактирования
-
- .
-
-
-
Что сообщество дает авторам
-
- -
-
- Право определять, каким будет журнал. Дискурс — это
- общественная институция, созданная людьми и ради людей, функционирующая
- на условиях прямой демократии. Авторы публикуют статьи и художественные
- проекты, участвуют в обсуждениях, голосуют за работы коллег и таким
- образом вносят свой вклад в развитие проекта, определяя содержание
- и направление журнала.
-
-
- -
-
- Возможность обратиться к широкой аудитории. Дискурс читают десятки
- тысяч людей, и с каждым днем их становится больше.
-
-
- -
-
- Поддержка редакции. Дискурс предоставляет авторам аккредитацию
- на мероприятия, базу контактов, юридическую поддержку, ознакомление
- с книжными, кино- и музыкальными новинками до их выхода в свет.
- Если что-то из этого вам понадобится, пишите на почту{' '}
-
- welcome@discours.io
-
- — поможем.
-
-
- -
-
- Пресс-карты для корреспондентов. Три опубликованные статьи позволяют
- авторам Дискурса получить официальные удостоверения журналистов (пресс-карты)
- на следующий год. Пресс-карты удостоверяют, что вы журналист и можете
- пользоваться всеми теми правами, которые гарантирует Закон о СМИ. Кроме того,
- многие культурные институции (музеи, галереи и др.) предоставляют журналистам право
- свободного входа.
-
-
- -
-
- Помощь сотен специалистов в разных областях. В основе
- Дискурса лежит идея совместного редактирования. Участники редакционного
- сообщества — несколько сотен журналистов, исследователей, художников,
- литераторов из разных стран — изучают материалы друг друга до публикации
- и помогают сделать их качественнее и интереснее. Так, в редакции
- нередко складываются творческие союзы: например, авторов текстов и художников,
- создающих для них иллюстрации.
-
-
- -
-
- Пространство общения полное выдающихся людей. Дискурс —
- большое живое сообщество интеллектуалов, разбросанных по всему земному шару.
- Вступив в редакцию, вы сможете познакомиться со множеством интересных
- людей, которые определяют повестку завтрашнего дня, вдохновляют окружающих, создают
- новое и изучают старое, ищут знания и готовы ими делиться, чтобы менять мир
- в соответствии со своими идеалами.
-
-
-
-
-
-
- За свежими публикациями Дискурса можно следить не только на сайте,
- но и на страницах в
-
- Фейсбуке
-
- ,{' '}
-
- ВКонтакте
- {' '}
- и
-
- Телеграме
-
- . А ещё раз в месяц мы отправляем почтовую рассылку{' '}
- с дайджестом лучших материалов.
-
-
- Если вы хотите сотрудничать, что-то обсудить или предложить — пожалуйста, пишите
- на
-
- welcome@discours.io
-
- . Мы обязательно ответим.
-
-
+
+
+ За свежими публикациями Дискурса можно следить не только на сайте,
+ но и на страницах в
+
+ Фейсбуке
+
+ ,{' '}
+
+ ВКонтакте
+ {' '}
+ и
+
+ Телеграме
+
+ . А ещё раз в месяц мы отправляем почтовую рассылку{' '}
+ с дайджестом лучших материалов.
+
+
+ Если вы хотите сотрудничать, что-то обсудить или предложить — пожалуйста, пишите
+ на
+
+ welcome@discours.io
+
+ . Мы обязательно ответим.
+
diff --git a/src/components/Pages/about/HelpPage.tsx b/src/components/Pages/about/HelpPage.tsx
index f50fe2a8..34180213 100644
--- a/src/components/Pages/about/HelpPage.tsx
+++ b/src/components/Pages/about/HelpPage.tsx
@@ -1,9 +1,15 @@
+import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { Donate } from '../../Discours/Donate'
+import { Icon } from '../../Nav/Icon'
// const title = t('Support us')
export const HelpPage = () => {
+ const [indexExpanded, setIndexExpanded] = createSignal(true)
+
+ const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
+
return (
{/*Здесь можно поддержать Дискурс материально.*/}
@@ -13,134 +19,145 @@ export const HelpPage = () => {
-
-
+
+
+
+
+
+
-
+
Как вы можете поддержать Дискурс?
-
-
- Дискурс — уникальное независимое издание с горизонтальной редакцией,
- существующее в интересах своих читателей. Ваша поддержка действительно много
- значит — не только для редакции Дискурса, но и для сохранения
- свободной мысли и некоммерческого искусства в нашем обществе.
-
-
- Дискурс существует на добровольных началах. Никакой медиахолдинг, фонд или
- государственная структура не финансирует нас — благодаря этому мы можем
- писать о том, что важно, а не о том, что выгодно. Сообщество наших
- волонтеров ежедневно трудится, чтобы рассказывать вам интересные, не освещенные другими
- изданиями истории — но мы не сможем делать это без вашей помощи.
- Пожертвования читателей составляют основу нашего бюджета и позволяют нам существовать.
-
-
- Если вам нравится то, что мы делаем и вы хотите, чтобы Дискурс
- продолжался, пожалуйста, поддержите проект.
-
-
-
-
-
+
+ Дискурс — уникальное независимое издание с горизонтальной редакцией,
+ существующее в интересах своих читателей. Ваша поддержка действительно много
+ значит — не только для редакции Дискурса, но и для сохранения
+ свободной мысли и некоммерческого искусства в нашем обществе.
+
+
+ Дискурс существует на добровольных началах. Никакой медиахолдинг, фонд или
+ государственная структура не финансирует нас — благодаря этому мы можем
+ писать о том, что важно, а не о том, что выгодно. Сообщество наших
+ волонтеров ежедневно трудится, чтобы рассказывать вам интересные, не освещенные другими
+ изданиями истории — но мы не сможем делать это без вашей помощи.
+ Пожертвования читателей составляют основу нашего бюджета и позволяют нам существовать.
+
+
+ Если вам нравится то, что мы делаем и вы хотите, чтобы Дискурс
+ продолжался, пожалуйста, поддержите проект.
+
+
+
+
-
На что пойдут деньги?
-
- Ваши пожертвования пойдут на оплату серверов, содержание офиса, зарплату редакции
- и налоги, оплату юридического сопровождения и труда бухгалтера, совершенствование
- сайта, аренду помещения для открытой редакции, на печать альманаха Дискурс
- с лучшими текстами авторов за полгода, а также на другие редакционные
- и технические расходы.
-
-
Ваша помощь позволит нам
-
- -
-
Оставаться бесплатным изданием.
-
- Мы делаем открытый журнал для всех желающих, а также собираем искусство лучших
- авторов по всему миру. Ваша поддержка позволяет нам становиться лучше.
-
-
- -
-
Создавать еще больше контента.
-
- Каждый день к нам присоединяются новые люди, и чем больше нас становится, тем
- больше мы творим и строже оцениваем результаты творчества друг друга.
- В результате повышается и количество, и качество контента. Каждый день мы
- трудимся, чтобы открывать нашим читателям новые грани окружающего мира.
-
-
- -
-
Развивать форматы и расширять деятельность Дискурса.
-
- Мы создаем различные спецпроекты и регулярно проводим необычные мероприятия.
- Мы хотим приносить пользу человечеству всеми возможными способами.
-
-
- -
-
Модернизировать сайт.
-
- Мы совершенствуем платформу и стараемся сделать проект максимально удобным для
- вас. Мы работаем над мобильной версией, новым дизайном, фукционалом, системой
- регистрации, навигации и рекомендаций, которые сделают наше общение еще
- увлекательней.
-
-
- -
-
Выпускать альманах.
-
- Выпускать раз в полугодие печатный альманах Дискурс с 33 лучшими текстами
- сайта.
-
-
- -
-
Захватить весь мир
- и принести «Дискурс» в каждый дом.
-
-
-
Войдите в попечительский совет Дискурса
-
- Вы хотите сделать крупное пожертвование? Станьте попечителем Дискурса —
-
- напишите нам
-
- , мы будем рады единомышленникам.
-
-
Как ещё можно поддержать Дискурс?
-
- Есть много других способов поддержать Дискурс и труд наших авторов. Например,
- вы можете периодически рассказывать о проекте своим друзьям в соцсетях,
- делиться хорошими материалами или — что еще лучше — публиковать свои
- статьи в «Дискурсе». Но главное, что вы можете сделать для
- Дискурса, — читать нас. Мы вкладываем в журнал душу, и внимание каждого
- читателя убеждает нас в правильности выбранного пути. Не переключайтесь.
-
-
- Если вы хотите помочь проекту, но у вас возникли вопросы, напишите нам письмо
- по адресу{' '}
-
- welcome@discours.io
-
- .
-
+
На что пойдут деньги?
+
+ Ваши пожертвования пойдут на оплату серверов, содержание офиса, зарплату редакции
+ и налоги, оплату юридического сопровождения и труда бухгалтера, совершенствование
+ сайта, аренду помещения для открытой редакции, на печать альманаха Дискурс с лучшими
+ текстами авторов за полгода, а также на другие редакционные и технические
+ расходы.
+
+
Ваша помощь позволит нам
+
+ -
+
Оставаться бесплатным изданием.
+
+ Мы делаем открытый журнал для всех желающих, а также собираем искусство лучших
+ авторов по всему миру. Ваша поддержка позволяет нам становиться лучше.
+
+
+ -
+
Создавать еще больше контента.
+
+ Каждый день к нам присоединяются новые люди, и чем больше нас становится, тем
+ больше мы творим и строже оцениваем результаты творчества друг друга.
+ В результате повышается и количество, и качество контента. Каждый день мы
+ трудимся, чтобы открывать нашим читателям новые грани окружающего мира.
+
+
+ -
+
Развивать форматы и расширять деятельность Дискурса.
+
+ Мы создаем различные спецпроекты и регулярно проводим необычные мероприятия.
+ Мы хотим приносить пользу человечеству всеми возможными способами.
+
+
+ -
+
Модернизировать сайт.
+
+ Мы совершенствуем платформу и стараемся сделать проект максимально удобным для
+ вас. Мы работаем над мобильной версией, новым дизайном, фукционалом, системой
+ регистрации, навигации и рекомендаций, которые сделают наше общение еще
+ увлекательней.
+
+
+ -
+
Выпускать альманах.
+
+ Выпускать раз в полугодие печатный альманах Дискурс с 33 лучшими текстами
+ сайта.
+
+
+ -
+
Захватить весь мир
+ и принести «Дискурс» в каждый дом.
+
+
+
Войдите в попечительский совет Дискурса
+
+ Вы хотите сделать крупное пожертвование? Станьте попечителем Дискурса —
+
+ напишите нам
+
+ , мы будем рады единомышленникам.
+
+
Как ещё можно поддержать Дискурс?
+
+ Есть много других способов поддержать Дискурс и труд наших авторов. Например,
+ вы можете периодически рассказывать о проекте своим друзьям в соцсетях,
+ делиться хорошими материалами или — что еще лучше — публиковать свои
+ статьи в «Дискурсе». Но главное, что вы можете сделать для
+ Дискурса, — читать нас. Мы вкладываем в журнал душу, и внимание каждого
+ читателя убеждает нас в правильности выбранного пути. Не переключайтесь.
+
+
+ Если вы хотите помочь проекту, но у вас возникли вопросы, напишите нам письмо
+ по адресу{' '}
+
+ welcome@discours.io
+
+ .
+
diff --git a/src/components/Pages/about/ManifestPage.tsx b/src/components/Pages/about/ManifestPage.tsx
index a6453f43..3e7101af 100644
--- a/src/components/Pages/about/ManifestPage.tsx
+++ b/src/components/Pages/about/ManifestPage.tsx
@@ -1,12 +1,18 @@
+import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
import { Modal } from '../../Nav/Modal'
import { Feedback } from '../../Discours/Feedback'
import Subscribe from '../../Discours/Subscribe'
import Opener from '../../Nav/Opener'
+import { Icon } from '../../Nav/Icon'
// title={t('Manifest')}
export const ManifestPage = () => {
+ const [indexExpanded, setIndexExpanded] = createSignal(true)
+
+ const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
+
return (
@@ -17,80 +23,93 @@ export const ManifestPage = () => {
-
-
+
+
+
+
+
+
-
+
Манифест
-
-
- Дискурс — независимый художественно-аналитический журнал с горизонтальной
- редакцией, основанный на принципах свободы слова, прямой демократии и совместного
- редактирования. Дискурс создаётся открытым медиасообществом ученых, журналистов, музыкантов,
- писателей, предпринимателей, философов, инженеров, художников и специалистов
- со всего мира, объединившихся, чтобы вместе делать общий журнал и объяснять
- с разных точек зрения мозаичную картину современности.
-
-
- Мы пишем о культуре, науке и обществе, рассказываем о новых идеях
- и современном искусстве, публикуем статьи, исследования, репортажи, интервью людей, чью
- прямую речь стоит услышать, и работы художников из разных стран —
- от фильмов и музыки до живописи и фотографии. Помогая друг другу делать
- публикации качественнее и общим голосованием выбирая лучшие материалы для журнала,
- мы создаём новую горизонтальную журналистику, чтобы честно рассказывать о важном
- и интересном.
-
-
- Редакция Дискурса открыта для всех: у нас нет цензуры, запретных тем
- и идеологических рамок. Каждый может прислать материал{' '}
- в журнал и присоединиться к редакции. Предоставляя
- трибуну для независимой журналистики и художественных проектов, мы помогаем людям
- рассказывать свои истории так, чтобы они были услышаны. Мы убеждены: чем больше голосов
- будет звучать на Дискурсе, тем громче в полифонии мнений будет слышна истина.
-
-
+
+ Дискурс — независимый художественно-аналитический журнал с горизонтальной
+ редакцией, основанный на принципах свободы слова, прямой демократии и совместного
+ редактирования. Дискурс создаётся открытым медиасообществом ученых, журналистов, музыкантов,
+ писателей, предпринимателей, философов, инженеров, художников и специалистов
+ со всего мира, объединившихся, чтобы вместе делать общий журнал и объяснять
+ с разных точек зрения мозаичную картину современности.
+
+
+ Мы пишем о культуре, науке и обществе, рассказываем о новых идеях
+ и современном искусстве, публикуем статьи, исследования, репортажи, интервью людей, чью
+ прямую речь стоит услышать, и работы художников из разных стран —
+ от фильмов и музыки до живописи и фотографии. Помогая друг другу делать
+ публикации качественнее и общим голосованием выбирая лучшие материалы для журнала,
+ мы создаём новую горизонтальную журналистику, чтобы честно рассказывать о важном
+ и интересном.
+
+
+ Редакция Дискурса открыта для всех: у нас нет цензуры, запретных тем
+ и идеологических рамок. Каждый может прислать материал{' '}
+ в журнал и присоединиться к редакции. Предоставляя
+ трибуну для независимой журналистики и художественных проектов, мы помогаем людям
+ рассказывать свои истории так, чтобы они были услышаны. Мы убеждены: чем больше голосов
+ будет звучать на Дискурсе, тем громче в полифонии мнений будет слышна истина.
+
Как участвовать в самиздате
-
-
- Дискурс создается открытым сообществом энтузиастов новой
- независимой журналистики. Участвовать в открытой редакции и помогать журналу можно
- следующими способами:
-
-
Предлагать материалы
+
+ Дискурс создается открытым сообществом энтузиастов новой
+ независимой журналистики. Участвовать в открытой редакции и помогать журналу можно
+ следующими способами:
+
+
+
+ Предлагать материалы
+
Создавайте свои статьи и художественные работы —
лучшие из них будут опубликованы в журнале. Дискурс — некоммерческое
@@ -98,7 +117,12 @@ export const ManifestPage = () => {
поддержку редакции, право голоса, множество других
возможностей и читателей по всему миру.
- Поддерживать проект
+
+
+
+
+ Поддерживать проект
+
Дискурс существует на пожертвования читателей. Если вам нравится журнал, пожалуйста,
@@ -106,7 +130,12 @@ export const ManifestPage = () => {
поддержите нашу работу. Ваши пожертвования пойдут на выпуск
новых материалов, оплату серверов, труда программистов, дизайнеров и редакторов.
- Сотрудничать с журналом
+
+
+
+
+ Сотрудничать с журналом
+
Мы всегда открыты для сотрудничества и рады единомышленникам. Если вы хотите помогать
журналу с редактурой, корректурой, иллюстрациями, переводами, версткой, подкастами,
@@ -125,7 +154,12 @@ export const ManifestPage = () => {
и медиаинструментов находится{' '}
в свободном доступе на GitHub.
- Как еще можно помочь
+
+
+
+
+ Как еще можно помочь
+
Советуйте Дискурс друзьям и знакомым. Обсуждайте и распространяйте наши
публикации — все материалы открытой редакции можно читать и перепечатывать
@@ -141,19 +175,19 @@ export const ManifestPage = () => {
интересными темами, о которых хотели бы узнать больше, и историями, которые нужно
рассказать.
-
+
Будем на связи
-
+
Если вы хотите предложить материал, сотрудничать, рассказать о проблеме, которую нужно
осветить, сообщить об ошибке или баге, что-то обсудить, уточнить или посоветовать,
пожалуйста, напишите нам здесь или на почту{' '}
welcome@discours.io. Мы обязательно ответим
и постараемся реализовать все хорошие задумки.
-
+
diff --git a/src/components/Pages/about/PartnersPage.tsx b/src/components/Pages/about/PartnersPage.tsx
index a9eef34d..4800e380 100644
--- a/src/components/Pages/about/PartnersPage.tsx
+++ b/src/components/Pages/about/PartnersPage.tsx
@@ -8,8 +8,9 @@ export const PartnersPage = () => {
-
{t('Partners')}
-
+
+
{t('Partners')}
+
diff --git a/src/components/Pages/about/PrinciplesPage.tsx b/src/components/Pages/about/PrinciplesPage.tsx
index 4691bbf1..e52eed41 100644
--- a/src/components/Pages/about/PrinciplesPage.tsx
+++ b/src/components/Pages/about/PrinciplesPage.tsx
@@ -7,173 +7,168 @@ export const PrinciplesPage = () => {
-
+
{title}
-
-
- -
-
- Горизонтальность. Мы все разные, и это классно. Вертикалей
- в мире достаточно, мы — горизонтальное сообщество и ценим наши
- различия, потому что знаем — в них наша сила. Благодаря разнообразию
- сотен голосов, усиливающих друг друга, в сообществе складывается неповторимая
- синергия, которая помогает вместе достигать большего.
-
-
- -
-
- Многоголосие. Мы ценим свободу слова и аргументированные
- мнения. Предоставляя трибуну каждому, кому есть что сказать, самиздат отражает полифонию
- позиций, знаний и опыта, которые открывают более полную картину реальности.
-
-
- -
-
- Взаимопомощь. Мы помогаем друг другу, потому что хотим, чтобы
- в мире было еще больше хорошего. Обсуждая что-то, мы всегда интересуемся, чем
- можем помочь. В самиздате можно найти специалистов практически в любых сферах
- и получить поддержку от сотен людей. Благодаря коллективной экспертизе
- глобального сообщества в самиздате выходят крутейшие публикации, которыми можно
- вечно гордиться.
-
-
- -
-
- Взаимоуважение. Мы ценим, искренне уважаем друг друга
- и вместо борщевиков враждебности культивируем цветы добра, мира, знания
- и юмора. Нам некогда доказывать друг другу, кто круче. Гораздо приятнее
- сотрудничать, помогать и создавать что-то важное, интересное и полезное.
-
-
- -
-
- Созидание. Мы создаем, потому что любим создавать. Мы открыто
- делимся опытом, дарим идеи, обмениваемся мнениями и благодарим за критику,
- используя ее для совершенствования мастерства и саморазвития. Мы знаем,
- что мир не идеальное место, и делаем всё возможное, чтобы он стал лучше.
-
-
-
-
+
+ -
+
+ Горизонтальность. Мы все разные, и это классно. Вертикалей
+ в мире достаточно, мы — горизонтальное сообщество и ценим наши
+ различия, потому что знаем — в них наша сила. Благодаря разнообразию сотен
+ голосов, усиливающих друг друга, в сообществе складывается неповторимая синергия,
+ которая помогает вместе достигать большего.
+
+
+ -
+
+ Многоголосие. Мы ценим свободу слова и аргументированные
+ мнения. Предоставляя трибуну каждому, кому есть что сказать, самиздат отражает полифонию
+ позиций, знаний и опыта, которые открывают более полную картину реальности.
+
+
+ -
+
+ Взаимопомощь. Мы помогаем друг другу, потому что хотим, чтобы
+ в мире было еще больше хорошего. Обсуждая что-то, мы всегда интересуемся, чем
+ можем помочь. В самиздате можно найти специалистов практически в любых сферах
+ и получить поддержку от сотен людей. Благодаря коллективной экспертизе
+ глобального сообщества в самиздате выходят крутейшие публикации, которыми можно вечно
+ гордиться.
+
+
+ -
+
+ Взаимоуважение. Мы ценим, искренне уважаем друг друга и вместо
+ борщевиков враждебности культивируем цветы добра, мира, знания и юмора. Нам некогда
+ доказывать друг другу, кто круче. Гораздо приятнее сотрудничать, помогать и создавать
+ что-то важное, интересное и полезное.
+
+
+ -
+
+ Созидание. Мы создаем, потому что любим создавать. Мы открыто
+ делимся опытом, дарим идеи, обмениваемся мнениями и благодарим за критику,
+ используя ее для совершенствования мастерства и саморазвития. Мы знаем, что
+ мир не идеальное место, и делаем всё возможное, чтобы он стал лучше.
+
+
+
Как участвовать в самиздате
-
-
- Открытая редакция объединяет сотни потрясающих людей со всего мира, которые делают
- крутейшие вещи. Это пространство, где доверяют, вдохновляют, исследуют и создают новое
- вместе. Поскольку все в сообществе очень разные, как-то мы собрались и решили
- зафиксировать базовые ценности открытой редакции, а заодно придумали универсальные
- правила взаимодействия, чтобы общение было не только плодотворным,
- но и приятным для всех участников сообщества.
-
-
- -
-
- Действуем, помогаем и делимся. В редакции мы создаем
- свои проекты и помогаем другим создавать свои — советами, делом,
- участием, вовлеченностью. Мы открыто делимся опытом, мнениями и идеями, потому
- что ценим силу сотрудничества и знаем, что идеи реализуются скорее, лучше
- и веселее, если над ними трудиться сообща.
-
-
+
+ Открытая редакция объединяет сотни потрясающих людей со всего мира, которые делают
+ крутейшие вещи. Это пространство, где доверяют, вдохновляют, исследуют и создают новое
+ вместе. Поскольку все в сообществе очень разные, как-то мы собрались и решили
+ зафиксировать базовые ценности открытой редакции, а заодно придумали универсальные
+ правила взаимодействия, чтобы общение было не только плодотворным,
+ но и приятным для всех участников сообщества.
+
+
+ -
+
+ Действуем, помогаем и делимся. В редакции мы создаем свои
+ проекты и помогаем другим создавать свои — советами, делом, участием,
+ вовлеченностью. Мы открыто делимся опытом, мнениями и идеями, потому что ценим
+ силу сотрудничества и знаем, что идеи реализуются скорее, лучше и веселее, если
+ над ними трудиться сообща.
+
+
- -
-
- Общаемся дружелюбно. Помните, по ту сторону монитора
- находятся реальные люди. Неуважение ранит других так же, как ранило бы вас
- самих. Поэтому не стоит кричать (даже капслоком), заполнять эфир желчью
- и бросаться грубостями — так вы рискуете не только растерять
- доверие окружающих, но и остаться непонятым.
-
-
+ -
+
+ Общаемся дружелюбно. Помните, по ту сторону монитора находятся
+ реальные люди. Неуважение ранит других так же, как ранило бы вас самих. Поэтому
+ не стоит кричать (даже капслоком), заполнять эфир желчью и бросаться
+ грубостями — так вы рискуете не только растерять доверие окружающих,
+ но и остаться непонятым.
+
+
- -
-
- Критикуем и реагируем конструктивно. Самиздат про то, чтобы
- разбираться в сложных вещах всем сообществом, поэтому мы тактично и без
- агрессии делимся мнениями, стараясь убедительно аргументировать позиции.
- И с благодарностью принимаем критику, используя ее для улучшения наших
- проектов. Мы верим, что каждый участник сообщества имеет добрые намерения,
- и придерживаемся принципов доброжелательной критики, стараемся делиться
- советами — лучшим средством для самосовершенствования. Обоснованная критика
- помогает и адресату, и всем участникам сообщества досконально изучить тему
- и глубже разобраться в проблеме.
-
-
+ -
+
+ Критикуем и реагируем конструктивно. Самиздат про то, чтобы
+ разбираться в сложных вещах всем сообществом, поэтому мы тактично и без
+ агрессии делимся мнениями, стараясь убедительно аргументировать позиции.
+ И с благодарностью принимаем критику, используя ее для улучшения наших
+ проектов. Мы верим, что каждый участник сообщества имеет добрые намерения,
+ и придерживаемся принципов доброжелательной критики, стараемся делиться
+ советами — лучшим средством для самосовершенствования. Обоснованная критика
+ помогает и адресату, и всем участникам сообщества досконально изучить тему
+ и глубже разобраться в проблеме.
+
+
- -
-
- Решаем трудности не агрессией, а диалогом. Обесценивать
- мнения и оскорблять других людей только потому, что вы с ними
- не согласны, — не лучший способ донести свою точку зрения. Конечно,
- важно высказаться, если вас что-то не устраивает и откровенно бесит.
- Но прежде чем сжигать оппонента гневом, попробуйте понять, почему этот
- «нехороший человек» так поступает. Возможно, аргументы собеседника окажутся
- убедительными или вам удастся изменить его мнение. В любом случае конфликты
- решаются в диалогах и проходят, а налаженное взаимопонимание останется
- надолго.
-
-
+ -
+
+ Решаем трудности не агрессией, а диалогом. Обесценивать мнения
+ и оскорблять других людей только потому, что вы с ними
+ не согласны, — не лучший способ донести свою точку зрения. Конечно,
+ важно высказаться, если вас что-то не устраивает и откровенно бесит.
+ Но прежде чем сжигать оппонента гневом, попробуйте понять, почему этот
+ «нехороший человек» так поступает. Возможно, аргументы собеседника окажутся
+ убедительными или вам удастся изменить его мнение. В любом случае конфликты решаются
+ в диалогах и проходят, а налаженное взаимопонимание останется надолго.
+
+
- -
-
- Не переходим на личности — это признак токсичности
- . Всегда мудрее обсуждать точку зрения человека, а не его самого, даже если
- он вам не импонирует. Предвзятое отношение ограничивает кругозор, добавляет
- преждевременные морщины и не помогает окружающим стать лучше. Вежливость
- и взаимоуважение — краеугольная основа вдумчивых и осмысленных
- дискуссий.
-
-
+ -
+
+ Не переходим на личности — это признак токсичности.
+ Всегда мудрее обсуждать точку зрения человека, а не его самого, даже если
+ он вам не импонирует. Предвзятое отношение ограничивает кругозор, добавляет
+ преждевременные морщины и не помогает окружающим стать лучше. Вежливость
+ и взаимоуважение — краеугольная основа вдумчивых и осмысленных
+ дискуссий.
+
+
- -
-
- Благодарим за помощь. Благодарите коллег даже за самые,
- казалось бы, простые вещи. «Спасибо» не зря называют волшебным
- словом — на искренней благодарности держится любое подлинное
- сотрудничество. Поддержка воодушевляет на новые подвиги и напоминает, что мир
- делают прекрасным не машины, а живые люди.
-
-
+ -
+
+ Благодарим за помощь. Благодарите коллег даже за самые,
+ казалось бы, простые вещи. «Спасибо» не зря называют волшебным
+ словом — на искренней благодарности держится любое подлинное
+ сотрудничество. Поддержка воодушевляет на новые подвиги и напоминает, что мир
+ делают прекрасным не машины, а живые люди.
+
+
- -
-
- Даем еще один шанс. Все совершают ошибки, и за один проступок
- не стоит вычеркивать людей из жизни. Ошибки нужны, чтобы на них учиться
- и делать выводы. Однако если многократно и систематически нарушать правила
- сообщества, наверняка можно заслужить минусы в карму от других участников
- и потерять доступ к сообществу.
-
-
+ -
+
+ Даем еще один шанс. Все совершают ошибки, и за один проступок
+ не стоит вычеркивать людей из жизни. Ошибки нужны, чтобы на них учиться
+ и делать выводы. Однако если многократно и систематически нарушать правила
+ сообщества, наверняка можно заслужить минусы в карму от других участников
+ и потерять доступ к сообществу.
+
+
- -
-
- Вместе создаем идеальную среду общения. Открытая редакция —
- это утопическое пространство обогащающей и осмысленной коммуникации. Атмосфера
- горизонтального сообщества складывается из действий каждого, поэтому
- мы действуем так, чтобы способствовать сотворчеству, коллективному познанию
- и развитию самиздата и нашей альтернативной интеллектуальной медиасреды.
-
-
+ -
+
+ Вместе создаем идеальную среду общения. Открытая редакция —
+ это утопическое пространство обогащающей и осмысленной коммуникации. Атмосфера
+ горизонтального сообщества складывается из действий каждого, поэтому
+ мы действуем так, чтобы способствовать сотворчеству, коллективному познанию
+ и развитию самиздата и нашей альтернативной интеллектуальной медиасреды.
+
+
- -
-
- Помним, что всё в сообществе зависит от нас. Если нам чего-то
- не хватает, мы начинаем действовать — рассказываем об идее,
- находим единомышленников, готовим и запускаем проект. Так в сообществе
- становится на одну крутую активность больше. Так появилось наше сообщество. Так
- появился самиздат и все проекты открытой редакции. Чтобы в сообществе
- случилось что-то прекрасное, достаточно просто положить этому начало.
-
-
-
-
+
+
+ Помним, что всё в сообществе зависит от нас. Если нам чего-то
+ не хватает, мы начинаем действовать — рассказываем об идее,
+ находим единомышленников, готовим и запускаем проект. Так в сообществе
+ становится на одну крутую активность больше. Так появилось наше сообщество. Так
+ появился самиздат и все проекты открытой редакции. Чтобы в сообществе случилось
+ что-то прекрасное, достаточно просто положить этому начало.
+
+
+
diff --git a/src/components/Pages/about/ProjectsPage.tsx b/src/components/Pages/about/ProjectsPage.tsx
index 37a3e251..63a7e55e 100644
--- a/src/components/Pages/about/ProjectsPage.tsx
+++ b/src/components/Pages/about/ProjectsPage.tsx
@@ -8,8 +8,9 @@ export const ProjectsPage = () => {
-
{t('Projects')}
-
+
+
{t('Projects')}
+
diff --git a/src/components/Pages/about/TermsOfUsePage.tsx b/src/components/Pages/about/TermsOfUsePage.tsx
index a8b20121..3a300c00 100644
--- a/src/components/Pages/about/TermsOfUsePage.tsx
+++ b/src/components/Pages/about/TermsOfUsePage.tsx
@@ -1,8 +1,14 @@
+import { createSignal, Show } from 'solid-js'
import { MainLayout } from '../../Layouts/MainLayout'
+import { Icon } from '../../Nav/Icon'
// const title = t('Terms of use')
export const TermsOfUsePage = () => {
+ const [indexExpanded, setIndexExpanded] = createSignal(true)
+
+ const toggleIndexExpanded = () => setIndexExpanded((oldExpanded) => !oldExpanded)
+
return (
{/**/}
@@ -11,254 +17,260 @@ export const TermsOfUsePage = () => {
{/**/}
-
-
+
+
+
+
+
+
-
+
Пользовательское соглашение
-
-
- Дискурс — это сообщество творческих людей, объединенных идеей делать интересный
- журнал для всех желающих. Авторы Дискурса сообща посредством прямого голосования определяют
- содержание журнала.
-
-
Для того, чтобы Дискурс работал без помех, разработаны настоящие Правила.
-
Определения
-
- Сайт — портал discours.io
-
-
- Пользователь — лицо, пользующееся Сайтом, либо юридическое лицо,
- обладающее правами на интеллектуальную собственность.
-
-
- Публикация контента — размещение Пользователем посредством Сайта
- объектов авторских прав и другой информации для других пользователей.
-
-
- Издательство — администрация сайта, которая занимается
- технической и издательской деятельностью для обеспечения функционирования Сайта
- и Альманаха. Издательство не вмешивается в принятие редакционных решений
- авторским сообществом.
-
-
- Альманах «Дискурс» (свидетельство о регистрации СМИ: ПИ
- № ФС77-63947 от 18.12.15) — печатное периодическое издание, которое
- выходит раз в год и состоит из лучших публикаций на Сайте за это
- время.
-
-
Авторские права
-
- -
-
- Вся информация на сайте (включая тексты, изображения, видеоматериалы, аудиозаписи,
- программный код, дизайн сайта и т.д.) является объектом интеллектуальной
- собственности ее правообладателей и охраняется законодательством РФ.
-
-
- -
-
- Публикуя контент на сайте, Пользователь на безвозмездной основе предоставляет
- Издательству право на воспроизведение, распространение, перевод, редактирование
- контента. Данное право предоставляется Издательству на весь срок действия авторских
- прав Пользователя.
-
-
- -
-
- Пользователь предоставляет Издательству право редактировать контент, в том числе
- вносить в него изменения, сокращения и дополнения, снабжать его иллюстрациями
- и пояснениями, исправлять ошибки и уточнять фактические сведения, при условии,
- что этим не искажается авторский замысел.
-
-
- -
-
- Обнародование контента осуществляется Издательством в соответствии с условиями
- лицензии{' '}
-
- Creative Commons BY-NC-ND 4.0
-
- . Все материалы сайта предназначены исключительно для личного некоммерческого
- использования. Права на дизайн и программный код сайта принадлежат
- Издательству.
-
-
- -
-
- Все аудиовизуальные произведения являются собственностью своих авторов
- и правообладателей и используются только в образовательных
- и информационных целях. Если вы являетесь собственником того или иного
- произведения и не согласны с его размещением на сайте, пожалуйста,
- напишите на
-
- welcome@discours.io
-
- .
-
-
- -
-
- Цитирование, распространение, доведение до всеобщего сведения материалов Cайта
- приветствуется. При использовании материалов сайта необходимо указать имя автора
- и активную ссылку на материал на Сайте.
-
-
-
-
Правила поведения
-
- -
-
- Находясь на Сайте, Пользователь подтверждает свое совершеннолетие,
- правоспособность, а также согласие с настоящими Правилами и политикой
- конфиденциальности и готовность нести полную ответственность за их соблюдение.
-
-
- -
-
На сайте запрещено:
-
- -
- Публиковать контент, авторские права на который принадлежат третьим лицам, без
- согласия этих лиц. Если авторские права на контент принадлежат нескольким лицам,
- то его публикация предполагает согласие их всех.
-
- - Размещать коммерческую и политическую рекламу.
- -
- Целенаправленно препятствовать нормальному функционированию сообщества и сайта
- discours.io
-
- - Выдавать себя за другого человека и представляться его именем.
- -
- Размещать информацию, которая не соответствует целям создания Сайта, ущемляет
- интересы других пользователей или третьих лиц, нарушает законы Российской Федерации.
-
-
-
- -
-
- Пользователь несет всю ответственность за содержание публикуемого контента
- и свое взаимодействие с другими пользователями, и обязуется возместить
- все расходы в случае предъявления каких-либо претензий третьими лицами.
- Издательство не несет ответственности за содержание публикуемой пользователями
- информации, в том числе за размещенные на сайте комментарии. Переписка
- между Пользователем и Издательством считается юридически значимой. Настоящие
- Правила могут быть изменены Издательством, изменения вступают в силу с момента
- публикации на Сайте.
-
-
- -
-
- Если Пользователь очевидно и целенаправленно нарушает правила, Издательство может
- и принять в отношении автора следующие меры: вынести предупреждение
- и обязать автора устранить допущенное нарушение, удалить контент, нарушающий
- правила, заблокировать или удалить аккаунт нарушителя.
-
-
-
-
Политика конфиденциальности
-
- -
-
Сайт может собирать у пользователей следующие данные:
-
- -
-
- Данные, которые пользователи сообщают о себе сами при подаче заявки,
- регистрации, авторизации или заполнения профиля, в том числе ФИО
- и контактную информацию. Конфиденциальные данные, такие как идентификатор
- и электронный адрес, используются для идентификации пользователя. Данные
- профиля, размещённые публично по желанию пользователя, которое выражается
- фактом их предоставления, используется для демонстрации другим пользователям
- той информации о себе, которую пользователь готов предоставить.
-
-
- -
-
- Данные, собранные автоматическим путем, такие, как cookie-файлы. Эти
- неперсонализированные данные могут использоваться для сбора статистики
- и улучшения работы сайта.
-
-
-
-
- -
-
- Издательство обеспечивает конфиденциальность персональных данных и применяет все
- необходимые организационные и технические меры по их защите.
-
-
- -
-
- По желанию пользователя Издательство готово удалить любую информацию о нем,
- собранную автоматическим путем. Для этого следует написать на адрес электронной
- почты{' '}
-
- welcome@discours.io
-
- .
-
-
- -
-
- Если в информации, предоставляемой Издательству Пользователем, содержатся
- персональные данные последнего, то фактом их предоставления он соглашается
- на их обработку любым способом, не запрещенным законодательством РФ.
-
-
- Общедоступные видео на сайте могут транслироваться с YouTube
- и регулируются{' '}
-
- политикой конфиденциальности Google
-
- . Загрузка видео на сайт также означает согласие с
-
- Условиями использования YouTube
-
- .
-
-
- -
-
- Данные, которые мы получаем от вас, мы используем только
- в соответствии с принципами обработки данных, указанными в этом
- документе.
-
-
-
-
Обратная связь
-
- Любые вопросы и предложения по поводу функционирования сайта можно направить
- по электронной почте{' '}
-
- welcome@discours.io
- {' '}
- или через форму «предложить идею».
-
-
+
+ Дискурс — это сообщество творческих людей, объединенных идеей делать интересный
+ журнал для всех желающих. Авторы Дискурса сообща посредством прямого голосования определяют
+ содержание журнала.
+
+
Для того, чтобы Дискурс работал без помех, разработаны настоящие Правила.
+
Определения
+
+ Сайт — портал discours.io
+
+
+ Пользователь — лицо, пользующееся Сайтом, либо юридическое лицо,
+ обладающее правами на интеллектуальную собственность.
+
+
+ Публикация контента — размещение Пользователем посредством Сайта
+ объектов авторских прав и другой информации для других пользователей.
+
+
+ Издательство — администрация сайта, которая занимается технической
+ и издательской деятельностью для обеспечения функционирования Сайта и Альманаха.
+ Издательство не вмешивается в принятие редакционных решений авторским сообществом.
+
+
+ Альманах «Дискурс» (свидетельство о регистрации СМИ: ПИ
+ № ФС77-63947 от 18.12.15) — печатное периодическое издание, которое
+ выходит раз в год и состоит из лучших публикаций на Сайте за это
+ время.
+
+
Авторские права
+
+ -
+
+ Вся информация на сайте (включая тексты, изображения, видеоматериалы, аудиозаписи,
+ программный код, дизайн сайта и т.д.) является объектом интеллектуальной
+ собственности ее правообладателей и охраняется законодательством РФ.
+
+
+ -
+
+ Публикуя контент на сайте, Пользователь на безвозмездной основе предоставляет
+ Издательству право на воспроизведение, распространение, перевод, редактирование
+ контента. Данное право предоставляется Издательству на весь срок действия авторских
+ прав Пользователя.
+
+
+ -
+
+ Пользователь предоставляет Издательству право редактировать контент, в том числе
+ вносить в него изменения, сокращения и дополнения, снабжать его иллюстрациями
+ и пояснениями, исправлять ошибки и уточнять фактические сведения, при условии,
+ что этим не искажается авторский замысел.
+
+
+ -
+
+ Обнародование контента осуществляется Издательством в соответствии с условиями
+ лицензии{' '}
+
+ Creative Commons BY-NC-ND 4.0
+
+ . Все материалы сайта предназначены исключительно для личного некоммерческого
+ использования. Права на дизайн и программный код сайта принадлежат Издательству.
+
+
+ -
+
+ Все аудиовизуальные произведения являются собственностью своих авторов
+ и правообладателей и используются только в образовательных
+ и информационных целях. Если вы являетесь собственником того или иного
+ произведения и не согласны с его размещением на сайте, пожалуйста,
+ напишите на
+
+ welcome@discours.io
+
+ .
+
+
+ -
+
+ Цитирование, распространение, доведение до всеобщего сведения материалов Cайта
+ приветствуется. При использовании материалов сайта необходимо указать имя автора
+ и активную ссылку на материал на Сайте.
+
+
+
+
Правила поведения
+
+ -
+
+ Находясь на Сайте, Пользователь подтверждает свое совершеннолетие, правоспособность,
+ а также согласие с настоящими Правилами и политикой конфиденциальности
+ и готовность нести полную ответственность за их соблюдение.
+
+
+ -
+
На сайте запрещено:
+
+ -
+ Публиковать контент, авторские права на который принадлежат третьим лицам, без
+ согласия этих лиц. Если авторские права на контент принадлежат нескольким лицам,
+ то его публикация предполагает согласие их всех.
+
+ - Размещать коммерческую и политическую рекламу.
+ -
+ Целенаправленно препятствовать нормальному функционированию сообщества и сайта
+ discours.io
+
+ - Выдавать себя за другого человека и представляться его именем.
+ -
+ Размещать информацию, которая не соответствует целям создания Сайта, ущемляет
+ интересы других пользователей или третьих лиц, нарушает законы Российской Федерации.
+
+
+
+ -
+
+ Пользователь несет всю ответственность за содержание публикуемого контента
+ и свое взаимодействие с другими пользователями, и обязуется возместить все
+ расходы в случае предъявления каких-либо претензий третьими лицами. Издательство
+ не несет ответственности за содержание публикуемой пользователями информации,
+ в том числе за размещенные на сайте комментарии. Переписка между
+ Пользователем и Издательством считается юридически значимой. Настоящие Правила могут
+ быть изменены Издательством, изменения вступают в силу с момента публикации
+ на Сайте.
+
+
+ -
+
+ Если Пользователь очевидно и целенаправленно нарушает правила, Издательство может
+ и принять в отношении автора следующие меры: вынести предупреждение
+ и обязать автора устранить допущенное нарушение, удалить контент, нарушающий правила,
+ заблокировать или удалить аккаунт нарушителя.
+
+
+
+
Политика конфиденциальности
+
+ -
+
Сайт может собирать у пользователей следующие данные:
+
+ -
+
+ Данные, которые пользователи сообщают о себе сами при подаче заявки, регистрации,
+ авторизации или заполнения профиля, в том числе ФИО и контактную информацию.
+ Конфиденциальные данные, такие как идентификатор и электронный адрес,
+ используются для идентификации пользователя. Данные профиля, размещённые публично
+ по желанию пользователя, которое выражается фактом их предоставления,
+ используется для демонстрации другим пользователям той информации о себе, которую
+ пользователь готов предоставить.
+
+
+ -
+
+ Данные, собранные автоматическим путем, такие, как cookie-файлы. Эти
+ неперсонализированные данные могут использоваться для сбора статистики
+ и улучшения работы сайта.
+
+
+
+
+ -
+
+ Издательство обеспечивает конфиденциальность персональных данных и применяет все
+ необходимые организационные и технические меры по их защите.
+
+
+ -
+
+ По желанию пользователя Издательство готово удалить любую информацию о нем,
+ собранную автоматическим путем. Для этого следует написать на адрес электронной почты{' '}
+
+ welcome@discours.io
+
+ .
+
+
+ -
+
+ Если в информации, предоставляемой Издательству Пользователем, содержатся
+ персональные данные последнего, то фактом их предоставления он соглашается
+ на их обработку любым способом, не запрещенным законодательством РФ.
+
+
+ Общедоступные видео на сайте могут транслироваться с YouTube и регулируются{' '}
+
+ политикой конфиденциальности Google
+
+ . Загрузка видео на сайт также означает согласие с
+
+ Условиями использования YouTube
+
+ .
+
+
+ -
+
+ Данные, которые мы получаем от вас, мы используем только
+ в соответствии с принципами обработки данных, указанными в этом документе.
+
+
+
+
Обратная связь
+
+ Любые вопросы и предложения по поводу функционирования сайта можно направить
+ по электронной почте{' '}
+
+ welcome@discours.io
+ {' '}
+ или через форму «предложить идею».
+
diff --git a/src/components/Pages/about/ThanksPage.tsx b/src/components/Pages/about/ThanksPage.tsx
index 20ad2f9c..c4efa93d 100644
--- a/src/components/Pages/about/ThanksPage.tsx
+++ b/src/components/Pages/about/ThanksPage.tsx
@@ -12,12 +12,10 @@ export const ThanksPage = () => {
-
+
{title}
-
-
{/*
Команда
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
index fc1a7ff9..46efc845 100644
--- a/src/components/Root.tsx
+++ b/src/components/Root.tsx
@@ -2,12 +2,11 @@
// import 'solid-devtools'
import { MODALS, setLocale, showModal } from '../stores/ui'
-import { Component, createEffect, createMemo, onMount } from 'solid-js'
+import { Component, createEffect, createMemo } from 'solid-js'
import { Routes, useRouter } from '../stores/router'
import { Dynamic, isServer } from 'solid-js/web'
-import { getLogger } from '../utils/logger'
-import type { PageProps } from './types'
+import type { PageProps, RootSearchParams } from './types'
import { HomePage } from './Pages/HomePage'
import { AllTopicsPage } from './Pages/AllTopicsPage'
@@ -30,7 +29,7 @@ import { TermsOfUsePage } from './Pages/about/TermsOfUsePage'
import { ThanksPage } from './Pages/about/ThanksPage'
import { CreatePage } from './Pages/CreatePage'
import { ConnectPage } from './Pages/ConnectPage'
-import { renewSession } from '../stores/auth'
+import { SessionProvider } from '../context/session'
// TODO: lazy load
// const HomePage = lazy(() => import('./Pages/HomePage'))
@@ -52,13 +51,6 @@ import { renewSession } from '../stores/auth'
// const ThanksPage = lazy(() => import('./Pages/about/ThanksPage'))
// const CreatePage = lazy(() => import('./Pages/about/CreatePage'))
-const log = getLogger('root')
-
-type RootSearchParams = {
- modal: string
- lang: string
-}
-
const pagesMap: Record> = {
connect: ConnectPage,
create: CreatePage,
@@ -92,10 +84,6 @@ export const Root = (props: PageProps) => {
}
})
- onMount(() => {
- renewSession()
- })
-
const pageComponent = createMemo(() => {
const result = pagesMap[page().route]
@@ -114,5 +102,9 @@ export const Root = (props: PageProps) => {
})
}
- return
+ return (
+
+
+
+ )
}
diff --git a/src/components/Topic/Card.module.scss b/src/components/Topic/Card.module.scss
index 801f7eaa..4d68d697 100644
--- a/src/components/Topic/Card.module.scss
+++ b/src/components/Topic/Card.module.scss
@@ -3,16 +3,8 @@
margin-top: 3.2rem;
.stats & {
- @include media-breakpoint-down(sm) {
- margin-left: 0;
- }
-
.topicDetailsItem {
margin-bottom: 1.2rem;
-
- @include media-breakpoint-up(md) {
- margin-bottom: 3.2rem;
- }
}
}
}
@@ -33,9 +25,9 @@
.topicTitle {
font-weight: bold;
- @include font-size(1.7rem);
+ @include font-size(2.2rem);
- margin-bottom: 0.8rem;
+ margin-bottom: 1.2rem;
}
.topicAvatar {
@@ -56,10 +48,10 @@
}
.topicDescription {
- @include font-size(1.5rem);
+ @include font-size(1.6rem);
color: #696969;
- margin: 0 0 0.8rem;
+ margin: 0 0 1.6rem;
&.compact {
font-size: medium;
@@ -85,6 +77,8 @@
}
.topicDetailsItem {
+ @include font-size(1.5rem);
+
margin-right: 1.6rem;
white-space: nowrap;
@@ -104,3 +98,9 @@
float: right;
}
}
+
+.controlContainer {
+ @include media-breakpoint-up(md) {
+ text-align: right;
+ }
+}
diff --git a/src/components/Topic/Card.tsx b/src/components/Topic/Card.tsx
index da9d6d3f..d8fbff48 100644
--- a/src/components/Topic/Card.tsx
+++ b/src/components/Topic/Card.tsx
@@ -5,9 +5,10 @@ import type { Topic } from '../../graphql/types.gen'
import { FollowingEntity } from '../../graphql/types.gen'
import { t } from '../../utils/intl'
import { locale } from '../../stores/ui'
-import { useAuthStore } from '../../stores/auth'
import { follow, unfollow } from '../../stores/zine/common'
import { getLogger } from '../../utils/logger'
+import { clsx } from 'clsx'
+import { useSession } from '../../context/session'
const log = getLogger('TopicCard')
@@ -23,7 +24,7 @@ interface TopicProps {
}
export const TopicCard = (props: TopicProps) => {
- const { session } = useAuthStore()
+ const { session } = useSession()
const subscribed = createMemo(() => {
if (!session()?.user?.slug || !session()?.news?.topics) {
@@ -49,11 +50,11 @@ export const TopicCard = (props: TopicProps) => {
[styles.topicInRow]: props.isTopicInRow
}}
>
-
+
-
+
@@ -65,7 +66,7 @@ export const TopicCard = (props: TopicProps) => {
{props.topic.body}
@@ -75,7 +76,7 @@ export const TopicCard = (props: TopicProps) => {
-
+
{props.topic.stat?.shouts +
' ' +
t('post') +
@@ -84,7 +85,7 @@ export const TopicCard = (props: TopicProps) => {
locale() === 'ru' ? ['ов', '', 'а'] : ['s', '', 's']
)}
-
+
{props.topic.stat?.authors +
' ' +
t('author') +
@@ -127,21 +128,24 @@ export const TopicCard = (props: TopicProps) => {
-
+
subscribe(true)} class="button--light button--subscribe-topic">
+
- + {t('Follow')}
+ {t('Follow')}
}
>
diff --git a/src/components/Topic/Full.scss b/src/components/Topic/Full.module.scss
similarity index 66%
rename from src/components/Topic/Full.scss
rename to src/components/Topic/Full.module.scss
index ef8c4baf..f0c39156 100644
--- a/src/components/Topic/Full.scss
+++ b/src/components/Topic/Full.module.scss
@@ -1,11 +1,19 @@
-.topic__header {
+.topicHeader {
@include font-size(1.7rem);
padding-top: 5.8rem;
text-align: center;
+
+ h1 {
+ color: #2638d9;
+ font-weight: 500;
+ text-transform: uppercase;
+
+ @include font-size(2rem);
+ }
}
-.topic__actions {
+.topicActions {
margin-top: 2.8rem;
button,
diff --git a/src/components/Topic/Full.tsx b/src/components/Topic/Full.tsx
index 5923ead9..86c48852 100644
--- a/src/components/Topic/Full.tsx
+++ b/src/components/Topic/Full.tsx
@@ -1,51 +1,50 @@
import { createMemo, Show } from 'solid-js'
import type { Topic } from '../../graphql/types.gen'
import { FollowingEntity } from '../../graphql/types.gen'
-import './Full.scss'
-import { useAuthStore } from '../../stores/auth'
+import styles from './Full.module.scss'
import { follow, unfollow } from '../../stores/zine/common'
import { t } from '../../utils/intl'
+import { clsx } from 'clsx'
+import { useSession } from '../../context/session'
type Props = {
topic: Topic
}
export const FullTopic = (props: Props) => {
- const { session } = useAuthStore()
+ const { session } = useSession()
const subscribed = createMemo(() => session()?.news?.topics?.includes(props.topic?.slug))
return (
-
-
-
-
)
}
diff --git a/src/components/Views/AllAuthors.tsx b/src/components/Views/AllAuthors.tsx
index 7c4ad362..93a46f36 100644
--- a/src/components/Views/AllAuthors.tsx
+++ b/src/components/Views/AllAuthors.tsx
@@ -1,12 +1,13 @@
-import { createEffect, createMemo, For, Show } from 'solid-js'
+import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
import type { Author } from '../../graphql/types.gen'
import { AuthorCard } from '../Author/Card'
import { Icon } from '../Nav/Icon'
import { t } from '../../utils/intl'
import { useAuthorsStore, setAuthorsSort } from '../../stores/zine/authors'
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
-import { useAuthStore } from '../../stores/auth'
-import '../../styles/AllTopics.scss'
+import styles from '../../styles/AllTopics.module.scss'
+import { clsx } from 'clsx'
+import { useSession } from '../../context/session'
type AllAuthorsPageSearchParams = {
by: '' | 'name' | 'shouts' | 'rating'
@@ -16,10 +17,13 @@ type Props = {
authors: Author[]
}
+const PAGE_SIZE = 20
+
export const AllAuthorsView = (props: Props) => {
const { sortedAuthors } = useAuthorsStore({ authors: props.authors })
+ const [limit, setLimit] = createSignal(PAGE_SIZE)
- const { session } = useAuthStore()
+ const { session } = useSession()
createEffect(() => {
setAuthorsSort(searchParams().by || 'shouts')
@@ -53,86 +57,100 @@ export const AllAuthorsView = (props: Props) => {
return keys
})
- // log.debug(getSearchParams())
+ const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
return (
-
+
0}>
-
-
-
-
-
-
-
-
-
(
-
-
- {(author) => (
-
- )}
-
-
- )}
- >
-
- {(letter) => (
-
-
{letter}
-
-
-
- {(author: Author) => (
-
- )}
-
-
-
-
- )}
-
-
-
+
+
+
+
+
(
+
+
+
+
+ {(author) => (
+
+ )}
+
+
+
+
limit()}>
+
+
+
+ )}
+ >
+
+ {(letter) => (
+
+
{letter}
+
+
+
+
+
+ {(author: Author) => (
+
+ )}
+
+
+
+
+
+
+ )}
+
+
diff --git a/src/components/Views/AllTopics.tsx b/src/components/Views/AllTopics.tsx
index 91f28e90..fd676ba6 100644
--- a/src/components/Views/AllTopics.tsx
+++ b/src/components/Views/AllTopics.tsx
@@ -1,12 +1,13 @@
-import { createEffect, createMemo, For, Show } from 'solid-js'
+import { createEffect, createMemo, createSignal, For, Show } from 'solid-js'
import type { Topic } from '../../graphql/types.gen'
import { Icon } from '../Nav/Icon'
import { t } from '../../utils/intl'
import { setTopicsSort, useTopicsStore } from '../../stores/zine/topics'
import { handleClientRouteLinkClick, useRouter } from '../../stores/router'
import { TopicCard } from '../Topic/Card'
-import { useAuthStore } from '../../stores/auth'
-import '../../styles/AllTopics.scss'
+import styles from '../../styles/AllTopics.module.scss'
+import { clsx } from 'clsx'
+import { useSession } from '../../context/session'
type AllTopicsPageSearchParams = {
by: 'shouts' | 'authors' | 'title' | ''
@@ -16,18 +17,22 @@ type AllTopicsViewProps = {
topics: Topic[]
}
+const PAGE_SIZE = 20
+
export const AllTopicsView = (props: AllTopicsViewProps) => {
const { searchParams, changeSearchParam } = useRouter
()
+ const [limit, setLimit] = createSignal(PAGE_SIZE)
const { sortedTopics } = useTopicsStore({
topics: props.topics,
sortBy: searchParams().by || 'shouts'
})
- const { session } = useAuthStore()
+ const { session } = useSession()
createEffect(() => {
setTopicsSort(searchParams().by || 'shouts')
+ setLimit(PAGE_SIZE)
})
const byLetter = createMemo<{ [letter: string]: Topic[] }>(() => {
@@ -51,87 +56,96 @@ export const AllTopicsView = (props: AllTopicsViewProps) => {
const subscribed = (s) => Boolean(session()?.news?.topics && session()?.news?.topics?.includes(s || ''))
+ const showMore = () => setLimit((oldLimit) => oldLimit + PAGE_SIZE)
+
return (
-
+
0}>
-
-
-
-
-
+
+
+
+
+
(
+ <>
+
+ {(topic) => (
+
+ )}
+
+ limit()}>
+
+
+ >
+ )}
+ >
+
+ {(letter) => (
+
+
{letter}
+
+
+
+
+
+ {(topic) => (
+
+ )}
+
+
+
+
+
+
+ )}
+
+
diff --git a/src/components/Views/Author.tsx b/src/components/Views/Author.tsx
index 52cd189f..59278d34 100644
--- a/src/components/Views/Author.tsx
+++ b/src/components/Views/Author.tsx
@@ -7,7 +7,6 @@ import { t } from '../../utils/intl'
import { useAuthorsStore } from '../../stores/zine/authors'
import { loadAuthorArticles, useArticlesStore } from '../../stores/zine/articles'
-import '../../styles/Topic.scss'
import { useTopicsStore } from '../../stores/zine/topics'
import { useRouter } from '../../stores/router'
import { Beside } from '../Feed/Beside'
diff --git a/src/components/Views/Create.tsx b/src/components/Views/Create.tsx
index 757ea2b2..636b1386 100644
--- a/src/components/Views/Create.tsx
+++ b/src/components/Views/Create.tsx
@@ -1,73 +1,11 @@
-import { Show, onCleanup, createEffect, onError, onMount, untrack, createSignal } from 'solid-js'
-import { createMutable, unwrap } from 'solid-js/store'
-import { State, StateContext, newState } from '../Editor/store/context'
-import { createCtrl } from '../Editor/store/actions'
-import { Layout } from '../Editor/components/Layout'
-import { Editor } from '../Editor/components/Editor'
-import { Sidebar } from '../Editor/components/Sidebar'
-import ErrorView from '../Editor/components/Error'
-
-const matchDark = () => window.matchMedia('(prefers-color-scheme: dark)')
+import { Editor } from '../EditorNew/Editor'
+import { ClientContainer } from '../_shared/ClientContainer'
export const CreateView = () => {
- const [isMounted, setIsMounted] = createSignal(false)
-
- onMount(() => setIsMounted(true))
-
- const onChangeTheme = () => ctrl.updateTheme()
- onMount(() => {
- matchDark().addEventListener('change', onChangeTheme)
- onCleanup(() => matchDark().removeEventListener('change', onChangeTheme))
- })
-
- const [store, ctrl] = createCtrl(newState())
- const mouseEnterCoords = createMutable({ x: 0, y: 0 })
-
- const onMouseEnter = (e: MouseEvent) => {
- mouseEnterCoords.x = e.pageX
- mouseEnterCoords.y = e.pageY
- }
-
- onMount(async () => {
- console.debug('[create] view mounted')
- if (store.error) {
- console.error(store.error)
- return
- }
- await ctrl.init()
- })
-
- onError((error) => {
- console.error('[create] error:', error)
- ctrl.setState({ error: { id: 'exception', props: { error } } })
- })
-
- createEffect((prev) => {
- const lastModified = store.lastModified
- if (!lastModified || (store.loading === 'initialized' && prev === 'loading')) {
- return store.loading
- }
- const state: State = untrack(() => unwrap(store))
- ctrl.saveState(state)
- console.debug('[create] status update')
- return store.loading
- }, store.loading)
-
return (
-
-
-
- }>
-
-
-
-
-
-
+
+
+
)
}
diff --git a/src/components/Views/Feed.tsx b/src/components/Views/Feed.tsx
index c791331c..aca7987b 100644
--- a/src/components/Views/Feed.tsx
+++ b/src/components/Views/Feed.tsx
@@ -8,13 +8,13 @@ import { ArticleCard } from '../Feed/Card'
import { AuthorCard } from '../Author/Card'
import { t } from '../../utils/intl'
import { FeedSidebar } from '../Feed/Sidebar'
-import { useAuthStore } from '../../stores/auth'
import CommentCard from '../Article/Comment'
import { loadRecentArticles, useArticlesStore } from '../../stores/zine/articles'
import { useReactionsStore } from '../../stores/zine/reactions'
import { useAuthorsStore } from '../../stores/zine/authors'
import { useTopicsStore } from '../../stores/zine/topics'
import { useTopAuthorsStore } from '../../stores/zine/topAuthors'
+import { useSession } from '../../context/session'
// const AUTHORSHIP_REACTIONS = [
// ReactionKind.Accept,
@@ -32,7 +32,7 @@ export const FeedView = () => {
const { sortedAuthors } = useAuthorsStore()
const { topTopics } = useTopicsStore()
const { topAuthors } = useTopAuthorsStore()
- const { session } = useAuthStore()
+ const { session } = useSession()
const topReactions = createMemo(() => sortBy(reactions(), byCreated))
diff --git a/src/components/Views/Topic.tsx b/src/components/Views/Topic.tsx
index 8e96ea6f..fca408f4 100644
--- a/src/components/Views/Topic.tsx
+++ b/src/components/Views/Topic.tsx
@@ -3,16 +3,18 @@ import type { Shout, Topic } from '../../graphql/types.gen'
import { Row3 } from '../Feed/Row3'
import { Row2 } from '../Feed/Row2'
import { Beside } from '../Feed/Beside'
-import { ArticleCard } from '../Feed/Card'
-import '../../styles/Topic.scss'
+import styles from '../../styles/Topic.module.scss'
import { FullTopic } from '../Topic/Full'
import { t } from '../../utils/intl'
import { useRouter } from '../../stores/router'
import { useTopicsStore } from '../../stores/zine/topics'
-import { loadPublishedArticles, useArticlesStore } from '../../stores/zine/articles'
+import { loadTopicArticles, useArticlesStore } from '../../stores/zine/articles'
import { useAuthorsStore } from '../../stores/zine/authors'
import { restoreScrollPosition, saveScrollPosition } from '../../utils/scroll'
import { splitToPages } from '../../utils/splitToPages'
+import { clsx } from 'clsx'
+import Slider from '../Feed/Slider'
+import { Row1 } from '../Feed/Row1'
type TopicsPageSearchParams = {
by: 'comments' | '' | 'recent' | 'viewed' | 'rating' | 'commented'
@@ -24,7 +26,7 @@ interface TopicProps {
topicSlug: string
}
-export const PRERENDERED_ARTICLES_COUNT = 21
+export const PRERENDERED_ARTICLES_COUNT = 28
const LOAD_MORE_PAGE_SIZE = 9 // Row3 + Row3 + Row3
export const TopicView = (props: TopicProps) => {
@@ -42,7 +44,8 @@ export const TopicView = (props: TopicProps) => {
const loadMore = async () => {
saveScrollPosition()
- const { hasMore } = await loadPublishedArticles({
+ const { hasMore } = await loadTopicArticles({
+ topicSlug: topic().slug,
limit: LOAD_MORE_PAGE_SIZE,
offset: sortedArticles().length
})
@@ -70,94 +73,94 @@ export const TopicView = (props: TopicProps) => {
)
return (
-
+
-
-
-
- -
-
-
- {/*TODO: server sort*/}
- {/*- */}
- {/* */}
- {/*
*/}
- {/*- */}
- {/* */}
- {/*
*/}
- {/*- */}
- {/* */}
- {/*
*/}
-
-
-
-
- {`${t('Show')} `}
-
{t('All posts')}
+
+
+
+
+ -
+
+
+ {/*TODO: server sort*/}
+ {/*- */}
+ {/* */}
+ {/*
*/}
+ {/*- */}
+ {/* */}
+ {/*
*/}
+ {/*- */}
+ {/* */}
+ {/*
*/}
+
+
+
+
+ {`${t('Show')} `}
+ {t('All posts')}
+
-
-
-
-
{title()}
-
- {(article) => (
-
- )}
-
-
-
-
+
+
-
-
5}>
-
-
-
-
-
-
-
+
-
- {(page) => (
- <>
-
-
-
- >
- )}
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+ {(page) => (
+ <>
+
+
+
+ >
+ )}
+
+
+
+
+
+
+
)
diff --git a/src/components/_shared/ClientContainer.tsx b/src/components/_shared/ClientContainer.tsx
new file mode 100644
index 00000000..913efae6
--- /dev/null
+++ b/src/components/_shared/ClientContainer.tsx
@@ -0,0 +1,12 @@
+import type { JSX } from 'solid-js'
+import { createSignal, onMount, Show } from 'solid-js'
+
+// show children only on client side
+// usage of isServer causing hydration errors
+export const ClientContainer = (props: { children: JSX.Element }) => {
+ const [isMounted, setIsMounted] = createSignal(false)
+
+ onMount(() => setIsMounted(true))
+
+ return
{props.children}
+}
diff --git a/src/components/Nav/Popup.module.scss b/src/components/_shared/Popup.module.scss
similarity index 100%
rename from src/components/Nav/Popup.module.scss
rename to src/components/_shared/Popup.module.scss
diff --git a/src/components/Nav/Popup.tsx b/src/components/_shared/Popup.tsx
similarity index 94%
rename from src/components/Nav/Popup.tsx
rename to src/components/_shared/Popup.tsx
index 5ee469b9..bfb4ef89 100644
--- a/src/components/Nav/Popup.tsx
+++ b/src/components/_shared/Popup.tsx
@@ -1,4 +1,4 @@
-import { createEffect, createSignal, JSX, onCleanup, onMount, Show } from 'solid-js'
+import { createEffect, createSignal, JSX, Show } from 'solid-js'
import styles from './Popup.module.scss'
import { clsx } from 'clsx'
import { useOutsideClickHandler } from '../../utils/useOutsideClickHandler'
diff --git a/src/components/types.ts b/src/components/types.ts
index f3158bb7..866bb4f5 100644
--- a/src/components/types.ts
+++ b/src/components/types.ts
@@ -17,3 +17,8 @@ export type PageProps = {
searchResults?: Shout[]
chats?: Chat[]
}
+
+export type RootSearchParams = {
+ modal: string
+ lang: string
+}
diff --git a/src/context/session.tsx b/src/context/session.tsx
new file mode 100644
index 00000000..47a51e76
--- /dev/null
+++ b/src/context/session.tsx
@@ -0,0 +1,81 @@
+import type { Accessor, InitializedResource, JSX } from 'solid-js'
+import { createContext, createMemo, createResource, onMount, useContext } from 'solid-js'
+import type { AuthResult } from '../graphql/types.gen'
+import { apiClient } from '../utils/apiClient'
+import { resetToken, setToken } from '../graphql/privateGraphQLClient'
+
+type SessionContextType = {
+ session: InitializedResource
+ isAuthenticated: Accessor
+ actions: {
+ refreshSession: () => AuthResult | Promise
+ signIn: ({ email, password }: { email: string; password: string }) => Promise
+ signOut: () => Promise
+ confirmEmail: (token: string) => Promise
+ }
+}
+
+const SessionContext = createContext()
+
+const refreshSession = async (): Promise => {
+ try {
+ const authResult = await apiClient.getSession()
+ if (!authResult) {
+ return null
+ }
+ setToken(authResult.token)
+ return authResult
+ } catch (error) {
+ console.error('renewSession error:', error)
+ resetToken()
+ return null
+ }
+}
+
+export function useSession() {
+ return useContext(SessionContext)
+}
+
+export const SessionProvider = (props: { children: JSX.Element }) => {
+ const [session, { refetch: refetchRefreshSession, mutate }] = createResource(refreshSession, {
+ ssrLoadFrom: 'initial',
+ initialValue: null
+ })
+
+ const isAuthenticated = createMemo(() => Boolean(session()?.user?.slug))
+
+ const signIn = async ({ email, password }: { email: string; password: string }) => {
+ const authResult = await apiClient.authLogin({ email, password })
+ mutate(authResult)
+ setToken(authResult.token)
+ console.debug('signed in')
+ }
+
+ const signOut = async () => {
+ // TODO: call backend to revoke token
+ mutate(null)
+ resetToken()
+ console.debug('signed out')
+ }
+
+ const confirmEmail = async (token: string) => {
+ const authResult = await apiClient.confirmEmail({ token })
+ mutate(authResult)
+ setToken(authResult.token)
+ }
+
+ const actions = {
+ refreshSession: refetchRefreshSession,
+ signIn,
+ signOut,
+ confirmEmail
+ }
+
+ const value: SessionContextType = { session, isAuthenticated, actions }
+
+ onMount(() => {
+ refetchRefreshSession()
+ })
+
+ return {props.children}
+}
diff --git a/src/graphql/mutation/article-create.ts b/src/graphql/mutation/article-create.ts
index 62523e87..0f2ef0ba 100644
--- a/src/graphql/mutation/article-create.ts
+++ b/src/graphql/mutation/article-create.ts
@@ -9,13 +9,11 @@ export default gql`
slug
title
subtitle
- image
body
topics {
_id: slug
title
slug
- image
}
authors {
_id: slug
diff --git a/src/graphql/privateGraphQLClient.ts b/src/graphql/privateGraphQLClient.ts
index dc9ff986..442343cf 100644
--- a/src/graphql/privateGraphQLClient.ts
+++ b/src/graphql/privateGraphQLClient.ts
@@ -10,6 +10,10 @@ if (isDev) {
exchanges.unshift(devtoolsExchange)
}
+export const getToken = (): string => {
+ return localStorage.getItem(TOKEN_LOCAL_STORAGE_KEY)
+}
+
export const setToken = (token: string) => {
localStorage.setItem(TOKEN_LOCAL_STORAGE_KEY, token)
}
@@ -27,7 +31,6 @@ const options: ClientOptions = {
// меняем через setToken, например при получении значения с сервера
// скорее всего придумаем что-нибудь получше со временем
const token = localStorage.getItem(TOKEN_LOCAL_STORAGE_KEY)
-
const headers = { Auth: token }
return { headers }
},
diff --git a/src/graphql/query/author-by-slug.ts b/src/graphql/query/author-by-slug.ts
new file mode 100644
index 00000000..bf35df61
--- /dev/null
+++ b/src/graphql/query/author-by-slug.ts
@@ -0,0 +1,22 @@
+import { gql } from '@urql/core'
+
+export default gql`
+ query GetAuthorBySlugQuery($slug: String!) {
+ getAuthor(slug: $slug) {
+ _id: slug
+ slug
+ name
+ bio
+ userpic
+ communities
+ links
+ createdAt
+ lastSeen
+ ratings {
+ _id: rater
+ rater
+ value
+ }
+ }
+ }
+`
diff --git a/src/graphql/query/authors-all.ts b/src/graphql/query/authors-all.ts
index 4570a611..4b02a3a5 100644
--- a/src/graphql/query/authors-all.ts
+++ b/src/graphql/query/authors-all.ts
@@ -8,14 +8,11 @@ export default gql`
name
bio
userpic
- communities
links
- createdAt
lastSeen
- ratings {
- _id: rater
- rater
- value
+ stat {
+ followers
+ followings
}
}
}
diff --git a/src/graphql/query/topic-by-slug.ts b/src/graphql/query/topic-by-slug.ts
new file mode 100644
index 00000000..0e440496
--- /dev/null
+++ b/src/graphql/query/topic-by-slug.ts
@@ -0,0 +1,22 @@
+import { gql } from '@urql/core'
+
+export default gql`
+ query TopicBySlugQuery($slug: String!) {
+ getTopic(slug: $slug) {
+ title
+ body
+ slug
+ pic
+ parents
+ children
+ # community
+ stat {
+ _id: shouts
+ shouts
+ authors
+ # viewed
+ followers
+ }
+ }
+ }
+`
diff --git a/src/graphql/types.gen.ts b/src/graphql/types.gen.ts
index 81c1ac59..8f2e7454 100644
--- a/src/graphql/types.gen.ts
+++ b/src/graphql/types.gen.ts
@@ -35,6 +35,7 @@ export type Author = {
}
export type AuthorStat = {
+ commented?: Maybe
followers?: Maybe
followings?: Maybe
rating?: Maybe
@@ -366,6 +367,7 @@ export type Query = {
recentAll: Array>
recentCandidates: Array>
recentCommented: Array>
+ recentLayoutShouts: Array>
recentPublished: Array>
recentReacted: Array>
searchChats: Result
@@ -382,7 +384,9 @@ export type Query = {
signOut: AuthResult
topAuthors: Array>
topCommented: Array>
+ topLayoutShouts: Array>
topMonth: Array>
+ topMonthLayoutShouts: Array>
topOverall: Array>
topPublished: Array>
topicsAll: Array>
@@ -470,6 +474,12 @@ export type QueryRecentCommentedArgs = {
offset: Scalars['Int']
}
+export type QueryRecentLayoutShoutsArgs = {
+ amount?: InputMaybe
+ layout: Scalars['String']
+ offset?: InputMaybe
+}
+
export type QueryRecentPublishedArgs = {
limit: Scalars['Int']
offset: Scalars['Int']
@@ -555,11 +565,23 @@ export type QueryTopCommentedArgs = {
offset: Scalars['Int']
}
+export type QueryTopLayoutShoutsArgs = {
+ amount?: InputMaybe
+ layout: Scalars['String']
+ offset?: InputMaybe
+}
+
export type QueryTopMonthArgs = {
limit: Scalars['Int']
offset: Scalars['Int']
}
+export type QueryTopMonthLayoutShoutsArgs = {
+ amount?: InputMaybe
+ layout: Scalars['String']
+ offset?: InputMaybe
+}
+
export type QueryTopOverallArgs = {
limit: Scalars['Int']
offset: Scalars['Int']
@@ -708,6 +730,7 @@ export type Shout = {
lang?: Maybe
layout?: Maybe
mainTopic?: Maybe
+ media?: Maybe
publishedAt?: Maybe
publishedBy?: Maybe
slug: Scalars['String']
diff --git a/src/locales/ru.json b/src/locales/ru.json
index bf90bb84..a7644c70 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -168,5 +168,8 @@
"We've sent you a message with a link to enter our website.": "Мы выслали вам письмо с ссылкой на почту. Перейдите по ссылке в письме, чтобы войти на сайт.",
"Send link again": "Прислать ссылку ещё раз",
"Link sent, check your email": "Ссылка отправлена, проверьте почту",
- "Create post": "Создать публикацию"
+ "Create post": "Создать публикацию",
+ "Just start typing...": "Просто начните печатать...",
+ "We can't find you, check email or": "Не можем вас найти, проверьте адрес электронной почты или",
+ "register": "зарегистрируйтесь"
}
diff --git a/src/pages/author/[slug]/index.astro b/src/pages/author/[slug]/index.astro
index 50e29aa4..f7a2cc9b 100644
--- a/src/pages/author/[slug]/index.astro
+++ b/src/pages/author/[slug]/index.astro
@@ -7,7 +7,7 @@ import { PRERENDERED_ARTICLES_COUNT } from '../../../components/Views/Author'
const slug = Astro.params.slug.toString()
const articles = await apiClient.getArticlesForAuthors({ authorSlugs: [slug], limit: PRERENDERED_ARTICLES_COUNT })
-const author = articles[0].authors.find((a) => a.slug === slug)
+const author = await apiClient.getAuthor({ slug })
const { pathname, search } = Astro.url
initRouter(pathname, search)
diff --git a/src/pages/topic/[slug].astro b/src/pages/topic/[slug].astro
index 91ebde65..c8f61246 100644
--- a/src/pages/topic/[slug].astro
+++ b/src/pages/topic/[slug].astro
@@ -6,7 +6,7 @@ import { PRERENDERED_ARTICLES_COUNT } from '../../components/Views/Topic'
const slug = Astro.params.slug?.toString() || ''
const articles = await apiClient.getArticlesForTopics({ topicSlugs: [slug], limit: PRERENDERED_ARTICLES_COUNT })
-const topic = articles[0].topics.find(({ slug: topicSlug }) => topicSlug === slug)
+const topic = await apiClient.getTopic({ slug })
import { initRouter } from '../../stores/router'
diff --git a/src/stores/auth.ts b/src/stores/auth.ts
index 5b07ad25..b92814fa 100644
--- a/src/stores/auth.ts
+++ b/src/stores/auth.ts
@@ -1,45 +1,4 @@
-import type { AuthResult } from '../graphql/types.gen'
-import { resetToken, setToken } from '../graphql/privateGraphQLClient'
import { apiClient } from '../utils/apiClient'
-import { createSignal } from 'solid-js'
-
-const [session, setSession] = createSignal(null)
-
-export const signIn = async (params) => {
- const authResult = await apiClient.authLogin(params)
- setSession(authResult)
- setToken(authResult.token)
- console.debug('signed in')
-}
-export const signOut = async () => {
- const result = await apiClient.authSignOut()
- if (result.error) {
- console.error('[auth] sign out error', result.error)
- } else {
- setSession(null)
- resetToken()
- console.debug('signed out')
- }
-}
-
-export const [emailChecks, setEmailChecks] = createSignal<{ [email: string]: boolean }>({})
-
-export const checkEmail = async (email: string): Promise => {
- if (emailChecks()[email]) {
- return true
- }
-
- const checkResult = await apiClient.authCheckEmail({ email })
-
- if (checkResult) {
- setEmailChecks((oldEmailChecks) => ({ ...oldEmailChecks, [email]: true }))
- return true
- }
-
- return false
-}
-
-export const [resetCode, setResetCode] = createSignal('')
export const register = async ({
name,
@@ -60,19 +19,3 @@ export const register = async ({
export const signSendLink = async ({ email, lang }: { email: string; lang: string }) => {
return await apiClient.authSendLink({ email, lang })
}
-
-export const renewSession = async () => {
- const authResult = await apiClient.getSession() // token in header
- setToken(authResult.token)
- setSession(authResult)
-}
-
-export const confirmEmail = async (token: string) => {
- const authResult = await apiClient.confirmEmail({ token })
- setToken(authResult.token)
- setSession(authResult)
-}
-
-export const useAuthStore = () => {
- return { session, emailChecks }
-}
diff --git a/src/stores/editor.ts b/src/stores/editor.ts
index 3868646d..8acb0c42 100644
--- a/src/stores/editor.ts
+++ b/src/stores/editor.ts
@@ -2,7 +2,8 @@ import { persistentMap } from '@nanostores/persistent'
import type { Reaction } from '../graphql/types.gen'
import { atom } from 'nanostores'
import { createSignal } from 'solid-js'
-import type { Draft } from '../components/Editor/store/context'
+
+// import type { Draft } from '../components/EditorExample/store/context'
interface Collab {
authors: string[] // slugs
@@ -12,7 +13,7 @@ interface Collab {
title?: string
}
-export const drafts = persistentMap<{ [key: string]: Draft }>(
+export const drafts = persistentMap<{ [key: string]: string }>(
'drafts',
{},
{
diff --git a/src/stores/emailChecks.ts b/src/stores/emailChecks.ts
new file mode 100644
index 00000000..62c5e71f
--- /dev/null
+++ b/src/stores/emailChecks.ts
@@ -0,0 +1,23 @@
+import { apiClient } from '../utils/apiClient'
+import { createSignal } from 'solid-js'
+
+const [emailChecks, setEmailChecks] = createSignal<{ [email: string]: boolean }>({})
+
+export const checkEmail = async (email: string): Promise => {
+ if (emailChecks()[email]) {
+ return true
+ }
+
+ const checkResult = await apiClient.authCheckEmail({ email })
+
+ if (checkResult) {
+ setEmailChecks((oldEmailChecks) => ({ ...oldEmailChecks, [email]: true }))
+ return true
+ }
+
+ return false
+}
+
+export const useEmailChecks = () => {
+ return { emailChecks }
+}
diff --git a/src/stores/ui.ts b/src/stores/ui.ts
index 8b71e4d6..d71801a8 100644
--- a/src/stores/ui.ts
+++ b/src/stores/ui.ts
@@ -1,6 +1,8 @@
//import { persistentAtom } from '@nanostores/persistent'
import { createSignal } from 'solid-js'
import { useRouter } from './router'
+import type { AuthModalSearchParams, ConfirmEmailSearchParams } from '../components/Nav/AuthModal/types'
+import type { RootSearchParams } from '../components/types'
//export const locale = persistentAtom('locale', 'ru')
export const [locale, setLocale] = createSignal('ru')
@@ -26,10 +28,22 @@ const [modal, setModal] = createSignal(null)
const [warnings, setWarnings] = createSignal([])
export const showModal = (modalType: ModalType) => setModal(modalType)
+
+// TODO: find a better solution
export const hideModal = () => {
- const { changeSearchParam } = useRouter()
+ const { searchParams, changeSearchParam } = useRouter<
+ AuthModalSearchParams & ConfirmEmailSearchParams & RootSearchParams
+ >()
+
+ if (searchParams().modal === 'auth') {
+ if (searchParams().mode === 'confirm-email') {
+ changeSearchParam('token', null, true)
+ }
+ changeSearchParam('mode', null, true)
+ }
+
changeSearchParam('modal', null, true)
- changeSearchParam('mode', null, true)
+
setModal(null)
}
diff --git a/src/stores/zine/articles.ts b/src/stores/zine/articles.ts
index b53790ba..a7ac1eee 100644
--- a/src/stores/zine/articles.ts
+++ b/src/stores/zine/articles.ts
@@ -1,4 +1,4 @@
-import type { Author, Shout, Topic } from '../../graphql/types.gen'
+import type { Author, Shout, ShoutInput, Topic } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient'
import { addAuthorsByTopic } from './authors'
import { addTopicsByAuthor } from './topics'
@@ -272,6 +272,14 @@ export const loadArticle = async ({ slug }: { slug: string }): Promise =>
addArticles([article])
}
+export const createArticle = async ({ article }: { article: ShoutInput }) => {
+ try {
+ await apiClient.createArticle({ article })
+ } catch (error) {
+ console.error(error)
+ }
+}
+
type InitialState = {
sortedArticles?: Shout[]
topRatedArticles?: Shout[]
diff --git a/src/stores/zine/authors.ts b/src/stores/zine/authors.ts
index 2a5cd7f8..527ae5e5 100644
--- a/src/stores/zine/authors.ts
+++ b/src/stores/zine/authors.ts
@@ -52,9 +52,7 @@ const addAuthors = (authors: Author[]) => {
}
export const loadAuthor = async ({ slug }: { slug: string }): Promise => {
- // TODO:
- const articles = await apiClient.getArticlesForAuthors({ authorSlugs: [slug], limit: 1 })
- const author = articles[0].authors.find((a) => a.slug === slug)
+ const author = await apiClient.getAuthor({ slug })
addAuthors([author])
}
diff --git a/src/stores/zine/topics.ts b/src/stores/zine/topics.ts
index 4992d873..12e3abf3 100644
--- a/src/stores/zine/topics.ts
+++ b/src/stores/zine/topics.ts
@@ -100,9 +100,7 @@ export const loadRandomTopics = async (): Promise => {
}
export const loadTopic = async ({ slug }: { slug: string }): Promise => {
- // TODO:
- const articles = await apiClient.getArticlesForTopics({ topicSlugs: [slug], limit: 1 })
- const topic = articles[0].topics.find(({ slug: topicSlug }) => topicSlug === slug)
+ const topic = await apiClient.getTopic({ slug })
addTopics([topic])
}
diff --git a/src/styles/AllTopics.scss b/src/styles/AllTopics.module.scss
similarity index 63%
rename from src/styles/AllTopics.scss
rename to src/styles/AllTopics.module.scss
index 2fd3ab75..4129e8f2 100644
--- a/src/styles/AllTopics.scss
+++ b/src/styles/AllTopics.module.scss
@@ -1,11 +1,4 @@
-.all-topics-page {
- .page-header,
- .group h2 {
- @include media-breakpoint-down(sm) {
- margin-left: 1.3rem;
- }
- }
-
+.allTopicsPage {
.group {
font-weight: bold;
margin: 3em 0 9.6rem;
@@ -24,9 +17,7 @@
}
.topic {
- @include media-breakpoint-down(md) {
- margin-top: 1.6rem;
- }
+ margin-bottom: 1.6rem;
@include media-breakpoint-down(sm) {
margin-left: 2.6rem;
@@ -38,3 +29,21 @@
width: auto;
}
}
+
+.stats {
+ margin-top: 2.4rem;
+}
+
+.loadMoreContainer {
+ margin-top: 48px;
+ text-align: center;
+
+ .loadMoreButton {
+ padding: 0.6em 5em;
+ width: 100%;
+
+ @include media-breakpoint-up(sm) {
+ width: auto;
+ }
+ }
+}
diff --git a/src/styles/Topic.scss b/src/styles/Topic.module.scss
similarity index 82%
rename from src/styles/Topic.scss
rename to src/styles/Topic.module.scss
index 20b16c28..fa18beab 100644
--- a/src/styles/Topic.scss
+++ b/src/styles/Topic.module.scss
@@ -1,11 +1,11 @@
-.topic-page {
- .group__controls {
+.topicPage {
+ .groupControls {
align-items: baseline;
margin-bottom: 4rem;
margin-top: 7rem;
}
- .floor--important {
+ .floorImportant {
a:hover {
background: #fff;
color: #000 !important;
diff --git a/src/styles/app.scss b/src/styles/app.scss
index 0e6e02e6..fa894ebc 100644
--- a/src/styles/app.scss
+++ b/src/styles/app.scss
@@ -3,7 +3,7 @@
@import 'bootstrap/scss/mixins/utilities';
@import 'bootstrap/scss/containers';
@import 'bootstrap/scss/grid';
-@import 'bootstrap/scss/utilities';
+@import 'bootstrap/scss/bootstrap-utilities';
:root {
--background-color: #fff;
@@ -20,6 +20,11 @@
box-sizing: border-box;
}
+::selection {
+ background: #000;
+ color: #fff;
+}
+
html {
color: $default-color;
font-size: 62.5%;
@@ -32,7 +37,7 @@ html {
body {
font-family: Muller, Arial, Helvetica, sans-serif;
font-size: 2rem;
- line-height: 1.4;
+ line-height: 1.6;
min-height: 100%;
text-size-adjust: 100%;
@@ -75,9 +80,15 @@ h2 {
.wrapped {
background: #000;
color: #fff;
+ margin-left: -0.15em;
padding: 0 0.15em;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
+
+ &::selection {
+ background: #fff;
+ color: #000;
+ }
}
}
@@ -92,6 +103,7 @@ h2 {
line-height: 1.1;
margin-bottom: 0.5em;
+ margin-top: 1.5em;
}
h3 {
@@ -228,6 +240,7 @@ button {
background: #fff;
border: 2px solid #000;
border-radius: 0.8rem;
+ color: #000;
&:hover {
background: #000;
@@ -236,6 +249,57 @@ button {
}
}
+.button--content-index {
+ background: none;
+ border: 2px solid #fff;
+ height: 3.2rem;
+ float: right;
+ padding: 0;
+ position: absolute;
+ right: $container-padding-x * 0.5;
+ top: -0.5rem;
+ width: 3.2rem;
+ z-index: 1;
+
+ @include media-breakpoint-up(md) {
+ margin-top: -0.5rem;
+ position: sticky;
+ top: 90px;
+ }
+
+ @include media-breakpoint-up(sm) {
+ right: $container-padding-x;
+ }
+
+ .icon {
+ background: #fff;
+ transition: filter 0.3s;
+ }
+
+ .icon,
+ img {
+ height: 100%;
+ vertical-align: middle;
+ width: auto;
+ }
+
+ &:hover {
+ .icon {
+ filter: invert(1);
+ }
+ }
+
+ .expanded {
+ border-radius: 100%;
+ overflow: hidden;
+
+ img {
+ height: auto;
+ margin-top: 0.8rem;
+ }
+ }
+}
+
form {
.pretty-form__item {
position: relative;
@@ -401,23 +465,23 @@ figcaption {
}
.view-switcher {
- @include font-size(2.2rem);
+ @include font-size(1.7rem);
+ display: flex;
+ flex-wrap: wrap;
font-weight: bold;
list-style: none;
- margin: 0;
+ margin: 3.6rem 0 0;
padding: 0;
- .all-topics-page & {
- @include media-breakpoint-down(sm) {
- margin-left: 1.3rem;
- }
- }
-
li {
display: inline-block;
margin-right: 1em;
- margin-bottom: 0.5em;
+ margin-bottom: 0.6em;
+
+ &:last-child {
+ margin-right: 0;
+ }
}
button {
@@ -433,6 +497,11 @@ figcaption {
a {
border-bottom: 2px solid #fff;
+ color: rgb(0 0 0 / 50%);
+
+ &:hover {
+ color: #fff;
+ }
}
.selected {
@@ -455,7 +524,13 @@ figcaption {
}
.view-switcher__search {
- margin-left: 2em;
+ text-align: right;
+ white-space: nowrap;
+
+ @include media-breakpoint-up(sm) {
+ flex: 1;
+ margin-left: 2em;
+ }
.icon {
display: inline-block;
@@ -515,6 +590,11 @@ figcaption {
padding-top: $grid-gutter-width;
}
+ ::selection {
+ background: #fff;
+ color: #000;
+ }
+
h2 {
@include font-size(4.4rem);
@@ -542,12 +622,10 @@ figcaption {
padding-left: divide($container-padding-x, 2);
padding-right: divide($container-padding-x, 2);
- /*
- .row {
+ > .row {
margin-left: divide(-$container-padding-x, 2);
margin-right: divide(-$container-padding-x, 2);
}
- */
}
}
@@ -565,7 +643,6 @@ astro-island {
min-height: 300px;
padding-top: 100px;
position: relative;
- transition: all 1s ease;
}
.main-content--no-padding {
@@ -575,26 +652,41 @@ astro-island {
.container {
max-width: 1400px;
+ width: auto;
- // margin-left: 201px;
- // width: auto;
-
- @include media-breakpoint-up(md) {
- // padding-left: 227px;
+ @include media-breakpoint-down(sm) {
+ // padding: 0 $container-padding-x * 0.5;
}
}
.container--static-page {
- padding-top: 1.5em;
+ @include font-size(1.7rem);
+
+ color: #404040;
+ position: relative;
+
+ @include media-breakpoint-up(md) {
+ padding-top: 1.5em;
+
+ > .row {
+ flex-wrap: nowrap;
+ justify-content: space-between;
+ margin-right: 0;
+ }
+ }
+
+ .order-md-last {
+ padding-right: 0;
+ }
}
.shift-content {
@include media-breakpoint-up(md) {
- margin-left: 127px;
+ margin-left: 161px;
}
@include media-breakpoint-up(lg) {
- margin-left: 201px;
+ margin-left: 235px;
}
}
@@ -620,7 +712,7 @@ astro-island {
.content-index {
@include font-size(1.4rem);
- margin-bottom: 2em;
+ margin: 0 3.6rem 2em 0;
@include media-breakpoint-up(md) {
position: sticky;
@@ -634,6 +726,14 @@ astro-island {
li {
margin-bottom: 1em;
}
+
+ a {
+ border: none;
+ }
+
+ h4 {
+ @include font-size(1.6rem);
+ }
}
.load-more-container {
@@ -643,3 +743,53 @@ astro-island {
padding: 0.6em 1.5em;
}
}
+
+details {
+ margin-bottom: 1.5em;
+
+ @include media-breakpoint-down(md) {
+ padding-left: 3rem;
+ }
+
+ summary {
+ display: block;
+ position: relative;
+
+ &::marker {
+ display: none;
+ }
+
+ h3 {
+ display: inline-block;
+ cursor: pointer;
+ margin-bottom: 0;
+
+ &::before {
+ content: '';
+ background: url(/icons/expand.svg) no-repeat;
+ background-size: contain;
+ height: 1.3rem;
+ margin-right: 0.5em;
+ position: absolute;
+ right: 100%;
+ top: 0.35em;
+ transition: transform 0.3s;
+ width: 2rem;
+ }
+ }
+ }
+
+ &[open] {
+ h3::before {
+ transform: rotate(180deg);
+ }
+ }
+}
+
+.text-truncate {
+ display: -webkit-box !important;
+ overflow: hidden;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ white-space: normal;
+}
diff --git a/src/utils/apiClient.ts b/src/utils/apiClient.ts
index b24b6ab7..7a5b436e 100644
--- a/src/utils/apiClient.ts
+++ b/src/utils/apiClient.ts
@@ -1,6 +1,14 @@
-import type { Reaction, Shout, FollowingEntity, AuthResult } from '../graphql/types.gen'
+import type {
+ Reaction,
+ Shout,
+ FollowingEntity,
+ AuthResult,
+ ShoutInput,
+ Topic,
+ Author
+} from '../graphql/types.gen'
import { publicGraphQLClient } from '../graphql/publicGraphQLClient'
-import { privateGraphQLClient } from '../graphql/privateGraphQLClient'
+import { getToken, privateGraphQLClient } from '../graphql/privateGraphQLClient'
import articleBySlug from '../graphql/query/article-by-slug'
import articlesRecentAll from '../graphql/query/articles-recent-all'
import articlesRecentPublished from '../graphql/query/articles-recent-published'
@@ -25,15 +33,21 @@ import authorsAll from '../graphql/query/authors-all'
import reactionCreate from '../graphql/mutation/reaction-create'
import reactionDestroy from '../graphql/mutation/reaction-destroy'
import reactionUpdate from '../graphql/mutation/reaction-update'
-import authorsBySlugs from '../graphql/query/authors-by-slugs'
import incrementView from '../graphql/mutation/increment-view'
-import myChats from '../graphql/query/im-chats'
-import loadChat from '../graphql/query/im-load-messages'
-import CreateChat from '../graphql/mutation/create-chat'
+import createArticle from '../graphql/mutation/article-create'
+import myChats from '../graphql/query/my-chats'
+import authorBySlug from '../graphql/query/author-by-slug'
+import topicBySlug from '../graphql/query/topic-by-slug'
const FEED_SIZE = 50
-type ApiErrorCode = 'unknown' | 'email_not_confirmed' | 'user_not_found' | 'user_already_exists'
+type ApiErrorCode =
+ | 'unknown'
+ | 'email_not_confirmed'
+ | 'user_not_found'
+ | 'user_already_exists'
+ | 'token_expired'
+ | 'token_invalid'
export class ApiError extends Error {
code: ApiErrorCode
@@ -45,7 +59,7 @@ export class ApiError extends Error {
}
export const apiClient = {
- authLogin: async ({ email, password }): Promise => {
+ authLogin: async ({ email, password }: { email: string; password: string }): Promise => {
const response = await publicGraphQLClient.query(authLoginQuery, { email, password }).toPromise()
// console.debug('[api-client] authLogin', { response })
if (response.error) {
@@ -99,13 +113,34 @@ export const apiClient = {
authSendLink: async ({ email, lang }) => {
// send link with code on email
const response = await publicGraphQLClient.mutation(authSendLinkMutation, { email, lang }).toPromise()
+
+ if (response.error) {
+ if (response.error.message === '[GraphQL] User not found') {
+ throw new ApiError('user_not_found', response.error.message)
+ }
+
+ throw new ApiError('unknown', response.error.message)
+ }
+
+ if (response.data.sendLink.error) {
+ throw new ApiError('unknown', response.data.sendLink.message)
+ }
+
return response.data.sendLink
},
confirmEmail: async ({ token }: { token: string }) => {
// confirm email with code from link
const response = await publicGraphQLClient.mutation(authConfirmEmailMutation, { token }).toPromise()
-
if (response.error) {
+ // TODO: better error communication
+ if (response.error.message === '[GraphQL] check token lifetime') {
+ throw new ApiError('token_expired', response.error.message)
+ }
+
+ if (response.error.message === '[GraphQL] token is not valid') {
+ throw new ApiError('token_invalid', response.error.message)
+ }
+
throw new ApiError('unknown', response.error.message)
}
@@ -238,11 +273,14 @@ export const apiClient = {
},
getSession: async (): Promise => {
+ if (!getToken()) {
+ return null
+ }
+
// renew session with auth token in header (!)
const response = await privateGraphQLClient.mutation(mySession, {}).toPromise()
if (response.error) {
- // TODO
throw new ApiError('unknown', response.error.message)
}
@@ -275,9 +313,13 @@ export const apiClient = {
}
return response.data.authorsAll
},
- getAuthor: async ({ slug }: { slug: string }) => {
- const response = await publicGraphQLClient.query(authorsBySlugs, { slugs: [slug] }).toPromise()
- return response.data.getUsersBySlugs
+ getAuthor: async ({ slug }: { slug: string }): Promise => {
+ const response = await publicGraphQLClient.query(authorBySlug, { slug }).toPromise()
+ return response.data.getAuthor
+ },
+ getTopic: async ({ slug }: { slug: string }): Promise => {
+ const response = await publicGraphQLClient.query(topicBySlug, { slug }).toPromise()
+ return response.data.getTopic
},
getArticle: async ({ slug }: { slug: string }): Promise => {
const response = await publicGraphQLClient.query(articleBySlug, { slug }).toPromise()
@@ -305,9 +347,10 @@ export const apiClient = {
return response.data.reactionsForShouts
},
- getAuthorsBySlugs: async ({ slugs }) => {
- const response = await publicGraphQLClient.query(authorsBySlugs, { slugs }).toPromise()
- return response.data.getUsersBySlugs
+ createArticle: async ({ article }: { article: ShoutInput }) => {
+ const response = await privateGraphQLClient.mutation(createArticle, { shout: article }).toPromise()
+ console.debug('createArticle response:', response)
+ return response.data.createShout
},
createReaction: async ({ reaction }) => {
const response = await privateGraphQLClient.mutation(reactionCreate, { reaction }).toPromise()
diff --git a/src/utils/config.ts b/src/utils/config.ts
index 8501f5bd..ab0ab70b 100644
--- a/src/utils/config.ts
+++ b/src/utils/config.ts
@@ -1,6 +1,4 @@
export const isDev = import.meta.env.MODE === 'development'
-export const apiBaseUrl = 'https://testapi.discours.io'
-// export const apiBaseUrl = 'https://newapi.discours.io'
-// testapi.discours.io
+export const apiBaseUrl = 'https://newapi.discours.io'
// export const apiBaseUrl = 'http://localhost:8080'
diff --git a/src/utils/useOutsideClickHandler.ts b/src/utils/useOutsideClickHandler.ts
index cfeb71a1..e1102d4f 100644
--- a/src/utils/useOutsideClickHandler.ts
+++ b/src/utils/useOutsideClickHandler.ts
@@ -17,7 +17,7 @@ export const useOutsideClickHandler = (options: Options) => {
return
}
- options.handler()
+ handler()
}
onMount(() => {
diff --git a/yarn.lock b/yarn.lock
index 764e2393..786a141a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7928,20 +7928,13 @@ prosemirror-menu@^1.0.0, prosemirror-menu@^1.2.1:
prosemirror-history "^1.0.0"
prosemirror-state "^1.0.0"
-prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.2.0:
+prosemirror-model@^1.0.0, prosemirror-model@^1.16.0:
version "1.18.1"
resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd"
integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==
dependencies:
orderedmap "^2.0.0"
-prosemirror-schema-basic@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.0.tgz#c33ad74426efae1d41e2260371866f623e8eb10e"
- integrity sha512-JMN/ammP94ObOUS6cpIy121r0MEDN9V95mAxFVALwC4bbmhpWXGjBGHTA5LHPPdbqZKyR6Jar1Akv4Z5k9CNLw==
- dependencies:
- prosemirror-model "^1.2.0"
-
prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.2.2.tgz#bafda37b72367d39accdcaf6ddf8fb654a16e8e5"