webapp/src/context/following.tsx

203 lines
6.1 KiB
TypeScript
Raw Normal View History

2024-06-24 17:50:27 +00:00
import {
Accessor,
JSX,
createContext,
createEffect,
createMemo,
createSignal,
on,
2024-06-26 08:22:05 +00:00
useContext
2024-06-24 17:50:27 +00:00
} from 'solid-js'
2024-01-31 12:34:15 +00:00
import { createStore } from 'solid-js/store'
2024-06-24 17:50:27 +00:00
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'
2024-06-24 17:50:27 +00:00
import { useGraphQL } from './graphql'
2024-01-31 12:34:15 +00:00
import { useSession } from './session'
2024-06-24 17:50:27 +00:00
export type FollowsFilter = 'all' | 'authors' | 'topics' | 'communities'
2024-05-20 11:16:54 +00:00
type FollowingData = { slug: string; type: 'follow' | 'unfollow' }
2024-03-11 05:20:00 +00:00
2024-01-31 12:34:15 +00:00
interface FollowingContextType {
loading: Accessor<boolean>
2024-05-05 16:13:48 +00:00
followers: Accessor<Author[]>
2024-05-20 11:16:54 +00:00
setFollows: (follows: AuthorFollowsResult) => void
2024-06-24 17:50:27 +00:00
following: Accessor<FollowingData | undefined>
2024-05-20 11:16:54 +00:00
changeFollowing: (what: FollowingEntity, slug: string, value: boolean) => void
follows: AuthorFollowsResult
loadFollows: () => void
2024-01-31 12:34:15 +00:00
follow: (what: FollowingEntity, slug: string) => Promise<void>
unfollow: (what: FollowingEntity, slug: string) => Promise<void>
}
2024-06-24 17:50:27 +00:00
const FollowingContext = createContext<FollowingContextType>({} as FollowingContextType)
2024-01-31 12:34:15 +00:00
export function useFollowing() {
return useContext(FollowingContext)
}
2024-06-24 17:50:27 +00:00
interface AuthorFollowsResult {
authors?: Author[]
topics?: Topic[]
communities?: Community[]
}
2024-03-29 09:58:32 +00:00
const EMPTY_SUBSCRIPTIONS: AuthorFollowsResult = {
2024-06-24 17:50:27 +00:00
topics: [] as Topic[],
authors: [] as Author[],
2024-06-26 08:22:05 +00:00
communities: [] as Community[]
2024-01-31 12:34:15 +00:00
}
2024-06-24 17:50:27 +00:00
const defaultFollowing = { slug: '', type: 'follow' } as FollowingData
2024-01-31 12:34:15 +00:00
export const FollowingProvider = (props: { children: JSX.Element }) => {
const [loading, setLoading] = createSignal<boolean>(false)
2024-06-24 17:50:27 +00:00
const [followers, setFollowers] = createSignal<Author[]>([] as Author[])
2024-05-20 11:16:54 +00:00
const [follows, setFollows] = createStore<AuthorFollowsResult>(EMPTY_SUBSCRIPTIONS)
2024-06-24 17:50:27 +00:00
const { session } = useSession()
const authorized = createMemo<boolean>(() => Boolean(session()?.access_token))
const { query, mutation } = useGraphQL()
2024-01-31 12:34:15 +00:00
const fetchData = async () => {
setLoading(true)
try {
2024-06-24 17:50:27 +00:00
if (session()?.access_token) {
2024-01-31 12:34:15 +00:00
console.debug('[context.following] fetching subs data...')
2024-06-24 17:50:27 +00:00
const result = await query(loadAuthorFollowers, { user: session()?.user?.id }).toPromise()
if (result) {
setFollows((_: AuthorFollowsResult) => {
return { ...EMPTY_SUBSCRIPTIONS, ...result } as AuthorFollowsResult
})
}
2024-01-31 12:34:15 +00:00
}
} catch (error) {
2024-05-30 18:33:54 +00:00
console.warn('[context.following] cannot get subs', error)
2024-01-31 12:34:15 +00:00
} finally {
setLoading(false)
}
}
2024-06-24 17:50:27 +00:00
const [following, setFollowing] = createSignal<FollowingData>(defaultFollowing)
2024-01-31 12:34:15 +00:00
const follow = async (what: FollowingEntity, slug: string) => {
2024-06-24 17:50:27 +00:00
if (!authorized()) return
2024-05-20 11:16:54 +00:00
setFollowing({ slug, type: 'follow' })
2024-01-31 12:34:15 +00:00
try {
2024-06-24 17:50:27 +00:00
const resp = await mutation(followMutation, { what, slug }).toPromise()
const result = resp?.data?.follow
if (!result) return
2024-05-20 11:16:54 +00:00
setFollows((subs) => {
if (result.authors) subs['authors'] = result.authors || []
if (result.topics) subs['topics'] = result.topics || []
return subs
2024-03-15 14:57:03 +00:00
})
2024-01-31 12:34:15 +00:00
} catch (error) {
2024-03-15 14:57:03 +00:00
console.error(error)
2024-03-11 05:20:00 +00:00
} finally {
2024-06-24 17:50:27 +00:00
setFollowing(defaultFollowing)
2024-01-31 12:34:15 +00:00
}
}
const unfollow = async (what: FollowingEntity, slug: string) => {
2024-06-24 17:50:27 +00:00
if (!authorized()) return
2024-05-20 11:16:54 +00:00
setFollowing({ slug: slug, type: 'unfollow' })
2024-01-31 12:34:15 +00:00
try {
2024-06-24 17:50:27 +00:00
const resp = await mutation(unfollowMutation, { what, slug }).toPromise()
const result = resp?.data?.unfollow
if (!result) return
2024-05-20 11:16:54 +00:00
setFollows((subs) => {
if (result.authors) subs['authors'] = result.authors || []
if (result.topics) subs['topics'] = result.topics || []
return subs
})
2024-01-31 12:34:15 +00:00
} catch (error) {
console.error(error)
2024-03-11 05:20:00 +00:00
} finally {
2024-06-24 17:50:27 +00:00
setFollowing(defaultFollowing)
2024-01-31 12:34:15 +00:00
}
}
2024-05-20 11:16:54 +00:00
createEffect(
on(
2024-06-24 17:50:27 +00:00
() => session?.()?.user?.app_data,
2024-05-20 23:15:52 +00:00
(appdata) => {
if (appdata) {
const { authors, followers, topics } = appdata
setFollows({ authors, topics })
setFollowers(followers)
if (!authors) fetchData()
2024-04-17 15:47:24 +00:00
}
2024-06-26 08:22:05 +00:00
}
)
2024-05-20 11:16:54 +00:00
)
2024-01-31 12:34:15 +00:00
2024-05-20 11:16:54 +00:00
const changeFollowing = (what: FollowingEntity, slug: string, value = true) => {
2024-06-24 17:50:27 +00:00
setFollows((prevFollows: AuthorFollowsResult) => {
const updatedFollows = { ...prevFollows }
switch (what) {
case 'AUTHOR': {
if (value) {
if (!updatedFollows.authors?.some((author) => author.slug === slug)) {
updatedFollows.authors = [...(updatedFollows.authors || []), { slug } as Author]
}
} else {
updatedFollows.authors = updatedFollows.authors?.filter((author) => author.slug !== slug) || []
}
break
}
case 'TOPIC': {
if (value) {
if (!updatedFollows.topics?.some((topic) => topic.slug === slug)) {
updatedFollows.topics = [...(updatedFollows.topics || []), { slug } as Topic]
}
} else {
updatedFollows.topics = updatedFollows.topics?.filter((topic) => topic.slug !== slug) || []
}
break
}
case 'COMMUNITY': {
if (value) {
if (!updatedFollows.communities?.some((community) => community.slug === slug)) {
updatedFollows.communities = [...(updatedFollows.communities || []), { slug } as Community]
}
} else {
updatedFollows.communities =
updatedFollows.communities?.filter((community) => community.slug !== slug) || []
}
break
}
2024-01-31 12:34:15 +00:00
}
2024-06-24 17:50:27 +00:00
return updatedFollows
2024-01-31 12:34:15 +00:00
})
2024-06-24 17:50:27 +00:00
2024-01-31 12:34:15 +00:00
try {
2024-06-24 17:50:27 +00:00
if (value) {
follow(what, slug)
} else {
unfollow(what, slug)
}
2024-01-31 12:34:15 +00:00
} catch (error) {
console.error(error)
} finally {
fetchData()
}
}
const value: FollowingContextType = {
loading,
2024-05-20 11:16:54 +00:00
follows,
setFollows,
following,
changeFollowing,
2024-04-08 12:49:40 +00:00
followers,
2024-05-20 11:16:54 +00:00
loadFollows: fetchData,
2024-01-31 12:34:15 +00:00
follow,
2024-06-26 08:22:05 +00:00
unfollow
2024-01-31 12:34:15 +00:00
}
return <FollowingContext.Provider value={value}>{props.children}</FollowingContext.Provider>
}