(bioWrapperRef.current = el)}
class={styles.longBio}
classList={{ [styles.longBioExpanded]: isBioExpanded() }}
>
diff --git a/src/components/_shared/CheckButton/CheckButton.module.scss b/src/components/_shared/CheckButton/CheckButton.module.scss
new file mode 100644
index 00000000..dd1a491b
--- /dev/null
+++ b/src/components/_shared/CheckButton/CheckButton.module.scss
@@ -0,0 +1,30 @@
+.CheckButton {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ height: 32px;
+ min-width: 33px;
+ box-sizing: border-box;
+ padding: 0 8px;
+ background: var(--background-color);
+ color: var(--default-color);
+ border: 2px solid var(--default-color);
+ border-radius: 8px;
+ overflow: hidden;
+ align-self: center;
+
+ .close {
+ display: none;
+ }
+
+ &:hover {
+ background: var(--background-color-invert);
+ color: var(--default-color-invert);
+ .check {
+ display: none;
+ }
+ .close {
+ display: block;
+ }
+ }
+}
diff --git a/src/components/_shared/CheckButton/CheckButton.tsx b/src/components/_shared/CheckButton/CheckButton.tsx
new file mode 100644
index 00000000..a8dcd401
--- /dev/null
+++ b/src/components/_shared/CheckButton/CheckButton.tsx
@@ -0,0 +1,38 @@
+import { clsx } from 'clsx'
+import styles from './CheckButton.module.scss'
+import { Icon } from '../Icon'
+import { createSignal, Show } from 'solid-js'
+
+type Props = {
+ class?: string
+ checked: boolean
+ text: string
+ onClick: () => void
+}
+
+// Signed - check mark icon
+// On hover - cross icon
+// If you clicked on the cross, you unsubscribed. Then the “Subscribe” button appears
+
+export const CheckButton = (props: Props) => {
+ const [clicked, setClicked] = createSignal(!props.checked)
+ const handleClick = () => {
+ props.onClick()
+ setClicked((prev) => !prev)
+ }
+ return (
+
+ )
+}
diff --git a/src/components/_shared/CheckButton/index.ts b/src/components/_shared/CheckButton/index.ts
new file mode 100644
index 00000000..2233633d
--- /dev/null
+++ b/src/components/_shared/CheckButton/index.ts
@@ -0,0 +1 @@
+export { CheckButton } from './CheckButton'
diff --git a/src/components/_shared/SearchField.module.scss b/src/components/_shared/SearchField/SearchField.module.scss
similarity index 61%
rename from src/components/_shared/SearchField.module.scss
rename to src/components/_shared/SearchField/SearchField.module.scss
index 87fc6c72..03811331 100644
--- a/src/components/_shared/SearchField.module.scss
+++ b/src/components/_shared/SearchField/SearchField.module.scss
@@ -1,6 +1,23 @@
.searchField {
display: flex;
justify-content: flex-end;
+ position: relative;
+
+ &.bordered {
+ border: 2px solid var(--black-100);
+ padding: 10px 0 12px 10px;
+
+ input {
+ width: 100%;
+ display: block;
+ box-sizing: border-box;
+ margin-right: 40px;
+
+ &:focus {
+ box-shadow: unset;
+ }
+ }
+ }
input {
border: none;
diff --git a/src/components/_shared/SearchField.tsx b/src/components/_shared/SearchField/SearchField.tsx
similarity index 61%
rename from src/components/_shared/SearchField.tsx
rename to src/components/_shared/SearchField/SearchField.tsx
index 8b1d91fe..5401b04c 100644
--- a/src/components/_shared/SearchField.tsx
+++ b/src/components/_shared/SearchField/SearchField.tsx
@@ -1,19 +1,20 @@
import styles from './SearchField.module.scss'
-import { Icon } from './Icon'
+import { Icon } from '../Icon'
import { clsx } from 'clsx'
-import { useLocalize } from '../../context/localize'
+import { useLocalize } from '../../../context/localize'
-type SearchFieldProps = {
+type Props = {
onChange: (value: string) => void
class?: string
+ variant?: 'bordered'
}
-export const SearchField = (props: SearchFieldProps) => {
+export const SearchField = (props: Props) => {
const handleInputChange = (event) => props.onChange(event.target.value.trim())
const { t } = useLocalize()
return (
-
+
@@ -24,7 +25,7 @@ export const SearchField = (props: SearchFieldProps) => {
onInput={handleInputChange}
placeholder={t('Search')}
/>
-
+
)
}
diff --git a/src/components/_shared/SearchField/index.ts b/src/components/_shared/SearchField/index.ts
new file mode 100644
index 00000000..5ef4d0da
--- /dev/null
+++ b/src/components/_shared/SearchField/index.ts
@@ -0,0 +1 @@
+export { SearchField } from './SearchField'
diff --git a/src/context/profile.tsx b/src/context/profile.tsx
index 01ec6e70..0038b518 100644
--- a/src/context/profile.tsx
+++ b/src/context/profile.tsx
@@ -5,6 +5,12 @@ import { loadAuthor, useAuthorsStore } from '../stores/zine/authors'
import { apiClient } from '../utils/apiClient'
import type { ProfileInput } from '../graphql/types.gen'
+const userpicUrl = (userpic: string) => {
+ if (userpic.includes('assets.discours.io')) {
+ return userpic.replace('100x', '500x500')
+ }
+ return userpic
+}
const useProfileForm = () => {
const { session } = useSession()
const currentSlug = createMemo(() => session()?.user?.slug)
@@ -34,13 +40,12 @@ const useProfileForm = () => {
if (!currentSlug()) return
try {
await loadAuthor({ slug: currentSlug() })
-
setForm({
name: currentAuthor()?.name,
slug: currentAuthor()?.slug,
bio: currentAuthor()?.bio,
about: currentAuthor()?.about,
- userpic: currentAuthor()?.userpic.replace('100x', '500x500'),
+ userpic: userpicUrl(currentAuthor()?.userpic),
links: currentAuthor()?.links
})
} catch (error) {
diff --git a/src/pages/profile/Settings.module.scss b/src/pages/profile/Settings.module.scss
index 5282c107..a6ce9407 100644
--- a/src/pages/profile/Settings.module.scss
+++ b/src/pages/profile/Settings.module.scss
@@ -108,12 +108,12 @@ h5 {
}
.searchField {
- display: block;
+ margin-bottom: 2rem;
label:first-child {
opacity: 0.5;
position: absolute;
- right: 1em;
+ right: 12px;
transform: translateY(-50%);
top: 50%;
}
diff --git a/src/pages/profile/profileSecurity.page.tsx b/src/pages/profile/profileSecurity.page.tsx
index a32e13ce..2c95bbfb 100644
--- a/src/pages/profile/profileSecurity.page.tsx
+++ b/src/pages/profile/profileSecurity.page.tsx
@@ -2,7 +2,7 @@ import { PageLayout } from '../../components/_shared/PageLayout'
import styles from './Settings.module.scss'
import { Icon } from '../../components/_shared/Icon'
import { clsx } from 'clsx'
-import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
+import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation'
export const ProfileSecurityPage = () => {
return (
diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx
index e9c67d1d..da392bda 100644
--- a/src/pages/profile/profileSettings.page.tsx
+++ b/src/pages/profile/profileSettings.page.tsx
@@ -1,6 +1,6 @@
import { PageLayout } from '../../components/_shared/PageLayout'
import { Icon } from '../../components/_shared/Icon'
-import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
+import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation'
import { For, createSignal, Show, onMount, onCleanup, createEffect } from 'solid-js'
import deepEqual from 'fast-deep-equal'
import { clsx } from 'clsx'
diff --git a/src/pages/profile/profileSubscriptions.page.tsx b/src/pages/profile/profileSubscriptions.page.tsx
index 2b21924c..8500788f 100644
--- a/src/pages/profile/profileSubscriptions.page.tsx
+++ b/src/pages/profile/profileSubscriptions.page.tsx
@@ -2,10 +2,61 @@ import { PageLayout } from '../../components/_shared/PageLayout'
import styles from './Settings.module.scss'
import stylesSettings from '../../styles/FeedSettings.module.scss'
import { clsx } from 'clsx'
-import ProfileSettingsNavigation from '../../components/Discours/ProfileSettingsNavigation'
+import { ProfileSettingsNavigation } from '../../components/Nav/ProfileSettingsNavigation'
import { SearchField } from '../../components/_shared/SearchField'
+import { createEffect, createSignal, For, onMount, Show } from 'solid-js'
+import { Author, Topic } from '../../graphql/types.gen'
+import { apiClient } from '../../utils/apiClient'
+import { useSession } from '../../context/session'
+import { isAuthor } from '../../utils/isAuthor'
+import { useLocalize } from '../../context/localize'
+import { SubscriptionFilter } from '../types'
+import { Loading } from '../../components/_shared/Loading'
+import { TopicCard } from '../../components/Topic/Card'
+import { AuthorCard } from '../../components/Author/AuthorCard'
+import { dummyFilter } from '../../utils/dummyFilter'
export const ProfileSubscriptionsPage = () => {
+ const { t, lang } = useLocalize()
+ const { user, isAuthenticated } = useSession()
+ const [following, setFollowing] = createSignal
>([])
+ const [filtered, setFiltered] = createSignal>([])
+ const [subscriptionFilter, setSubscriptionFilter] = createSignal('all')
+ const [searchQuery, setSearchQuery] = createSignal('')
+
+ const fetchSubscriptions = async () => {
+ try {
+ const [getAuthors, getTopics] = await Promise.all([
+ apiClient.getAuthorFollowingUsers({ slug: user().slug }),
+ apiClient.getAuthorFollowingTopics({ slug: user().slug })
+ ])
+ setFollowing([...getAuthors, ...getTopics])
+ setFiltered([...getAuthors, ...getTopics])
+ } catch (error) {
+ console.error('[fetchSubscriptions] :', error)
+ throw error
+ }
+ }
+
+ onMount(async () => {
+ if (isAuthenticated()) {
+ await fetchSubscriptions()
+ }
+ })
+
+ createEffect(() => {
+ if (following()) {
+ if (subscriptionFilter() === 'users') {
+ setFiltered(following().filter((s) => 'name' in s))
+ } else if (subscriptionFilter() === 'topics') {
+ setFiltered(following().filter((s) => 'title' in s))
+ } else {
+ setFiltered(following())
+ }
+ }
+ setFiltered(dummyFilter(following(), searchQuery(), lang()))
+ })
+
return (
@@ -19,112 +70,65 @@ export const ProfileSubscriptionsPage = () => {
-
Подписки
-
Здесь можно управлять всеми своими подписками на сайте.
-
-
+
diff --git a/src/pages/types.ts b/src/pages/types.ts
index b8aac518..c66bc7f3 100644
--- a/src/pages/types.ts
+++ b/src/pages/types.ts
@@ -46,3 +46,5 @@ export type UploadedFile = {
url: string
originalFilename?: string
}
+
+export type SubscriptionFilter = 'all' | 'users' | 'topics'
diff --git a/src/styles/FeedSettings.module.scss b/src/styles/FeedSettings.module.scss
index 49a1ae37..bff45ed7 100644
--- a/src/styles/FeedSettings.module.scss
+++ b/src/styles/FeedSettings.module.scss
@@ -1,5 +1,6 @@
.settingsList {
display: table;
+ width: 100%;
h2 {
margin-top: 1em;
@@ -45,16 +46,3 @@
}
}
}
-
-.settingsListRow {
- display: table-row;
-}
-
-.settingsListCell {
- display: table-cell;
- padding: 0 0.5em 1em 0;
-
- &:first-child {
- padding-right: 2em;
- }
-}
diff --git a/src/utils/dummyFilter.ts b/src/utils/dummyFilter.ts
new file mode 100644
index 00000000..fe4d8f4b
--- /dev/null
+++ b/src/utils/dummyFilter.ts
@@ -0,0 +1,36 @@
+import { translit } from './ru2en'
+import { Author, Topic } from '../graphql/types.gen'
+
+type SearchData = Array
+
+const prepareQuery = (searchQuery, lang) => {
+ const q = searchQuery.toLowerCase()
+ if (q.length === 0) return ''
+ return lang === 'ru' ? translit(q) : q
+}
+
+const stringMatches = (str, q, lang) => {
+ const preparedStr = lang === 'ru' ? translit(str.toLowerCase()) : str.toLowerCase()
+ return preparedStr.split(' ').some((word) => word.startsWith(q))
+}
+
+export const dummyFilter = (data: SearchData, searchQuery: string, lang: 'ru' | 'en'): SearchData => {
+ const q = prepareQuery(searchQuery, lang)
+ if (q.length === 0) return data
+
+ return data.filter((item) => {
+ const slugMatches = item.slug && item.slug.split('-').some((w) => w.startsWith(q))
+ if (slugMatches) return true
+
+ if ('title' in item) {
+ return stringMatches(item.title, q, lang)
+ }
+
+ if ('name' in item) {
+ return stringMatches(item.name, q, lang) || (item.bio && stringMatches(item.bio, q, lang))
+ }
+ // If it does not match any of the 'slug', 'title', 'name' , 'bio' fields
+ // current element should not be included in the filtered array
+ return false
+ })
+}
diff --git a/src/utils/isAuthor.ts b/src/utils/isAuthor.ts
new file mode 100644
index 00000000..3d939b64
--- /dev/null
+++ b/src/utils/isAuthor.ts
@@ -0,0 +1,5 @@
+import { Author, Topic } from '../graphql/types.gen'
+
+export const isAuthor = (value: Author | Topic): value is Author => {
+ return 'name' in value
+}