diff --git a/src/components/Nav/AuthModal/ChangePasswordForm.tsx b/src/components/Nav/AuthModal/ChangePasswordForm.tsx index f519e5d9..1f7f212b 100644 --- a/src/components/Nav/AuthModal/ChangePasswordForm.tsx +++ b/src/components/Nav/AuthModal/ChangePasswordForm.tsx @@ -10,6 +10,7 @@ import { hideModal } from '../../../stores/ui' import { PasswordField } from './PasswordField' import styles from './AuthModal.module.scss' +import { useSession } from '../../../context/session' type FormFields = { password: string @@ -18,28 +19,31 @@ type FormFields = { type ValidationErrors = Partial> export const ChangePasswordForm = () => { - const { changeSearchParams } = useRouter() + const { searchParams, changeSearchParams } = useRouter() const { t } = useLocalize() + const { + actions: { changePassword }, + } = useSession() const [isSubmitting, setIsSubmitting] = createSignal(false) const [validationErrors, setValidationErrors] = createSignal({}) const [newPassword, setNewPassword] = createSignal() const [passwordError, setPasswordError] = createSignal() const [isSuccess, setIsSuccess] = createSignal(false) - const authFormRef: { current: HTMLFormElement } = { current: null } const handleSubmit = async (event: Event) => { event.preventDefault() setIsSubmitting(true) - // Fake change password logic - console.log('!!! sent new password:', newPassword) - setTimeout(() => { - setIsSubmitting(false) - setIsSuccess(true) - }, 1000) + if (newPassword()) { + await changePassword(newPassword(), searchParams()?.token) + setTimeout(() => { + setIsSubmitting(false) + setIsSuccess(true) + }, 1000) + } } - const handlePasswordInput = (value) => { + const handlePasswordInput = (value: string) => { setNewPassword(value) if (passwordError()) { setValidationErrors((errors) => ({ ...errors, password: passwordError() })) @@ -93,6 +97,12 @@ export const ChangePasswordForm = () => {
{t('Password updated!')}
{t('You can now login using your new password')}
+ diff --git a/src/components/Nav/AuthModal/EmailConfirm.tsx b/src/components/Nav/AuthModal/EmailConfirm.tsx index 23b6633f..8012cc29 100644 --- a/src/components/Nav/AuthModal/EmailConfirm.tsx +++ b/src/components/Nav/AuthModal/EmailConfirm.tsx @@ -1,11 +1,10 @@ import type { ConfirmEmailSearchParams } from './types' import { clsx } from 'clsx' -import { createEffect, createMemo, createSignal, onMount, Show } from 'solid-js' +import { createEffect, createMemo, createSignal, Show } from 'solid-js' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' -import { ApiError } from '../../../graphql/error' import { useRouter } from '../../../stores/router' import { hideModal } from '../../../stores/ui' @@ -13,52 +12,21 @@ import styles from './AuthModal.module.scss' export const EmailConfirm = () => { const { t } = useLocalize() + const { searchParams } = useRouter() const { - actions: { confirmEmail, loadSession, loadAuthor }, + actions: { confirmEmail }, session, } = useSession() - const [confirmedEmail, setConfirmedEmail] = createSignal(false) + const [isTokenExpired, setIsTokenExpired] = createSignal(false) // TODO: handle expired token in context/session + const [isTokenInvalid, setIsTokenInvalid] = createSignal(false) // TODO: handle invalid token in context/session - const [isTokenExpired, setIsTokenExpired] = createSignal(false) - const [isTokenInvalid, setIsTokenInvalid] = createSignal(false) - const { searchParams, changeSearchParam } = useRouter() - - onMount(async () => { - const token = searchParams().access_token - if (token) { - changeSearchParam({}) - try { - await confirmEmail({ token }) - await loadSession() - await loadAuthor() - } catch (error) { - // TODO: adapt this code to authorizer - if (error instanceof ApiError) { - if (error.code === 'token_expired') { - setIsTokenExpired(true) - return - } - - if (error.code === 'token_invalid') { - setIsTokenInvalid(true) - return - } - } - - console.log(error) - } - } - }) - - createEffect(() => { - const confirmed = session()?.user?.email_verified - if (confirmed) { - console.debug(`[EmailConfirm] email successfully verified`) - setConfirmedEmail(confirmed) - } + createEffect(async () => { + const token = searchParams()?.access_token + if (token) await confirmEmail({ token }) }) const email = createMemo(() => session()?.user?.email) + const confirmedEmail = createMemo(() => session()?.user?.email_verified) return (
diff --git a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx index 3bed4e43..aec2dd26 100644 --- a/src/components/Nav/AuthModal/ForgotPasswordForm.tsx +++ b/src/components/Nav/AuthModal/ForgotPasswordForm.tsx @@ -5,7 +5,7 @@ import { createSignal, JSX, Show } from 'solid-js' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' -import { ApiError } from '../../../graphql/error' +// import { ApiError } from '../../../graphql/error' import { useRouter } from '../../../stores/router' import { validateEmail } from '../../../utils/validateEmail' @@ -33,17 +33,13 @@ export const ForgotPasswordForm = () => { const [isSubmitting, setIsSubmitting] = createSignal(false) const [validationErrors, setValidationErrors] = createSignal({}) const [isUserNotFount, setIsUserNotFound] = createSignal(false) - const authFormRef: { current: HTMLFormElement } = { current: null } - const [message, setMessage] = createSignal('') const handleSubmit = async (event: Event) => { event.preventDefault() - setSubmitError('') setIsUserNotFound(false) - const newValidationErrors: ValidationErrors = {} if (!email()) { @@ -53,7 +49,6 @@ export const ForgotPasswordForm = () => { } setValidationErrors(newValidationErrors) - const isValid = Object.keys(newValidationErrors).length === 0 if (!isValid) { @@ -68,18 +63,17 @@ export const ForgotPasswordForm = () => { try { const response = await authorizer().forgotPassword({ email: email(), - redirect_uri: window.location.href + '&success=1', // FIXME: redirect to success page accepting confirmation code + redirect_uri: window.location.origin, }) - if (response) { - console.debug('[ForgotPasswordForm]', response) - if (response.message) setMessage(response.message) - } + console.debug('[ForgotPasswordForm] authorizer response: ', response) + if (response && response.message) setMessage(response.message) } catch (error) { - if (error instanceof ApiError && error.code === 'user_not_found') { + console.error(error) + if (error?.code === 'user_not_found') { setIsUserNotFound(true) return } - setSubmitError(error.message) + setSubmitError(error?.message) } finally { setIsSubmitting(false) } @@ -92,17 +86,17 @@ export const ForgotPasswordForm = () => { ref={(el) => (authFormRef.current = el)} >
-

{t('Forgot password?')}

+

{t('Restore password')}

{t(message()) || t('Everything is ok, please give us your email address')}
-
{
-
diff --git a/src/components/Nav/AuthModal/LoginForm.tsx b/src/components/Nav/AuthModal/LoginForm.tsx index dfa1959a..9238d0dc 100644 --- a/src/components/Nav/AuthModal/LoginForm.tsx +++ b/src/components/Nav/AuthModal/LoginForm.tsx @@ -17,6 +17,7 @@ import { email, setEmail } from './sharedLogic' import { SocialProviders } from './SocialProviders' import styles from './AuthModal.module.scss' +import { VerifyEmailInput } from '@authorizerdev/authorizer-js' type FormFields = { email: string @@ -65,11 +66,12 @@ export const LoginForm = () => { setIsLinkSent(true) setIsEmailNotConfirmed(false) setSubmitError('') - const { - actions: { authorizer, getToken }, - } = useSession() - const result = await authorizer().verifyEmail({ token: getToken() }) - if (!result) setSubmitError('cant sign send link') // TODO: + changeSearchParams({ mode: 'forgot-password' }) // NOTE: temporary solition + /* FIXME: + const { actions: { authorizer } } = useSession() + const result = await authorizer().verifyEmail({ token }) + if (!result) setSubmitError('cant sign send link') + */ } const handleSubmit = async (event: Event) => { @@ -110,6 +112,7 @@ export const LoginForm = () => { showSnackbar({ body: t('Welcome!') }) } catch (error) { + console.error(error) if (error instanceof ApiError) { if (error.code === 'email_not_confirmed') { setSubmitError(t('Please, confirm email')) @@ -184,16 +187,6 @@ export const LoginForm = () => { > {t('Forgot password?')} - - changeSearchParams({ - mode: 'change-password', - }) - } - > - {t('Change password')} -
diff --git a/src/components/Nav/AuthModal/RegisterForm.tsx b/src/components/Nav/AuthModal/RegisterForm.tsx index 85a0a599..8ef615e8 100644 --- a/src/components/Nav/AuthModal/RegisterForm.tsx +++ b/src/components/Nav/AuthModal/RegisterForm.tsx @@ -6,7 +6,7 @@ import { Show, createSignal } from 'solid-js' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' -import { ApiError } from '../../../graphql/error' +// import { ApiError } from '../../../graphql/error' import { checkEmail, useEmailChecks } from '../../../stores/emailChecks' import { useRouter } from '../../../stores/router' import { hideModal } from '../../../stores/ui' @@ -36,7 +36,7 @@ export const RegisterForm = () => { const { t } = useLocalize() const { emailChecks } = useEmailChecks() const { - actions: { authorizer }, + actions: { signUp }, } = useSession() const [submitError, setSubmitError] = createSignal('') const [fullName, setFullName] = createSignal('') @@ -105,19 +105,20 @@ export const RegisterForm = () => { } setIsSubmitting(true) - try { - await authorizer().signup({ + const opts = { given_name: cleanName, email: cleanEmail, password: password(), confirm_password: password(), redirect_uri: window.location.origin, - }) + } + await signUp(opts) setIsSuccess(true) } catch (error) { - if (error instanceof ApiError && error.code === 'user_already_exists') { + console.error(error) + if (error?.code === 'user_already_exists') { return } diff --git a/src/components/Nav/AuthModal/types.ts b/src/components/Nav/AuthModal/types.ts index 9dc54347..92efe4ea 100644 --- a/src/components/Nav/AuthModal/types.ts +++ b/src/components/Nav/AuthModal/types.ts @@ -11,6 +11,7 @@ export type AuthModalSource = export type AuthModalSearchParams = { mode: AuthModalMode source?: AuthModalSource + token?: string } export type ConfirmEmailSearchParams = { diff --git a/src/components/Nav/Confirmed.scss b/src/components/Nav/Confirmed.scss deleted file mode 100644 index 4307f056..00000000 --- a/src/components/Nav/Confirmed.scss +++ /dev/null @@ -1,6 +0,0 @@ -.center { - display: flex; - justify-content: center; - align-items: center; - height: 420px; -} diff --git a/src/components/Nav/Confirmed.tsx b/src/components/Nav/Confirmed.tsx deleted file mode 100644 index fe615284..00000000 --- a/src/components/Nav/Confirmed.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import './Confirmed.scss' -import { onMount } from 'solid-js' - -import { useLocalize } from '../../context/localize' - -export const Confirmed = (props: { token?: string }) => { - const { t } = useLocalize() - onMount(() => { - const token = props.token ?? document.cookie.split(';').at(0).replace('token=', '') - window.addEventListener('mousemove', () => window.close()) - window.addEventListener('keydown', () => window.close()) - window.addEventListener('click', () => window.close()) - localStorage.setItem('token', token) - }) - return ( - <> -
{t('You was successfully authorized')}
- - ) -} diff --git a/src/components/Nav/HeaderAuth.tsx b/src/components/Nav/HeaderAuth.tsx index bba8c902..8f2a9372 100644 --- a/src/components/Nav/HeaderAuth.tsx +++ b/src/components/Nav/HeaderAuth.tsx @@ -32,7 +32,7 @@ const MD_WIDTH_BREAKPOINT = 992 export const HeaderAuth = (props: Props) => { const { t } = useLocalize() const { page } = useRouter() - const { author, isAuthenticated, isSessionLoaded } = useSession() + const { session, author, isAuthenticated, isSessionLoaded } = useSession() const { unreadNotificationsCount, actions: { showNotificationsPanel }, @@ -139,12 +139,12 @@ export const HeaderAuth = (props: Props) => {
diff --git a/src/components/Views/Home.tsx b/src/components/Views/Home.tsx index b790385c..11effc2a 100644 --- a/src/components/Views/Home.tsx +++ b/src/components/Views/Home.tsx @@ -67,10 +67,11 @@ export const HomeView = (props: Props) => { setIsLoadMoreButtonVisible(hasMore) } - const { topic, shouts } = await apiClient.getRandomTopicShouts(RANDOM_TOPIC_SHOUTS_COUNT) + const result = await apiClient.getRandomTopicShouts(RANDOM_TOPIC_SHOUTS_COUNT) + if (!result) console.warn('[apiClient.getRandomTopicShouts] failed') batch(() => { - setRandomTopic(topic) - setRandomTopicArticles(shouts) + if (result?.topic) setRandomTopic(result.topic) + if (result?.shouts) setRandomTopicArticles(result.shouts) }) }) diff --git a/src/components/_shared/Image/Image.tsx b/src/components/_shared/Image/Image.tsx index 197a5367..75542cc5 100644 --- a/src/components/_shared/Image/Image.tsx +++ b/src/components/_shared/Image/Image.tsx @@ -1,7 +1,7 @@ import type { JSX } from 'solid-js' import { Link } from '@solidjs/meta' -import { splitProps } from 'solid-js' +import { createEffect, splitProps } from 'solid-js' import { getImageUrl } from '../../../utils/getImageUrl' @@ -21,10 +21,9 @@ export const Image = (props: Props) => { `${getImageUrl(local.src, { width: others.width * pixelDensity })} ${pixelDensity}x`, ) .join(', ') - return ( <> - + {local.alt} ) diff --git a/src/components/_shared/ShowIfAuthenticated.tsx b/src/components/_shared/ShowIfAuthenticated.tsx index a5a934b8..c3ea5c20 100644 --- a/src/components/_shared/ShowIfAuthenticated.tsx +++ b/src/components/_shared/ShowIfAuthenticated.tsx @@ -10,10 +10,10 @@ type ShowIfAuthenticatedProps = { } export const ShowIfAuthenticated = (props: ShowIfAuthenticatedProps) => { - const { isAuthenticated } = useSession() + const { author } = useSession() return ( - + {props.children} ) diff --git a/src/context/connect.tsx b/src/context/connect.tsx index 0695b5a0..5999cd9d 100644 --- a/src/context/connect.tsx +++ b/src/context/connect.tsx @@ -9,8 +9,8 @@ const RECONNECT_TIMES = 2 export interface SSEMessage { id: string - entity: string - action: string + entity: string // follower | shout | reaction + action: string // create | delete | update | join | follow | seen payload: any // Author | Shout | Reaction | Message created_at?: number // unixtime x1000 seen?: boolean @@ -29,9 +29,7 @@ export const ConnectProvider = (props: { children: JSX.Element }) => { const [messageHandlers, setHandlers] = createSignal>([]) // const [messages, setMessages] = createSignal>([]); const [connected, setConnected] = createSignal(false) - const { - actions: { getToken }, - } = useSession() + const { session } = useSession() const addHandler = (handler: MessageHandler) => { setHandlers((hhh) => [...hhh, handler]) @@ -39,8 +37,9 @@ export const ConnectProvider = (props: { children: JSX.Element }) => { const [retried, setRetried] = createSignal(0) createEffect(async () => { - const token = getToken() + const token = session()?.access_token if (token && !connected()) { + console.info('[context.connect] init SSE connection') await fetchEventSource('https://connect.discours.io', { method: 'GET', headers: { diff --git a/src/context/session.tsx b/src/context/session.tsx index 1af9fb7c..06358997 100644 --- a/src/context/session.tsx +++ b/src/context/session.tsx @@ -8,6 +8,7 @@ import { AuthToken, Authorizer, ConfigType, + SignupInput, } from '@authorizerdev/authorizer-js' import { createContext, @@ -29,9 +30,9 @@ import { showModal } from '../stores/ui' import { useLocalize } from './localize' import { useSnackbar } from './snackbar' -const config: ConfigType = { +const defaultConfig: ConfigType = { authorizerURL: 'https://auth.discours.io', - redirectURL: 'https://discoursio-webapp.vercel.app/?modal=auth', + redirectURL: 'https://discoursio-webapp.vercel.app', clientID: '9c113377-5eea-4c89-98e1-69302462fc08', // FIXME: use env? } @@ -41,10 +42,9 @@ export type SessionContextType = { author: Resource isSessionLoaded: Accessor subscriptions: Accessor - isAuthenticated: Accessor isAuthWithCallback: Accessor<() => void> + isAuthenticated: Accessor actions: { - getToken: () => string loadSession: () => AuthToken | Promise setSession: (token: AuthToken | null) => void // setSession loadAuthor: (info?: unknown) => Author | Promise @@ -54,9 +54,11 @@ export type SessionContextType = { callback: (() => Promise) | (() => void), modalSource: AuthModalSource, ) => void + signUp: (params: SignupInput) => Promise signIn: (params: LoginInput) => Promise signOut: () => Promise - confirmEmail: (input: VerifyEmailInput) => Promise // email confirm callback is in auth.discours.io + changePassword: (password: string, token: string) => void + confirmEmail: (input: VerifyEmailInput) => Promise // email confirm callback is in auth.discours.io setIsSessionLoaded: (loaded: boolean) => void authorizer: () => Authorizer } @@ -81,75 +83,40 @@ export const SessionProvider = (props: { const { actions: { showSnackbar }, } = useSnackbar() - const { searchParams, changeSearchParam } = useRouter() - const [isSessionLoaded, setIsSessionLoaded] = createSignal(false) - const [subscriptions, setSubscriptions] = createSignal(EMPTY_SUBSCRIPTIONS) + const { searchParams, changeSearchParams } = useRouter() - const getSession = async (): Promise => { - try { - const tkn = getToken() - // console.debug('[context.session] token before:', tkn) - const authResult = await authorizer().getSession({ - Authorization: tkn, + // handle callback's redirect_uri + createEffect(async () => { + // TODO: handle oauth here too + const token = searchParams()?.token + const access_token = searchParams()?.access_token + if (token) { + changeSearchParams({ + mode: 'change-password', + modal: 'auth', + }) + } else if (access_token) { + changeSearchParams({ + mode: 'confirm-email', + modal: 'auth', }) - if (authResult?.access_token) { - setSession(authResult) - // console.debug('[context.session] token after:', authResult.access_token) - await loadSubscriptions() - return authResult - } - } catch (error) { - console.error('[context.session] getSession error:', error) - setSession(null) - return null - } finally { - setTimeout(() => { - setIsSessionLoaded(true) - }, 0) - } - } - - const [session, { refetch: loadSession, mutate: setSession }] = createResource(getSession, { - ssrLoadFrom: 'initial', - initialValue: null, - }) - - createEffect(() => { - // detect confirm redirect - const params = searchParams() - if (params?.access_token) { - console.debug('[context.session] access token presented, changing search params') - changeSearchParam({ modal: 'auth', mode: 'confirm-email', access_token: params?.access_token }) } }) - createEffect(() => { - const token = getToken() - if (!inboxClient.private && token) { - apiClient.connect(token) - notifierClient.connect(token) - inboxClient.connect(token) - } - }) + // load - const loadSubscriptions = async (): Promise => { - if (apiClient.private) { - const result = await apiClient.getMySubscriptions() - if (result) { - setSubscriptions(result) - } else { - setSubscriptions(EMPTY_SUBSCRIPTIONS) - } - } - } - - const [author, { refetch: loadAuthor, mutate: setAuthor }] = createResource( + const [configuration, setConfig] = createSignal(defaultConfig) + const authorizer = createMemo(() => new Authorizer(defaultConfig)) + const [isSessionLoaded, setIsSessionLoaded] = createSignal(false) + const [session, { refetch: loadSession, mutate: setSession }] = createResource( async () => { - const u = session()?.user - if (u) { - return (await apiClient.getAuthorId({ user: u.id })) ?? null + try { + console.info('[context.session] loading session') + return await authorizer().getSession() + } catch (_) { + console.info('[context.session] cannot refresh session') + return null } - return null }, { ssrLoadFrom: 'initial', @@ -157,29 +124,71 @@ export const SessionProvider = (props: { }, ) - const isAuthenticated = createMemo(() => Boolean(session()?.user)) - - const signIn = async (params: LoginInput) => { - const authResult: AuthToken | void = await authorizer().login(params) - - if (authResult && authResult.access_token) { - setSession(authResult) - await loadSubscriptions() - console.debug('[context.session] signed in') - } else { - console.info((authResult as AuthToken).message) - } - } - - const authorizer = createMemo( - () => - new Authorizer({ - authorizerURL: config.authorizerURL, - redirectURL: config.redirectURL, - clientID: config.clientID, - }), + const [author, { refetch: loadAuthor, mutate: setAuthor }] = createResource( + async () => { + const u = session()?.user + return u ? (await apiClient.getAuthorId({ user: u.id })) || null : null + }, + { + ssrLoadFrom: 'initial', + initialValue: null, + }, ) + const [subscriptions, setSubscriptions] = createSignal(EMPTY_SUBSCRIPTIONS) + const loadSubscriptions = async (): Promise => { + const result = await apiClient.getMySubscriptions() + setSubscriptions(result || EMPTY_SUBSCRIPTIONS) + } + + // session postload effect + createEffect(async () => { + if (session()) { + const token = session()?.access_token + if (token) { + console.log('[context.session] token observer got token', token) + if (!inboxClient.private) { + apiClient.connect(token) + notifierClient.connect(token) + inboxClient.connect(token) + } + if (!author()) { + const a = await loadAuthor() + await loadSubscriptions() + if (a) { + console.log('[context.session] author profile and subs loaded', author()) + } else { + console.warn('[context.session] author is not loaded') + } + setIsSessionLoaded(true) + } + } + } else { + console.log('[context.session] setting session null') + if (session() === null && author() !== null) { + setIsSessionLoaded(true) + setAuthor(null) + setSubscriptions(EMPTY_SUBSCRIPTIONS) + } + } + }) + + // initial effect + onMount(async () => { + const metaRes = await authorizer().getMetaData() + setConfig({ ...defaultConfig, ...metaRes, redirectURL: window.location.origin }) + let s + try { + s = await loadSession() + } catch (e) {} + if (!s) { + setIsSessionLoaded(true) + setSession(null) + setAuthor(null) + } + }) + + // callback state updater createEffect( on( () => props.onStateChangeCallback, @@ -190,56 +199,57 @@ export const SessionProvider = (props: { ), ) - const [configuration, setConfig] = createSignal(config) - - onMount(async () => { - setIsSessionLoaded(false) - console.log('[context.session] loading...') - const metaRes = await authorizer().getMetaData() - setConfig({ ...config, ...metaRes, redirectURL: window.location.origin + '/?modal=auth' }) - console.log('[context.session] refreshing session...') - const s = await getSession() - console.debug('[context.session] session:', s) - console.log('[context.session] loading author...') - const a = await loadAuthor() - console.debug('[context.session] author:', a) - setIsSessionLoaded(true) - console.log('[context.session] loaded') - }) - + // require auth wrapper const [isAuthWithCallback, setIsAuthWithCallback] = createSignal<() => void>() const requireAuthentication = async (callback: () => void, modalSource: AuthModalSource) => { setIsAuthWithCallback(() => callback) - const userdata = await authorizer().getProfile() - if (userdata) setSession({ ...session(), user: userdata }) + await loadSession() - if (!isAuthenticated()) { + if (!session()) { showModal('auth', modalSource) } } + // authorizer api proxy methods + + const signUp = async (params) => { + const authResult: void | AuthToken = await authorizer().signup({ + ...params, + confirm_password: params.password, + }) + if (authResult) setSession(authResult) + } + + const signIn = async (params: LoginInput) => { + const authResult: AuthToken | void = await authorizer().login(params) + if (authResult) setSession(authResult) + } + const signOut = async () => { await authorizer().logout() setSession(null) - setSubscriptions(EMPTY_SUBSCRIPTIONS) showSnackbar({ body: t("You've successfully logged out") }) } + const changePassword = async (password: string, token: string) => { + const resp = await authorizer().resetPassword({ password, token, confirm_password: password }) + console.debug('[context.session] change password response:', resp) + } + const confirmEmail = async (input: VerifyEmailInput) => { console.debug(`[context.session] calling authorizer's verify email with`, input) const at: void | AuthToken = await authorizer().verifyEmail(input) if (at) setSession(at) - console.log(`[context.session] confirmEmail got result ${at}`) + return at } - const getToken = createMemo(() => session()?.access_token) - + const isAuthenticated = createMemo(() => Boolean(author())) const actions = { - getToken, loadSession, loadSubscriptions, requireAuthentication, + signUp, signIn, signOut, confirmEmail, @@ -248,16 +258,17 @@ export const SessionProvider = (props: { setAuthor, authorizer, loadAuthor, + changePassword, } const value: SessionContextType = { config: configuration(), session, subscriptions, isSessionLoaded, - isAuthenticated, author, actions, isAuthWithCallback, + isAuthenticated, } return {props.children} diff --git a/src/graphql/client/core.ts b/src/graphql/client/core.ts index e4432004..9b0d84b5 100644 --- a/src/graphql/client/core.ts +++ b/src/graphql/client/core.ts @@ -53,39 +53,28 @@ export const apiClient = { getRandomTopShouts: async (params: QueryLoad_Shouts_Random_TopArgs) => { const response = await publicGraphQLClient.query(loadShoutsTopRandom, params).toPromise() - if (!response.data) { - console.error('[graphql.core] getRandomTopShouts error', response.error) - } + if (!response.data) console.error('[graphql.core] load_shouts_random_top failed', response) + return response.data.load_shouts_random_top }, getUnratedShouts: async (limit = 50, offset = 0) => { const response = await apiClient.private.query(loadShoutsUnrated, { limit, offset }).toPromise() - - if (!response.data) { - console.error('[graphql.core] getUnratedShouts error', response.error) - } + if (!response.data) console.error('[graphql.core] load_shouts_unrated', response) return response.data.load_shouts_unrated }, getRandomTopics: async ({ amount }: { amount: number }) => { const response = await publicGraphQLClient.query(topicsRandomQuery, { amount }).toPromise() - - if (!response.data) { - console.error('[graphql.client.core] getRandomTopics', response.error) - } + if (!response.data) console.error('[graphql.client.core] get_topics_random failed', response) return response.data.get_topics_random }, getRandomTopicShouts: async (limit: number): Promise<{ topic: Topic; shouts: Shout[] }> => { const resp = await publicGraphQLClient.query(articlesLoadRandomTopic, { limit }).toPromise() - - if (resp.error) { - console.error(resp) - } - + if (!resp.data) console.error('[graphql.client.core] load_shouts_random_topic', resp) return resp.data.load_random_topics_shouts }, @@ -100,16 +89,14 @@ export const apiClient = { getAllTopics: async () => { const response = await publicGraphQLClient.query(topicsAll, {}).toPromise() - if (response.error) { - console.debug('[graphql.client.core] get_topics_all', response.error) - } + if (!response.data) console.error('[graphql.client.core] get_topics_all', response) + return response.data.get_topics_all }, getAllAuthors: async (limit: number = 50, offset: number = 0) => { const response = await publicGraphQLClient.query(authorsAll, { limit, offset }).toPromise() - if (response.error) { - console.debug('[graphql.client.core] load_authors_all', response.error) - } + if (!response.data) console.error('[graphql.client.core] load_authors_all', response) + return response.data.load_authors_all }, getAuthor: async (params: { slug?: string; author_id?: number }): Promise => { diff --git a/src/graphql/query/core/articles-load-random-top.ts b/src/graphql/query/core/articles-load-random-top.ts index 24bb164c..e103ea28 100644 --- a/src/graphql/query/core/articles-load-random-top.ts +++ b/src/graphql/query/core/articles-load-random-top.ts @@ -1,8 +1,8 @@ import { gql } from '@urql/core' export default gql` - query LoadRandomTopShoutsQuery($params: LoadRandomTopShoutsParams) { - load_shouts_random_top(params: $params) { + query LoadRandomTopShoutsQuery($options: LoadShoutsOptions) { + load_shouts_random_top(options: $options) { id title # lead