diff --git a/src/app.tsx b/src/app.tsx
index df984dfd..a05e3cfc 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -7,7 +7,6 @@ import { Loading } from './components/_shared/Loading'
import { AuthorsProvider } from './context/authors'
import { EditorProvider } from './context/editor'
import { FeedProvider } from './context/feed'
-import { GraphQLClientProvider } from './context/graphql'
import { LocalizeProvider } from './context/localize'
import { SessionProvider } from './context/session'
import { TopicsProvider } from './context/topics'
@@ -18,22 +17,20 @@ export const Providers = (props: { children?: JSX.Element }) => {
return (
-
-
-
-
-
-
-
-
- }>{props.children}
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ }>{props.children}
+
+
+
+
+
+
)
diff --git a/src/components/Article/Comment/Comment.tsx b/src/components/Article/Comment/Comment.tsx
index 18693fa2..5bb4b6e8 100644
--- a/src/components/Article/Comment/Comment.tsx
+++ b/src/components/Article/Comment/Comment.tsx
@@ -3,11 +3,12 @@ import { clsx } from 'clsx'
import { For, Show, Suspense, createMemo, createSignal, lazy } from 'solid-js'
import { Icon } from '~/components/_shared/Icon'
import { ShowIfAuthenticated } from '~/components/_shared/ShowIfAuthenticated'
-import { useGraphQL } from '~/context/graphql'
+import { coreApiUrl } from '~/config'
import { useLocalize } from '~/context/localize'
import { useReactions } from '~/context/reactions'
import { useSession } from '~/context/session'
import { useSnackbar, useUI } from '~/context/ui'
+import { graphqlClientCreate } from '~/graphql/client'
import deleteReactionMutation from '~/graphql/mutation/core/reaction-destroy'
import {
Author,
@@ -49,8 +50,7 @@ export const Comment = (props: Props) => {
const { createReaction, updateReaction } = useReactions()
const { showConfirm } = useUI()
const { showSnackbar } = useSnackbar()
- const { mutation } = useGraphQL()
-
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
const canEdit = createMemo(
() =>
Boolean(author()?.id) &&
@@ -70,7 +70,9 @@ export const Comment = (props: Props) => {
})
if (isConfirmed) {
- const resp = await mutation(deleteReactionMutation, { id: props.comment.id }).toPromise()
+ const resp = await client()
+ ?.mutation(deleteReactionMutation, { id: props.comment.id })
+ .toPromise()
const result = resp?.data?.delete_reaction
const { error } = result
const notificationType = error ? 'error' : 'success'
diff --git a/src/components/Author/AuthorRatingControl.tsx b/src/components/Author/AuthorRatingControl.tsx
index 5220b9ea..531ec61e 100644
--- a/src/components/Author/AuthorRatingControl.tsx
+++ b/src/components/Author/AuthorRatingControl.tsx
@@ -1,8 +1,10 @@
import type { Author } from '~/graphql/schema/core.gen'
import { clsx } from 'clsx'
-import { Show, createSignal } from 'solid-js'
-import { useGraphQL } from '~/context/graphql'
+import { Show, createMemo, createSignal } from 'solid-js'
+import { coreApiUrl } from '~/config'
+import { useSession } from '~/context/session'
+import { graphqlClientCreate } from '~/graphql/client'
import rateAuthorMutation from '~/graphql/mutation/core/author-rate'
import styles from './AuthorRatingControl.module.scss'
@@ -14,19 +16,24 @@ interface AuthorRatingControlProps {
export const AuthorRatingControl = (props: AuthorRatingControlProps) => {
const isUpvoted = false
const isDownvoted = false
+
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
// eslint-disable-next-line unicorn/consistent-function-scoping
const handleRatingChange = async (isUpvote: boolean) => {
console.log('handleRatingChange', { isUpvote })
if (props.author?.slug) {
const value = isUpvote ? 1 : -1
- const _resp = await mutation(rateAuthorMutation, {
- rated_slug: props.author?.slug,
- value
- }).toPromise()
+ const _resp = await client()
+ ?.mutation(rateAuthorMutation, {
+ rated_slug: props.author?.slug,
+ value
+ })
+ .toPromise()
setRating((r) => (r || 0) + value)
}
}
- const { mutation } = useGraphQL()
const [rating, setRating] = createSignal(props.author?.stat?.rating)
return (
{
// contexts
const { t } = useLocalize()
const loc = useLocation()
+
const { session } = useSession()
- const { query } = useGraphQL()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const { loadAuthor, authorsEntities } = useAuthors()
const { followers: myFollowers, follows: myFollows } = useFollowing()
@@ -70,11 +73,15 @@ export const AuthorView = (props: AuthorViewProps) => {
setAuthor(foundAuthor)
if (foundAuthor) {
- const followsResp = await query(getAuthorFollowsQuery, { slug: foundAuthor.slug }).toPromise()
+ const followsResp = await client()
+ ?.query(getAuthorFollowsQuery, { slug: foundAuthor.slug })
+ .toPromise()
const follows = followsResp?.data?.get_author_followers || {}
changeFollowing([...(follows?.authors || []), ...(follows?.topics || [])])
- const followersResp = await query(getAuthorFollowersQuery, { slug: foundAuthor.slug }).toPromise()
+ const followersResp = await client()
+ ?.query(getAuthorFollowersQuery, { slug: foundAuthor.slug })
+ .toPromise()
setFollowers(followersResp?.data?.get_author_followers || [])
}
}
diff --git a/src/components/Views/EditView/EditSettingsView.tsx b/src/components/Views/EditView/EditSettingsView.tsx
index 72843f2b..45b24127 100644
--- a/src/components/Views/EditView/EditSettingsView.tsx
+++ b/src/components/Views/EditView/EditSettingsView.tsx
@@ -1,13 +1,15 @@
import { clsx } from 'clsx'
import deepEqual from 'fast-deep-equal'
-import { Show, createEffect, createSignal, on, onCleanup, onMount } from 'solid-js'
+import { Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js'
import { createStore } from 'solid-js/store'
import { debounce } from 'throttle-debounce'
import { Icon } from '~/components/_shared/Icon'
import { InviteMembers } from '~/components/_shared/InviteMembers'
+import { coreApiUrl } from '~/config'
import { ShoutForm, useEditorContext } from '~/context/editor'
-import { useGraphQL } from '~/context/graphql'
import { useLocalize } from '~/context/localize'
+import { useSession } from '~/context/session'
+import { graphqlClientCreate } from '~/graphql/client'
import getMyShoutQuery from '~/graphql/query/core/article-my'
import type { Shout, Topic } from '~/graphql/schema/core.gen'
import { isDesktop } from '~/lib/mediaQuery'
@@ -42,7 +44,9 @@ const handleScrollTopButtonClick = (ev: MouseEvent | TouchEvent) => {
export const EditSettingsView = (props: Props) => {
const { t } = useLocalize()
const [isScrolled, setIsScrolled] = createSignal(false)
- const { query } = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const { form, setForm, saveDraft, saveDraftToLocalStorage, getDraftFromLocalStorage } = useEditorContext()
const [shoutTopics, setShoutTopics] = createSignal
([])
const [draft, setDraft] = createSignal()
@@ -106,7 +110,7 @@ export const EditSettingsView = (props: Props) => {
() => props.shout?.id,
async (shoutId) => {
if (shoutId) {
- const resp = await query(getMyShoutQuery, { shout_id: shoutId })
+ const resp = await client()?.query(getMyShoutQuery, { shout_id: shoutId })
const result = resp?.data?.get_my_shout
if (result) {
console.debug('[EditView] getMyShout result: ', result)
diff --git a/src/components/Views/EditView/EditView.tsx b/src/components/Views/EditView/EditView.tsx
index dc7cd775..32c6bbe1 100644
--- a/src/components/Views/EditView/EditView.tsx
+++ b/src/components/Views/EditView/EditView.tsx
@@ -19,9 +19,11 @@ import { InviteMembers } from '~/components/_shared/InviteMembers'
import { Loading } from '~/components/_shared/Loading'
import { Popover } from '~/components/_shared/Popover'
import { EditorSwiper } from '~/components/_shared/SolidSwiper'
+import { coreApiUrl } from '~/config'
import { ShoutForm, useEditorContext } from '~/context/editor'
-import { useGraphQL } from '~/context/graphql'
import { useLocalize } from '~/context/localize'
+import { useSession } from '~/context/session'
+import { graphqlClientCreate } from '~/graphql/client'
import getMyShoutQuery from '~/graphql/query/core/article-my'
import type { Shout, Topic } from '~/graphql/schema/core.gen'
import { slugify } from '~/intl/translit'
@@ -64,7 +66,9 @@ const handleScrollTopButtonClick = (ev: MouseEvent | TouchEvent) => {
export const EditView = (props: Props) => {
const { t } = useLocalize()
const [isScrolled, setIsScrolled] = createSignal(false)
- const { query } = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const {
form,
formErrors,
@@ -140,7 +144,7 @@ export const EditView = (props: Props) => {
() => props.shout?.id,
async (shoutId) => {
if (shoutId) {
- const resp = await query(getMyShoutQuery, { shout_id: shoutId })
+ const resp = await client()?.query(getMyShoutQuery, { shout_id: shoutId })
const result = resp?.data?.get_my_shout
if (result) {
console.debug('[EditView] getMyShout result: ', result)
diff --git a/src/components/Views/Expo/Expo.tsx b/src/components/Views/Expo/Expo.tsx
index 4ccf75a8..1756c444 100644
--- a/src/components/Views/Expo/Expo.tsx
+++ b/src/components/Views/Expo/Expo.tsx
@@ -5,10 +5,12 @@ import { ConditionalWrapper } from '~/components/_shared/ConditionalWrapper'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { Loading } from '~/components/_shared/Loading'
import { ArticleCardSwiper } from '~/components/_shared/SolidSwiper/ArticleCardSwiper'
+import { coreApiUrl } from '~/config'
import { useFeed } from '~/context/feed'
-import { useGraphQL } from '~/context/graphql'
import { useLocalize } from '~/context/localize'
+import { useSession } from '~/context/session'
import { loadShouts } from '~/graphql/api/public'
+import { graphqlClientCreate } from '~/graphql/client'
import getRandomTopShoutsQuery from '~/graphql/query/core/articles-load-random-top'
import { LoadShoutsFilters, LoadShoutsOptions, Shout } from '~/graphql/schema/core.gen'
import { SHOUTS_PER_PAGE } from '~/routes/(main)'
@@ -29,7 +31,9 @@ const LOAD_MORE_PAGE_SIZE = 12
export const Expo = (props: Props) => {
const { t } = useLocalize()
- const { query } = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const [favoriteTopArticles, setFavoriteTopArticles] = createSignal([])
const [reactedTopMonthArticles, setReactedTopMonthArticles] = createSignal([])
const [expoShouts, setExpoShouts] = createSignal([])
@@ -55,7 +59,7 @@ export const Expo = (props: Props) => {
limit: 10,
random_limit: 100
}
- const resp = await query(getRandomTopShoutsQuery, { options }).toPromise()
+ const resp = await client()?.query(getRandomTopShoutsQuery, { options }).toPromise()
setFavoriteTopArticles(resp?.data?.load_shouts_random_top || [])
}
@@ -67,7 +71,7 @@ export const Expo = (props: Props) => {
limit: 10,
random_limit: 10
}
- const resp = await query(getRandomTopShoutsQuery, { options }).toPromise()
+ const resp = await client()?.query(getRandomTopShoutsQuery, { options }).toPromise()
setReactedTopMonthArticles(resp?.data?.load_shouts_random_top || [])
}
diff --git a/src/components/Views/Feed/Feed.tsx b/src/components/Views/Feed/Feed.tsx
index c49e6d41..a9d84ff1 100644
--- a/src/components/Views/Feed/Feed.tsx
+++ b/src/components/Views/Feed/Feed.tsx
@@ -7,14 +7,15 @@ import { Icon } from '~/components/_shared/Icon'
import { InviteMembers } from '~/components/_shared/InviteMembers'
import { Loading } from '~/components/_shared/Loading'
import { ShareModal } from '~/components/_shared/ShareModal'
+import { coreApiUrl } from '~/config'
import { useAuthors } from '~/context/authors'
-import { useGraphQL } from '~/context/graphql'
import { useLocalize } from '~/context/localize'
import { useReactions } from '~/context/reactions'
import { useSession } from '~/context/session'
import { useTopics } from '~/context/topics'
import { useUI } from '~/context/ui'
import { loadUnratedShouts } from '~/graphql/api/private'
+import { graphqlClientCreate } from '~/graphql/client'
import type { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
import { byCreated } from '~/lib/sort'
import { FeedSearchParams } from '~/routes/feed/[...order]'
@@ -48,10 +49,12 @@ const PERIODS = {
export const FeedView = (props: FeedProps) => {
const { t } = useLocalize()
const loc = useLocation()
- const client = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const unrated = createAsync(async () => {
if (client) {
- const shoutsLoader = loadUnratedShouts(client, { limit: 5 })
+ const shoutsLoader = loadUnratedShouts(client(), { limit: 5 })
return await shoutsLoader()
}
})
@@ -59,7 +62,6 @@ export const FeedView = (props: FeedProps) => {
const { showModal } = useUI()
const [isLoading, setIsLoading] = createSignal(false)
const [isRightColumnLoaded, setIsRightColumnLoaded] = createSignal(false)
- const { session } = useSession()
const { loadReactionsBy } = useReactions()
const { topTopics } = useTopics()
const { topAuthors } = useAuthors()
diff --git a/src/context/editor.tsx b/src/context/editor.tsx
index 0462db5c..f668e5d4 100644
--- a/src/context/editor.tsx
+++ b/src/context/editor.tsx
@@ -1,16 +1,18 @@
import { useMatch, useNavigate } from '@solidjs/router'
import { Editor } from '@tiptap/core'
import type { JSX } from 'solid-js'
-import { Accessor, createContext, createSignal, useContext } from 'solid-js'
+import { Accessor, createContext, createMemo, createSignal, useContext } from 'solid-js'
import { SetStoreFunction, createStore } from 'solid-js/store'
+import { coreApiUrl } from '~/config'
import { useSnackbar } from '~/context/ui'
import deleteShoutQuery from '~/graphql/mutation/core/article-delete'
import updateShoutQuery from '~/graphql/mutation/core/article-update'
import { Topic, TopicInput } from '~/graphql/schema/core.gen'
import { slugify } from '~/intl/translit'
import { useFeed } from '../context/feed'
-import { useGraphQL } from './graphql'
+import { graphqlClientCreate } from '../graphql/client'
import { useLocalize } from './localize'
+import { useSession } from './session'
type WordCounter = {
characters: number
@@ -82,7 +84,9 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
const navigate = useNavigate()
const matchEdit = useMatch(() => '/edit')
const matchEditSettings = useMatch(() => '/editSettings')
- const { mutation } = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const { addFeed } = useFeed()
const snackbar = useSnackbar()
const [isEditorPanelVisible, setIsEditorPanelVisible] = createSignal(false)
@@ -133,7 +137,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
console.error(formToUpdate)
return { error: 'not enought data' }
}
- const resp = await mutation(updateShoutQuery, {
+ const resp = await client()?.mutation(updateShoutQuery, {
shout_id: formToUpdate.shoutId,
shout_input: {
body: formToUpdate.body,
@@ -235,7 +239,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
return
}
try {
- const resp = await mutation(updateShoutQuery, { shout_id, publish: true }).toPromise()
+ const resp = await client()?.mutation(updateShoutQuery, { shout_id, publish: true }).toPromise()
const result = resp?.data?.update_shout
if (result) {
const { shout: newShout, error } = result
@@ -259,7 +263,7 @@ export const EditorProvider = (props: { children: JSX.Element }) => {
const deleteShout = async (shout_id: number) => {
try {
- const resp = await mutation(deleteShoutQuery, { shout_id }).toPromise()
+ const resp = await client()?.mutation(deleteShoutQuery, { shout_id }).toPromise()
return resp?.data?.delete_shout
} catch {
snackbar?.showSnackbar({ type: 'error', body: localize?.t('Error') || '' })
diff --git a/src/context/feed.tsx b/src/context/feed.tsx
index 84a2a697..1ef90e07 100644
--- a/src/context/feed.tsx
+++ b/src/context/feed.tsx
@@ -1,6 +1,7 @@
import { createLazyMemo } from '@solid-primitives/memo'
import { makePersisted } from '@solid-primitives/storage'
-import { Accessor, JSX, Setter, createContext, createSignal, useContext } from 'solid-js'
+import { Accessor, JSX, Setter, createContext, createMemo, createSignal, useContext } from 'solid-js'
+import { coreApiUrl } from '~/config'
import { loadFollowedShouts } from '~/graphql/api/private'
import { loadShoutsSearch as fetchShoutsSearch, getShout, loadShouts } from '~/graphql/api/public'
import {
@@ -10,8 +11,9 @@ import {
Shout,
Topic
} from '~/graphql/schema/core.gen'
+import { graphqlClientCreate } from '../graphql/client'
import { byStat } from '../lib/sort'
-import { useGraphQL } from './graphql'
+import { useSession } from './session'
export const PRERENDERED_ARTICLES_COUNT = 5
export const SHOUTS_PER_PAGE = 20
@@ -172,14 +174,16 @@ export const FeedProvider = (props: { children: JSX.Element }) => {
addFeed(result)
return { hasMore, newShouts: result }
}
- const client = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
// Load the user's feed based on the provided options and update the articleEntities and sortedFeed state
const loadMyFeed = async (
options: LoadShoutsOptions
): Promise<{ hasMore: boolean; newShouts: Shout[] }> => {
if (!options.limit) options.limit = 0
options.limit += 1
- const fetcher = await loadFollowedShouts(client, options)
+ const fetcher = await loadFollowedShouts(client(), options)
const result = (await fetcher()) || []
const hasMore = result.length === options.limit + 1
if (hasMore) result.splice(-1)
diff --git a/src/context/following.tsx b/src/context/following.tsx
index 15512ef1..69425d1c 100644
--- a/src/context/following.tsx
+++ b/src/context/following.tsx
@@ -10,11 +10,12 @@ import {
} from 'solid-js'
import { createStore } from 'solid-js/store'
+import { coreApiUrl } from '~/config'
import followMutation from '~/graphql/mutation/core/follow'
import unfollowMutation from '~/graphql/mutation/core/unfollow'
import loadAuthorFollowers from '~/graphql/query/core/author-followers'
import { Author, Community, FollowingEntity, Topic } from '~/graphql/schema/core.gen'
-import { useGraphQL } from './graphql'
+import { graphqlClientCreate } from '../graphql/client'
import { useSession } from './session'
export type FollowsFilter = 'all' | 'authors' | 'topics' | 'communities'
@@ -71,14 +72,14 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
const [follows, setFollows] = createStore(EMPTY_SUBSCRIPTIONS)
const { session } = useSession()
const authorized = createMemo(() => Boolean(session()?.access_token))
- const { query, mutation } = useGraphQL()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
const fetchData = async () => {
setLoading(true)
try {
if (session()?.access_token) {
console.debug('[context.following] fetching subs data...')
- const result = await query(loadAuthorFollowers, { user: session()?.user?.id }).toPromise()
+ const result = await client()?.query(loadAuthorFollowers, { user: session()?.user?.id }).toPromise()
if (result) {
setFollows((_: AuthorFollowsResult) => {
return { ...EMPTY_SUBSCRIPTIONS, ...result } as AuthorFollowsResult
@@ -98,7 +99,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
if (!authorized()) return
setFollowing({ slug, type: 'follow' })
try {
- const resp = await mutation(followMutation, { what, slug }).toPromise()
+ const resp = await client()?.mutation(followMutation, { what, slug }).toPromise()
const result = resp?.data?.follow
if (!result) return
setFollows((subs) => {
@@ -117,7 +118,7 @@ export const FollowingProvider = (props: { children: JSX.Element }) => {
if (!authorized()) return
setFollowing({ slug: slug, type: 'unfollow' })
try {
- const resp = await mutation(unfollowMutation, { what, slug }).toPromise()
+ const resp = await client()?.mutation(unfollowMutation, { what, slug }).toPromise()
const result = resp?.data?.unfollow
if (!result) return
setFollows((subs) => {
diff --git a/src/context/graphql.tsx b/src/context/graphql.tsx
deleted file mode 100644
index d40f83dc..00000000
--- a/src/context/graphql.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Client, ClientOptions, cacheExchange, createClient, fetchExchange } from '@urql/core'
-import { createContext, createEffect, createSignal, on, useContext } from 'solid-js'
-import { JSX } from 'solid-js/jsx-runtime'
-import { chatApiUrl, coreApiUrl } from '../config'
-import { useSession } from './session'
-
-type GraphQLClientContextType = Record
-
-const GraphQLClientContext = createContext({} as GraphQLClientContextType)
-
-// Function to create a GraphQL client based on the provided URL and token
-const graphqlClientCreate = (url: string, token = ''): Client => {
- const exchanges = [fetchExchange, cacheExchange]
- const options: ClientOptions = {
- url,
- exchanges
- }
-
- if (token) {
- options.fetchOptions = () => ({
- headers: {
- 'content-type': 'application/json',
- authorization: token
- }
- })
- }
-
- return createClient(options)
-}
-
-export const defaultClient = graphqlClientCreate(coreApiUrl)
-
-export const GraphQLClientProvider = (props: { children?: JSX.Element }) => {
- const { session } = useSession()
- const [clients, setClients] = createSignal({ [coreApiUrl]: defaultClient })
-
- createEffect(
- on(
- () => session?.()?.access_token || '',
- (tkn: string) => {
- if (tkn) {
- console.info('[context.graphql] authorized client')
- setClients(() => ({
- [coreApiUrl]: graphqlClientCreate(coreApiUrl, tkn),
- [chatApiUrl]: graphqlClientCreate(chatApiUrl, tkn)
- }))
- } else {
- console.info('[context.graphql] can fetch data')
- setClients(() => ({
- [coreApiUrl]: defaultClient
- }))
- }
- },
- { defer: true }
- )
- )
-
- return {props.children}
-}
-
-export const useGraphQL = (url: string = coreApiUrl) => {
- const clients = useContext(GraphQLClientContext)
- let c = clients[coreApiUrl]
- if (url !== coreApiUrl) {
- try {
- c = clients[url]
- } catch (_e) {
- // pass
- }
- }
- if (!c) c = clients[coreApiUrl]
- return c
-}
diff --git a/src/context/inbox.tsx b/src/context/inbox.tsx
index b05817ee..c95b4719 100644
--- a/src/context/inbox.tsx
+++ b/src/context/inbox.tsx
@@ -1,7 +1,7 @@
import type { Accessor, JSX } from 'solid-js'
-import { createContext, createSignal, useContext } from 'solid-js'
+import { createContext, createMemo, createSignal, useContext } from 'solid-js'
import { chatApiUrl } from '~/config'
-import { useGraphQL } from '~/context/graphql'
+import { graphqlClientCreate } from '~/graphql/client'
import createChatMutation from '~/graphql/mutation/chat/chat-create'
import createMessageMutation from '~/graphql/mutation/chat/chat-message-create'
import loadChatMessagesQuery from '~/graphql/query/chat/chat-messages-load-by'
@@ -10,6 +10,7 @@ import type { Chat, Message, MessagesBy, MutationCreate_MessageArgs } from '~/gr
import { Author } from '~/graphql/schema/core.gen'
import { useAuthors } from '../context/authors'
import { SSEMessage, useConnect } from './connect'
+import { useSession } from './session'
type InboxContextType = {
chats: Accessor
@@ -37,7 +38,8 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
const [chats, setChats] = createSignal([])
const [messages, setMessages] = createSignal([])
const { authorsSorted } = useAuthors()
- const { query, mutation } = useGraphQL(chatApiUrl)
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(chatApiUrl, session()?.access_token))
const handleMessage = (sseMessage: SSEMessage) => {
// handling all action types: create update delete join left seen
@@ -56,13 +58,13 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
addHandler(handleMessage)
const loadMessages = async (by: MessagesBy, limit = 50, offset = 0): Promise => {
- const resp = await query(loadChatMessagesQuery, { by, limit, offset })
+ const resp = await client()?.query(loadChatMessagesQuery, { by, limit, offset })
const result = resp?.data?.load_chat_messages || []
setMessages((mmm) => [...new Set([...mmm, ...result])])
return result
}
const loadChats = async () => {
- const resp = await query(loadChatsQuery, { limit: 50, offset: 0 }).toPromise()
+ const resp = await client()?.query(loadChatsQuery, { limit: 50, offset: 0 }).toPromise()
const result = resp?.data?.load_chats || []
setChats(result)
return result
@@ -81,7 +83,7 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
}
const sendMessage = async (args: MutationCreate_MessageArgs) => {
- const resp = await mutation(createMessageMutation, args).toPromise()
+ const resp = await client()?.mutation(createMessageMutation, args).toPromise()
const result = resp?.data?.create_message
if (result) {
const { message, error } = result
@@ -100,7 +102,7 @@ export const InboxProvider = (props: { children: JSX.Element }) => {
const createChat = async (members: number[], title: string) => {
try {
- const resp = await mutation(createChatMutation, { members, title }).toPromise()
+ const resp = await client()?.mutation(createChatMutation, { members, title }).toPromise()
const result = resp?.data?.create_chat
if (result) {
const { chat, error } = result
diff --git a/src/context/notifications.tsx b/src/context/notifications.tsx
index e989893f..591898f3 100644
--- a/src/context/notifications.tsx
+++ b/src/context/notifications.tsx
@@ -5,6 +5,8 @@ import { createContext, createMemo, createSignal, onMount, useContext } from 'so
import { createStore } from 'solid-js/store'
import { Portal } from 'solid-js/web'
+import { coreApiUrl } from '~/config'
+import { graphqlClientCreate } from '~/graphql/client'
import markSeenMutation from '~/graphql/mutation/notifier/mark-seen'
import markSeenAfterMutation from '~/graphql/mutation/notifier/mark-seen-after'
import markSeenThreadMutation from '~/graphql/mutation/notifier/mark-seen-thread'
@@ -13,7 +15,6 @@ import { NotificationGroup, QueryLoad_NotificationsArgs } from '~/graphql/schema
import { NotificationsPanel } from '../components/NotificationsPanel'
import { ShowIfAuthenticated } from '../components/_shared/ShowIfAuthenticated'
import { SSEMessage, useConnect } from './connect'
-import { useGraphQL } from './graphql'
import { useSession } from './session'
type NotificationsContextType = {
@@ -49,11 +50,11 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
const { session } = useSession()
const authorized = createMemo(() => Boolean(session()?.access_token))
const { addHandler } = useConnect()
- const { query, mutation } = useGraphQL()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
const loadNotificationsGrouped = async (options: QueryLoad_NotificationsArgs) => {
if (authorized()) {
- const resp = await query(getNotifications, options).toPromise()
+ const resp = await client()?.query(getNotifications, options).toPromise()
const result = resp?.data?.get_notifications
const groups = result?.notifications || []
const total = result?.total || 0
@@ -98,7 +99,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
})
const markSeenThread = async (threadId: string) => {
- await mutation(markSeenThreadMutation, { threadId }).toPromise()
+ await client()?.mutation(markSeenThreadMutation, { threadId }).toPromise()
const thread = notificationEntities[threadId]
thread.seen = true
setNotificationEntities((nnn) => ({ ...nnn, [threadId]: thread }))
@@ -107,14 +108,14 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
const markSeenAll = async () => {
if (authorized()) {
- const _resp = await mutation(markSeenAfterMutation, { after: after() }).toPromise()
+ const _resp = await client()?.mutation(markSeenAfterMutation, { after: after() }).toPromise()
await loadNotificationsGrouped({ after: after() || now, limit: loadedNotificationsCount() })
}
}
const markSeen = async (notification_id: number) => {
if (authorized()) {
- await mutation(markSeenMutation, { notification_id }).toPromise()
+ await client()?.mutation(markSeenMutation, { notification_id }).toPromise()
await loadNotificationsGrouped({ after: after() || now, limit: loadedNotificationsCount() })
}
}
diff --git a/src/context/profile.tsx b/src/context/profile.tsx
index 4c5b68b5..7446732a 100644
--- a/src/context/profile.tsx
+++ b/src/context/profile.tsx
@@ -1,11 +1,21 @@
import type { Author, ProfileInput } from '~/graphql/schema/core.gen'
import { AuthToken } from '@authorizerdev/authorizer-js'
-import { Accessor, JSX, createContext, createEffect, createSignal, on, useContext } from 'solid-js'
+import {
+ Accessor,
+ JSX,
+ createContext,
+ createEffect,
+ createMemo,
+ createSignal,
+ on,
+ useContext
+} from 'solid-js'
import { createStore } from 'solid-js/store'
+import { coreApiUrl } from '~/config'
import updateAuthorMuatation from '~/graphql/mutation/core/author-update'
+import { graphqlClientCreate } from '../graphql/client'
import { useAuthors } from './authors'
-import { useGraphQL } from './graphql'
import { useSession } from './session'
type ProfileContextType = {
@@ -32,7 +42,7 @@ const userpicUrl = (userpic: string) => {
export const ProfileProvider = (props: { children: JSX.Element }) => {
const { session } = useSession()
- const { mutation } = useGraphQL()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
const { addAuthor } = useAuthors()
const [form, setForm] = createStore({} as ProfileInput)
const [author, setAuthor] = createSignal({} as Author)
@@ -55,7 +65,7 @@ export const ProfileProvider = (props: { children: JSX.Element }) => {
)
const submit = async (profile: ProfileInput) => {
- const response = await mutation(updateAuthorMuatation, profile).toPromise()
+ const response = await client()?.mutation(updateAuthorMuatation, profile).toPromise()
if (response.error) {
console.error(response.error)
throw response.error
diff --git a/src/context/reactions.tsx b/src/context/reactions.tsx
index b31515f8..a0f4ce55 100644
--- a/src/context/reactions.tsx
+++ b/src/context/reactions.tsx
@@ -1,7 +1,8 @@
import type { JSX } from 'solid-js'
-import { createContext, onCleanup, useContext } from 'solid-js'
+import { createContext, createMemo, onCleanup, useContext } from 'solid-js'
import { createStore, reconcile } from 'solid-js/store'
+import { coreApiUrl } from '~/config'
import { loadReactions } from '~/graphql/api/public'
import createReactionMutation from '~/graphql/mutation/core/reaction-create'
import destroyReactionMutation from '~/graphql/mutation/core/reaction-destroy'
@@ -13,8 +14,9 @@ import {
Reaction,
ReactionKind
} from '~/graphql/schema/core.gen'
-import { useGraphQL } from './graphql'
+import { graphqlClientCreate } from '../graphql/client'
import { useLocalize } from './localize'
+import { useSession } from './session'
import { useSnackbar } from './ui'
type ReactionsContextType = {
@@ -38,7 +40,9 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
const [reactionsByShout, setReactionsByShout] = createStore>({})
const { t } = useLocalize()
const { showSnackbar } = useSnackbar()
- const { mutation } = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const addReactions = (rrr: Reaction[]) => {
const newReactionsByShout: Record = { ...reactionsByShout }
const newReactionEntities = rrr.reduce(
@@ -64,7 +68,7 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
}
const createReaction = async (input: MutationCreate_ReactionArgs): Promise => {
- const resp = await mutation(createReactionMutation, input).toPromise()
+ const resp = await client()?.mutation(createReactionMutation, input).toPromise()
const { error, reaction } = resp?.data?.create_reaction || {}
if (error) await showSnackbar({ type: 'error', body: t(error) })
if (!reaction) return
@@ -96,7 +100,7 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
reaction_id: number
): Promise<{ error: string; reaction?: string } | null> => {
if (reaction_id) {
- const resp = await mutation(destroyReactionMutation, { reaction_id }).toPromise()
+ const resp = await client()?.mutation(destroyReactionMutation, { reaction_id }).toPromise()
const result = resp?.data?.destroy_reaction
if (!result.error) {
setReactionEntities({
@@ -109,7 +113,7 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
}
const updateReaction = async (input: MutationUpdate_ReactionArgs): Promise => {
- const resp = await mutation(updateReactionMutation, input).toPromise()
+ const resp = await client()?.mutation(updateReactionMutation, input).toPromise()
const result = resp?.data?.update_reaction
if (!result) throw new Error('cannot update reaction')
const { error, reaction } = result
diff --git a/src/graphql/api/public.ts b/src/graphql/api/public.ts
index 02fc58e9..74cfea07 100644
--- a/src/graphql/api/public.ts
+++ b/src/graphql/api/public.ts
@@ -1,5 +1,5 @@
import { cache } from '@solidjs/router'
-import { defaultClient } from '~/context/graphql'
+import { defaultClient } from '~/graphql/client'
import getShoutQuery from '~/graphql/query/core/article-load'
import loadShoutsByQuery from '~/graphql/query/core/articles-load-by'
import loadShoutsSearchQuery from '~/graphql/query/core/articles-load-search'
diff --git a/src/graphql/client.ts b/src/graphql/client.ts
new file mode 100644
index 00000000..30b9ce1f
--- /dev/null
+++ b/src/graphql/client.ts
@@ -0,0 +1,24 @@
+import { Client, ClientOptions, cacheExchange, createClient, fetchExchange } from '@urql/core'
+import { coreApiUrl } from '~/config'
+
+// Функция для создания GraphQL клиента с заданным URL и токеном
+export const graphqlClientCreate = (url: string, token = ''): Client => {
+ const exchanges = [fetchExchange, cacheExchange]
+ const options: ClientOptions = {
+ url,
+ exchanges
+ }
+
+ if (token) {
+ options.fetchOptions = () => ({
+ headers: {
+ 'content-type': 'application/json',
+ authorization: token
+ }
+ })
+ }
+
+ return createClient(options)
+}
+
+export const defaultClient = graphqlClientCreate(coreApiUrl)
diff --git a/src/routes/[slug]/[...tab].tsx b/src/routes/[slug]/[...tab].tsx
index df403bd1..8f7ad784 100644
--- a/src/routes/[slug]/[...tab].tsx
+++ b/src/routes/[slug]/[...tab].tsx
@@ -16,6 +16,7 @@ import AuthorPage, { AuthorPageProps } from '../author/[slug]/[...tab]'
import TopicPage, { TopicPageProps } from '../topic/[slug]/[...tab]'
const fetchShout = async (slug: string): Promise => {
+ if (slug.startsWith('@')) return
const shoutLoader = getShout({ slug })
const result = await shoutLoader()
return result
diff --git a/src/routes/author/[slug]/[...tab].tsx b/src/routes/author/[slug]/[...tab].tsx
index 01f68af2..fc71c33e 100644
--- a/src/routes/author/[slug]/[...tab].tsx
+++ b/src/routes/author/[slug]/[...tab].tsx
@@ -1,8 +1,9 @@
import { RouteSectionProps } from '@solidjs/router'
-import { ErrorBoundary, createEffect, createMemo, createSignal, on } from 'solid-js'
+import { ErrorBoundary, Suspense, createEffect, createMemo, createSignal, on } from 'solid-js'
import { AuthorView } from '~/components/Views/Author'
import { FourOuFourView } from '~/components/Views/FourOuFour'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
+import { Loading } from '~/components/_shared/Loading'
import { PageLayout } from '~/components/_shared/PageLayout'
import { useAuthors } from '~/context/authors'
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
@@ -100,24 +101,26 @@ export default function AuthorPage(props: RouteSectionProps) {
return (
}>
-
-
-
-
-
-
-
+ }>
+
+
+
+
+
+
+
+
)
}
diff --git a/src/routes/edit/(drafts).tsx b/src/routes/edit/(drafts).tsx
index 9bb4ec40..3f8b4ca9 100644
--- a/src/routes/edit/(drafts).tsx
+++ b/src/routes/edit/(drafts).tsx
@@ -1,10 +1,13 @@
import { createAsync } from '@solidjs/router'
import { Client } from '@urql/core'
+import { createMemo } from 'solid-js'
import { AuthGuard } from '~/components/AuthGuard'
import { DraftsView } from '~/components/Views/DraftsView'
import { PageLayout } from '~/components/_shared/PageLayout'
-import { useGraphQL } from '~/context/graphql'
+import { coreApiUrl } from '~/config'
import { useLocalize } from '~/context/localize'
+import { useSession } from '~/context/session'
+import { graphqlClientCreate } from '~/graphql/client'
import getDraftsQuery from '~/graphql/query/core/articles-load-drafts'
import { Shout } from '~/graphql/schema/core.gen'
@@ -16,8 +19,9 @@ const fetchDrafts = async (client: Client) => {
export default () => {
const { t } = useLocalize()
- const client = useGraphQL()
- const drafts = createAsync(async () => await fetchDrafts(client))
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+ const drafts = createAsync(async () => await fetchDrafts(client()))
return (
diff --git a/src/routes/edit/[id]/(draft).tsx b/src/routes/edit/[id]/(draft).tsx
index d4ae45e8..46c22a5e 100644
--- a/src/routes/edit/[id]/(draft).tsx
+++ b/src/routes/edit/[id]/(draft).tsx
@@ -2,10 +2,11 @@ import { RouteSectionProps, redirect } from '@solidjs/router'
import { createEffect, createMemo, createSignal, lazy, on } from 'solid-js'
import { AuthGuard } from '~/components/AuthGuard'
import { PageLayout } from '~/components/_shared/PageLayout'
-import { useGraphQL } from '~/context/graphql'
+import { coreApiUrl } from '~/config'
import { useLocalize } from '~/context/localize'
import { useSession } from '~/context/session'
import { useSnackbar } from '~/context/ui'
+import { graphqlClientCreate } from '~/graphql/client'
import getShoutDraft from '~/graphql/query/core/article-my'
import { Shout } from '~/graphql/schema/core.gen'
import { LayoutType } from '~/types/common'
@@ -38,12 +39,12 @@ export default (props: RouteSectionProps) => {
redirect('/edit') // all drafts page
}
const [shout, setShout] = createSignal()
- const client = useGraphQL()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
createEffect(on(session, (s) => s?.access_token && loadDraft(), { defer: true }))
const loadDraft = async () => {
- const result = await client.query(getShoutDraft, { shout_id: props.params.id }).toPromise()
+ const result = await client()?.query(getShoutDraft, { shout_id: props.params.id }).toPromise()
if (result) {
const { shout: loadedShout, error } = result.data.get_my_shout
if (error) {
diff --git a/src/routes/edit/[id]/settings.tsx b/src/routes/edit/[id]/settings.tsx
index 4f0c4ee0..275a7858 100644
--- a/src/routes/edit/[id]/settings.tsx
+++ b/src/routes/edit/[id]/settings.tsx
@@ -1,22 +1,23 @@
import { RouteSectionProps } from '@solidjs/router'
-import { createEffect, createSignal, on } from 'solid-js'
+import { createEffect, createMemo, createSignal, on } from 'solid-js'
import { AuthGuard } from '~/components/AuthGuard'
import EditSettingsView from '~/components/Views/EditView/EditSettingsView'
import { PageLayout } from '~/components/_shared/PageLayout'
-import { useGraphQL } from '~/context/graphql'
+import { coreApiUrl } from '~/config'
import { useLocalize } from '~/context/localize'
import { useSession } from '~/context/session'
+import { graphqlClientCreate } from '~/graphql/client'
import getShoutDraft from '~/graphql/query/core/article-my'
import { Shout } from '~/graphql/schema/core.gen'
export default (props: RouteSectionProps) => {
const { t } = useLocalize()
- const client = useGraphQL()
const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
createEffect(on(session, (s) => s?.access_token && loadDraft(), { defer: true }))
const [shout, setShout] = createSignal()
const loadDraft = async () => {
- const result = await client.query(getShoutDraft, { shout_id: props.params.id }).toPromise()
+ const result = await client()?.query(getShoutDraft, { shout_id: props.params.id }).toPromise()
if (result) {
const { shout: loadedShout, error } = result.data.get_my_shout
if (error) throw new Error(error)
diff --git a/src/routes/edit/new.tsx b/src/routes/edit/new.tsx
index a0366544..5ae69fdb 100644
--- a/src/routes/edit/new.tsx
+++ b/src/routes/edit/new.tsx
@@ -1,25 +1,31 @@
import { useNavigate } from '@solidjs/router'
import { clsx } from 'clsx'
-import { For } from 'solid-js'
+import { For, createMemo } from 'solid-js'
import { AuthGuard } from '~/components/AuthGuard'
import { Button } from '~/components/_shared/Button'
import { Icon } from '~/components/_shared/Icon'
import { PageLayout } from '~/components/_shared/PageLayout'
-import { useGraphQL } from '~/context/graphql'
+import { coreApiUrl } from '~/config'
import { useLocalize } from '~/context/localize'
+import { useSession } from '~/context/session'
import { useSnackbar } from '~/context/ui'
+import { graphqlClientCreate } from '~/graphql/client'
import createShoutMutation from '~/graphql/mutation/core/article-create'
import styles from '~/styles/Create.module.scss'
import { LayoutType } from '~/types/common'
export default () => {
const { t } = useLocalize()
- const client = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
+
const { showSnackbar } = useSnackbar()
const navigate = useNavigate()
const handleCreate = async (layout: LayoutType) => {
console.debug('[routes : edit/new] handling create click...')
- const result = await client.mutation(createShoutMutation, { shout: { layout: layout } }).toPromise()
+ const result = await client()
+ ?.mutation(createShoutMutation, { shout: { layout: layout } })
+ .toPromise()
if (result) {
console.debug(result)
const { shout, error } = result.data.create_shout
diff --git a/src/routes/feed/my/[...mode]/[...order].tsx b/src/routes/feed/my/[...mode]/[...order].tsx
index f3883f88..1eb00be7 100644
--- a/src/routes/feed/my/[...mode]/[...order].tsx
+++ b/src/routes/feed/my/[...mode]/[...order].tsx
@@ -5,10 +5,11 @@ import { Feed } from '~/components/Views/Feed'
import { FeedProps } from '~/components/Views/Feed/Feed'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { PageLayout } from '~/components/_shared/PageLayout'
+import { coreApiUrl } from '~/config'
import { useFeed } from '~/context/feed'
-import { useGraphQL } from '~/context/graphql'
import { useLocalize } from '~/context/localize'
import { ReactionsProvider } from '~/context/reactions'
+import { useSession } from '~/context/session'
import { useTopics } from '~/context/topics'
import {
loadCoauthoredShouts,
@@ -16,6 +17,7 @@ import {
loadFollowedShouts,
loadUnratedShouts
} from '~/graphql/api/private'
+import { graphqlClientCreate } from '~/graphql/client'
import { LoadShoutsOptions, Shout, Topic } from '~/graphql/schema/core.gen'
const feeds = {
@@ -54,8 +56,8 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>)
const [searchParams] = useSearchParams() // ?period=month
const { t } = useLocalize()
const { setFeed, feed } = useFeed()
- // TODO: use const { requireAuthentication } = useSession()
- const client = useGraphQL()
+ const { session } = useSession()
+ const client = createMemo(() => graphqlClientCreate(coreApiUrl, session()?.access_token))
// preload all topics
const { addTopics, sortedTopics } = useTopics()
@@ -97,7 +99,7 @@ export default (props: RouteSectionProps<{ shouts: Shout[]; topics: Topic[] }>)
options.filters = { after: getFromDate(period as FeedPeriod) }
}
- const shoutsLoader = gqlHandler(client, options)
+ const shoutsLoader = gqlHandler(client(), options)
const loaded = await shoutsLoader()
loaded && setFeed((prev: Shout[]) => [...prev, ...loaded])
return loaded as LoadMoreItems