auth-graphql-removed

This commit is contained in:
Untone 2023-11-28 18:36:00 +03:00
parent fecce70aaa
commit a703b85bd8
28 changed files with 113 additions and 410 deletions

View File

@ -36,17 +36,17 @@ generates:
useTypeImports: true
outputPath: './src/graphql/types/notifier.gen.ts'
# Generate types for auth
src/graphql/schema/auth.gen.ts:
schema: 'https://auth.discours.io/graphql'
plugins:
- 'typescript'
- 'typescript-operations'
- 'typescript-urql'
config:
skipTypename: true
useTypeImports: true
outputPath: './src/graphql/types/auth.gen.ts'
# internal types for auth
# src/graphql/schema/auth.gen.ts:
# schema: 'https://auth.discours.io/graphql'
# plugins:
# - 'typescript'
# - 'typescript-operations'
# - 'typescript-urql'
# config:
# skipTypename: true
# useTypeImports: true
# outputPath: './src/graphql/types/auth.gen.ts'
hooks:
afterAllFileWrite:

View File

@ -5,6 +5,7 @@ import { Component, createEffect, createMemo } from 'solid-js'
import { Dynamic } from 'solid-js/web'
import { ConfirmProvider } from '../context/confirm'
import { ConnectProvider } from '../context/connect'
import { EditorProvider } from '../context/editor'
import { LocalizeProvider } from '../context/localize'
import { NotificationsProvider } from '../context/notifications'
@ -40,7 +41,6 @@ import { SearchPage } from '../pages/search.page'
import { TopicPage } from '../pages/topic.page'
import { ROUTES, useRouter } from '../stores/router'
import { hideModal, MODALS, showModal } from '../stores/ui'
import { ConnectProvider } from '../context/connect'
// TODO: lazy load
// const SomePage = lazy(() => import('./Pages/SomePage'))

View File

@ -6,13 +6,14 @@ import { createMemo, createSignal, onMount, Show } from 'solid-js'
import { useLocalize } from '../../../context/localize'
import { useNotifications } from '../../../context/notifications'
import { apiClient } from '../../../graphql/client/core'
import { Reaction, Shout } from '../../../graphql/schema/core.gen'
import { Notification } from '../../../graphql/schema/notifier.gen'
import { router, useRouter } from '../../../stores/router'
import { GroupAvatar } from '../../_shared/GroupAvatar'
import { TimeAgo } from '../../_shared/TimeAgo'
import styles from './NotificationView.module.scss'
import { apiClient } from '../../../graphql/client/core'
import { Reaction, Shout } from '../../../graphql/schema/core.gen'
type Props = {
notification: Notification
@ -111,20 +112,21 @@ export const NotificationView = (props: Props) => {
)
}
}
case 'update': {
}
case 'delete': {
}
case 'follow': {
}
case 'unfollow': {
}
case 'invited': {
case 'update':
case 'delete':
case 'follow':
case 'unfollow':
case 'invited':
// TODO: invited for collaborative authoring
}
default:
default: {
return <></>
}
}
})
const handleClick = () => {

View File

@ -1,47 +1,14 @@
import { createContext, createEffect, createMemo, onCleanup, onMount, useContext } from 'solid-js'
import type { ParentComponent } from 'solid-js'
import { createStore } from 'solid-js/store'
import { Authorizer, User, AuthToken } from '@authorizerdev/authorizer-js'
export enum AuthorizerProviderActionType {
SET_USER = 'SET_USER',
SET_TOKEN = 'SET_TOKEN',
SET_LOADING = 'SET_LOADING',
SET_AUTH_DATA = 'SET_AUTH_DATA',
SET_CONFIG = 'SET_CONFIG',
}
import { Authorizer, User, AuthToken, ConfigType } from '@authorizerdev/authorizer-js'
import { createContext, createEffect, createMemo, onCleanup, onMount, useContext } from 'solid-js'
import { createStore } from 'solid-js/store'
export type AuthorizerState = {
user: User | null
token: AuthToken | null
loading: boolean
config: {
authorizerURL: string
redirectURL: string
client_id: string
is_google_login_enabled: boolean
is_github_login_enabled: boolean
is_facebook_login_enabled: boolean
is_linkedin_login_enabled: boolean
is_apple_login_enabled: boolean
is_twitter_login_enabled: boolean
is_microsoft_login_enabled: boolean
is_email_verification_enabled: boolean
is_basic_authentication_enabled: boolean
is_magic_link_login_enabled: boolean
is_sign_up_enabled: boolean
is_strong_password_enabled: boolean
}
}
export type AuthorizerProviderAction = {
type: AuthorizerProviderActionType
payload: any
}
export type OtpDataType = {
isScreenVisible: boolean
email: string
config: ConfigType
}
type AuthorizerContextActions = {
@ -52,27 +19,15 @@ type AuthorizerContextActions = {
authorizer: () => Authorizer
logout: () => Promise<void>
}
const config: ConfigType = {
authorizerURL: 'http://auth.discours.io',
redirectURL: 'http://auth.discours.io',
clientID: '9c113377-5eea-4c89-98e1-69302462fc08', // FIXME: use env?
}
// TODO: fix types
const AuthorizerContext = createContext<[AuthorizerState, AuthorizerContextActions]>([
{
config: {
authorizerURL: '',
redirectURL: '/',
client_id: '',
is_google_login_enabled: true,
is_github_login_enabled: true,
is_facebook_login_enabled: true,
is_linkedin_login_enabled: false,
is_apple_login_enabled: false,
is_twitter_login_enabled: true,
is_microsoft_login_enabled: false,
is_email_verification_enabled: true,
is_basic_authentication_enabled: true,
is_magic_link_login_enabled: true,
is_sign_up_enabled: true,
is_strong_password_enabled: true,
},
config,
user: null,
token: null,
loading: false,
@ -82,12 +37,7 @@ const AuthorizerContext = createContext<[AuthorizerState, AuthorizerContextActio
setToken: () => {},
setUser: () => {},
setAuthData: () => {},
authorizer: () =>
new Authorizer({
authorizerURL: 'http://auth.discours.io',
redirectURL: 'http://auth.discours.io',
clientID: '', // FIXME: add real client id
}),
authorizer: () => new Authorizer(config),
logout: async () => {},
},
])
@ -106,21 +56,8 @@ export const AuthorizerProvider: ParentComponent<AuthorizerProviderProps> = (pro
loading: true,
config: {
authorizerURL: props.authorizerURL,
is_apple_login_enabled: false,
is_microsoft_login_enabled: false,
redirectURL: props.redirectURL,
is_google_login_enabled: true,
is_github_login_enabled: true,
is_facebook_login_enabled: true,
is_linkedin_login_enabled: false,
is_twitter_login_enabled: true,
is_email_verification_enabled: true,
is_basic_authentication_enabled: true,
is_magic_link_login_enabled: true,
is_sign_up_enabled: false,
is_strong_password_enabled: true,
client_id: props.clientID,
},
clientID: props.clientID,
} as ConfigType,
})
const authorizer = createMemo(
@ -202,7 +139,7 @@ export const AuthorizerProvider: ParentComponent<AuthorizerProviderProps> = (pro
} else {
setState((prev) => ({ ...prev, user: null, token: null }))
}
} catch (e) {
} catch {
setState((prev) => ({ ...prev, user: null, token: null }))
} finally {
setState('config', (config) => ({ ...config, ...metaRes }))

View File

@ -2,7 +2,9 @@ import type { Accessor, JSX } from 'solid-js'
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { createContext, useContext, createSignal, createEffect } from 'solid-js'
import { getToken } from '../graphql/privateGraphQLClient'
import { useSession } from './session'
export interface SSEMessage {
@ -28,7 +30,7 @@ export const ConnectProvider = (props: { children: JSX.Element }) => {
// const [messages, setMessages] = createSignal<Array<SSEMessage>>([]);
const [connected, setConnected] = createSignal(false)
const { isAuthenticated, author } = useSession()
const { isAuthenticated } = useSession()
const addHandler = (handler: MessageHandler) => {
setHandlers((hhh) => [...hhh, handler])

View File

@ -1,9 +1,12 @@
import type { Chat, Message, MutationCreateMessageArgs } from '../graphql/schema/chat.gen'
import type { Accessor, JSX } from 'solid-js'
import { createContext, createSignal, useContext } from 'solid-js'
import { SSEMessage, useConnect } from './connect'
import { loadMessages } from '../stores/inbox'
import { inboxClient } from '../graphql/client/chat'
import { loadMessages } from '../stores/inbox'
import { SSEMessage, useConnect } from './connect'
type InboxContextType = {
chats: Accessor<Chat[]>

View File

@ -8,6 +8,7 @@ import { ShowIfAuthenticated } from '../components/_shared/ShowIfAuthenticated'
import { NotificationsPanel } from '../components/NotificationsPanel'
import { notifierClient as apiClient } from '../graphql/client/notifier'
import { Notification } from '../graphql/schema/notifier.gen'
import { SSEMessage, useConnect } from './connect'
type NotificationsContextType = {
@ -70,7 +71,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
const markNotificationAsRead = async (notification: Notification) => {
await apiClient.markNotificationAsRead(notification.id)
const nnn = new Set([...notification.seen, notification.id])
setNotificationEntities(notification.id, 'seen', Array.from(nnn))
setNotificationEntities(notification.id, 'seen', [...nnn])
setUnreadNotificationsCount((oldCount) => oldCount - 1)
}
const markAllNotificationsAsRead = async () => {

View File

@ -2,6 +2,7 @@ import type { AuthModalSource } from '../components/Nav/AuthModal/types'
import type { Author, Result } from '../graphql/schema/core.gen'
import type { Accessor, JSX, Resource } from 'solid-js'
import { VerifyEmailInput, LoginInput, AuthToken, User } from '@authorizerdev/authorizer-js'
import {
createEffect,
createContext,
@ -16,10 +17,9 @@ import { apiClient } from '../graphql/client/core'
import { getToken, resetToken, setToken } from '../graphql/privateGraphQLClient'
import { showModal } from '../stores/ui'
import { useAuthorizer } from './authorizer'
import { useLocalize } from './localize'
import { useSnackbar } from './snackbar'
import { useAuthorizer } from './authorizer'
import { VerifyEmailInput, LoginInput, AuthToken, User } from '@authorizerdev/authorizer-js'
export type SessionContextType = {
session: Resource<AuthToken>
@ -77,13 +77,13 @@ export const SessionProvider = (props: { children: JSX.Element }) => {
const authResult = await authorizer().getSession({
Authorization: token,
})
if (!authResult) {
return null
} else {
if (authResult) {
console.log(authResult)
setToken(authResult.access_token || authResult.id_token)
loadSubscriptions()
return authResult
} else {
return null
}
} catch (error) {
console.error('getSession error:', error)

View File

@ -1,141 +0,0 @@
import { ApiError } from '../error'
import authConfirmEmailMutation from '../mutation/_auth/auth-confirm-email'
import authLogoutQuery from '../mutation/_auth/auth-logout'
import authRegisterMutation from '../mutation/_auth/auth-register'
import authSendLinkMutation from '../mutation/_auth/auth-send-link'
import mySession from '../mutation/_auth/my-session'
import { getToken, getPrivateClient } from '../privateGraphQLClient'
import { getPublicClient } from '../publicGraphQLClient'
import authCheckEmailQuery from '../query/_auth/auth-check-email'
import authLoginQuery from '../query/_auth/auth-login'
import {
ResendVerifyEmailInput,
ResetPasswordInput,
CreateUserInput,
DeleteUserInput,
UpdateUserInput,
LoginInput,
SignUpInput,
MagicLinkLoginInput,
AuthResponse,
} from '../schema/auth.gen'
export const authPublicGraphQLClient = getPublicClient('auth')
export const authPrivateGraphqlClient = getPrivateClient('auth')
export const authApiClient = {
authLogin: async ({ email, password }: { email: string; password: string }): Promise<AuthResponse> => {
const response = await authPublicGraphQLClient.query(authLoginQuery, { email, password }).toPromise()
// console.debug('[api-client] authLogin', { response })
if (response.error) {
if (
response.error.message === '[GraphQL] User not found' ||
response.error.message === "[GraphQL] 'dict' object has no attribute 'id'"
) {
throw new ApiError('user_not_found')
}
throw new ApiError('unknown', response.error.message)
}
if (response.data.signIn.error) {
if (response.data.signIn.error === 'please, confirm email') {
throw new ApiError('email_not_confirmed')
}
throw new ApiError('unknown', response.data.signIn.error)
}
return response.data.signIn
},
authRegister: async ({
email,
password,
name,
}: {
email: string
password: string
name: string
}): Promise<void> => {
const response = await authPublicGraphQLClient
.mutation(authRegisterMutation, { email, password, name })
.toPromise()
if (response.error) {
if (response.error.message === '[GraphQL] User already exist') {
throw new ApiError('user_already_exists', response.error.message)
}
throw new ApiError('unknown', response.error.message)
}
},
authSignOut: async () => {
const response = await authPublicGraphQLClient.query(authLogoutQuery, {}).toPromise()
return response.data.signOut
},
authCheckEmail: async ({ email }) => {
// check if email is used
const response = await authPublicGraphQLClient.query(authCheckEmailQuery, { email }).toPromise()
return response.data.isEmailUsed
},
authSendLink: async ({ email, lang, template }) => {
// send link with code on email
const response = await authPublicGraphQLClient
.mutation(authSendLinkMutation, { email, lang, template })
.toPromise()
if (response.error) {
if (response.error.message === '[GraphQL] User not found') {
throw new ApiError('user_not_found', response.error.message)
}
throw new ApiError('unknown', response.error.message)
}
if (response.data.sendLink.error) {
throw new ApiError('unknown', response.data.sendLink.message)
}
return response.data.sendLink
},
confirmEmail: async ({ token }: { token: string }) => {
// confirm email with code from link
const response = await authPublicGraphQLClient.mutation(authConfirmEmailMutation, { token }).toPromise()
if (response.error) {
// TODO: better error communication
if (response.error.message === '[GraphQL] check token lifetime') {
throw new ApiError('token_expired', response.error.message)
}
if (response.error.message === '[GraphQL] token is not valid') {
throw new ApiError('token_invalid', response.error.message)
}
throw new ApiError('unknown', response.error.message)
}
if (response.data?.confirmEmail?.error) {
throw new ApiError('unknown', response.data?.confirmEmail?.error)
}
return response.data.confirmEmail
},
getSession: async (): Promise<AuthResponse> => {
if (!getToken()) {
return null
}
// renew session with auth token in header (!)
const response = await authPrivateGraphqlClient.mutation(mySession, {}).toPromise()
if (response.error) {
throw new ApiError('unknown', response.error.message)
}
if (response.data?.getSession?.error) {
throw new ApiError('unknown', response.data.getSession.error)
}
return response.data.getSession
},
}

View File

@ -11,67 +11,67 @@ import chatMessagesLoadBy from '../query/chat/chat-messages-load-by'
import loadRecipients from '../query/chat/chat-recipients'
import myChats from '../query/chat/chats-load'
import {
QueryLoadChatsArgs,
QueryLoadMessagesByArgs,
MutationCreateChatArgs,
MutationCreateMessageArgs,
QueryLoadRecipientsArgs,
Chat,
MutationMarkAsReadArgs,
MutationDeleteChatArgs,
MutationUpdateChatArgs,
MutationUpdateMessageArgs,
MutationDeleteMessageArgs,
MutationCreate_ChatArgs,
MutationCreate_MessageArgs,
MutationDelete_ChatArgs,
MutationDelete_MessageArgs,
MutationMark_As_ReadArgs,
MutationUpdate_ChatArgs,
MutationUpdate_MessageArgs,
QueryLoad_ChatsArgs,
QueryLoad_Messages_ByArgs,
QueryLoad_RecipientsArgs,
} from '../schema/chat.gen'
const privateInboxGraphQLClient = getPrivateClient('chat')
export const inboxClient = {
loadChats: async (options: QueryLoadChatsArgs): Promise<Chat[]> => {
loadChats: async (options: QueryLoad_ChatsArgs): Promise<Chat[]> => {
const resp = await privateInboxGraphQLClient.query(myChats, options).toPromise()
return resp.data.load_chats.chats
},
createChat: async (options: MutationCreateChatArgs) => {
createChat: async (options: MutationCreate_ChatArgs) => {
const resp = await privateInboxGraphQLClient.mutation(createChat, options).toPromise()
return resp.data.create_chat
},
markAsRead: async (options: MutationMarkAsReadArgs) => {
markAsRead: async (options: MutationMark_As_ReadArgs) => {
const resp = await privateInboxGraphQLClient.mutation(markAsRead, options).toPromise()
return resp.data.mark_as_read
},
updateChat: async (options: MutationUpdateChatArgs) => {
updateChat: async (options: MutationUpdate_ChatArgs) => {
const resp = await privateInboxGraphQLClient.mutation(updateChat, options).toPromise()
return resp.data.update_chat
},
deleteChat: async (options: MutationDeleteChatArgs) => {
deleteChat: async (options: MutationDelete_ChatArgs) => {
const resp = await privateInboxGraphQLClient.mutation(deleteChat, options).toPromise()
return resp.data.delete_chat
},
createMessage: async (options: MutationCreateMessageArgs) => {
createMessage: async (options: MutationCreate_MessageArgs) => {
const resp = await privateInboxGraphQLClient.mutation(createChatMessage, options).toPromise()
return resp.data.create_message.message
},
updateMessage: async (options: MutationUpdateMessageArgs) => {
updateMessage: async (options: MutationUpdate_MessageArgs) => {
const resp = await privateInboxGraphQLClient.mutation(updateChatMessage, options).toPromise()
return resp.data.update_message.message
},
deleteMessage: async (options: MutationDeleteMessageArgs) => {
deleteMessage: async (options: MutationDelete_MessageArgs) => {
const resp = await privateInboxGraphQLClient.mutation(deleteChatMessage, options).toPromise()
return resp.data.delete_message
},
loadChatMessages: async (options: QueryLoadMessagesByArgs) => {
loadChatMessages: async (options: QueryLoad_Messages_ByArgs) => {
const resp = await privateInboxGraphQLClient.query(chatMessagesLoadBy, options).toPromise()
return resp.data.load_messages_by.messages
},
loadRecipients: async (options: QueryLoadRecipientsArgs) => {
loadRecipients: async (options: QueryLoad_RecipientsArgs) => {
const resp = await privateInboxGraphQLClient.query(loadRecipients, options).toPromise()
return resp.data.load_recipients.members
},

View File

@ -4,12 +4,12 @@ import type {
Topic,
Author,
LoadShoutsOptions,
QueryLoadAuthorsByArgs,
ProfileInput,
ReactionInput,
ReactionBy,
Shout,
Result,
QueryLoad_Authors_ByArgs,
} from '../schema/core.gen'
import createArticle from '../mutation/core/article-create'
@ -25,18 +25,18 @@ import { getPrivateClient } from '../privateGraphQLClient'
import { getPublicClient } from '../publicGraphQLClient'
import shoutLoad from '../query/core/article-load'
import shoutsLoadBy from '../query/core/articles-load-by'
import authorBy from '../query/core/author-by'
import authorFollowers from '../query/core/author-followers'
import authorFollowed from '../query/core/authors-followed-by'
import userFollowedTopics from '../query/core/topics-by-author'
import authorsAll from '../query/core/authors-all'
import authorsLoadBy from '../query/core/authors-load-by'
import draftsLoad from '../query/core/articles-load-drafts'
import myFeed from '../query/core/articles-load-feed'
import authorBy from '../query/core/author-by'
import authorFollowers from '../query/core/author-followers'
import authorsAll from '../query/core/authors-all'
import authorFollowed from '../query/core/authors-followed-by'
import authorsLoadBy from '../query/core/authors-load-by'
import mySubscriptions from '../query/core/my-followed'
import reactionsLoadBy from '../query/core/reactions-load-by'
import topicBySlug from '../query/core/topic-by-slug'
import topicsAll from '../query/core/topics-all'
import userFollowedTopics from '../query/core/topics-by-author'
import topicsRandomQuery from '../query/core/topics-random'
export const privateGraphQLClient = getPublicClient('core')
@ -145,7 +145,7 @@ export const apiClient = {
console.debug('[graphql.client.core] updateReaction:', response)
return response.data.update_reaction.reaction
},
getAuthorsBy: async (options: QueryLoadAuthorsByArgs) => {
getAuthorsBy: async (options: QueryLoad_Authors_ByArgs) => {
const resp = await publicGraphQLClient.query(authorsLoadBy, options).toPromise()
return resp.data.load_authors_by
},

View File

@ -8,7 +8,7 @@ export const notifierPrivateGraphqlClient = getPrivateClient('notifier')
export const notifierClient = {
getNotifications: async (params: QueryLoad_NotificationsArgs): Promise<NotificationsResult> => {
const resp = await notifierPrivateGraphqlClient.query(loadNotifications, { params }).toPromise()
const resp = await notifierPrivateGraphqlClient.query(loadNotifications, params).toPromise()
return resp.data.load_notifications
},
markNotificationAsRead: async (notification_id: number): Promise<void> => {

View File

@ -1,19 +0,0 @@
import { gql } from '@urql/core'
export default gql`
mutation ConfirmEmailMutation($token: String!) {
confirmEmail(token: $token) {
error
token
user {
id
email
name
slug
bio
userpic
links
}
}
}
`

View File

@ -1,9 +0,0 @@
import { gql } from '@urql/core'
export default gql`
query SignOutQuery {
signOut {
error
}
}
`

View File

@ -1,9 +0,0 @@
import { gql } from '@urql/core'
export default gql`
mutation RegisterMutation($email: String!, $password: String!, $name: String!) {
registerUser(email: $email, password: $password, name: $name) {
error
}
}
`

View File

@ -1,9 +0,0 @@
import { gql } from '@urql/core'
export default gql`
mutation SendLinkQuery($email: String!, $lang: String, $template: String) {
sendLink(email: $email, lang: $lang, template: $template) {
error
}
}
`

View File

@ -1,18 +0,0 @@
import { gql } from '@urql/core'
export default gql`
mutation GetSessionMutation {
getSession {
error
token
user {
id
name
slug
bio
userpic
links
}
}
}
`

View File

@ -41,7 +41,7 @@ const options: ClientOptions = {
}
const headers = { Authorization: token }
return { headers }
} catch (_) {
} catch {
return {}
}
},
@ -51,5 +51,5 @@ const options: ClientOptions = {
export const getPrivateClient = (name: string) =>
createClient({
...options,
url: `https://${name.replace('core', 'testapi')}.discours.io`,
url: `https://${name}.discours.io`,
})

View File

@ -20,5 +20,5 @@ const options: ClientOptions = {
export const getPublicClient = (name: string) =>
createClient({
...options,
url: `https://${name.replace('core', 'testapi')}.discours.io`,
url: `https://${name}.discours.io`,
})

View File

@ -1,7 +0,0 @@
import { gql } from '@urql/core'
export default gql`
query isEmailUsedQuery($email: String!) {
isEmailUsed(email: $email)
}
`

View File

@ -1,16 +0,0 @@
import { gql } from '@urql/core'
export default gql`
query SignInQuery($email: String!, $password: String!) {
signIn(email: $email, password: $password) {
error
token
user {
id
name
slug
userpic
}
}
}
`

View File

@ -1,16 +0,0 @@
import { gql } from '@urql/core'
export default gql`
query GetUserRolesBySlug($slug: String!) {
getUserRoles(slug: $slug) {
id
name
# community
desc
permissions {
operation_id
resource_id
}
}
}
`

View File

@ -1,4 +1,7 @@
import { authApiClient as apiClient } from '../graphql/client/_auth'
import { MagicLinkLoginInput, SignupInput } from '@authorizerdev/authorizer-js'
import { useAuthorizer } from '../context/authorizer'
const [, { authorizer }] = useAuthorizer()
export const register = async ({
name,
@ -9,11 +12,12 @@ export const register = async ({
email: string
password: string
}) => {
await apiClient.authRegister({
name,
const signupInput: SignupInput = {
email,
password,
})
confirm_password: password,
}
await authorizer().signup(signupInput)
}
export const signSendLink = async ({
@ -25,5 +29,5 @@ export const signSendLink = async ({
lang: string
template: string
}) => {
return await apiClient.authSendLink({ email, lang, template })
return await authorizer().magicLinkLogin({ email } as MagicLinkLoginInput)
}

View File

@ -1,15 +1,12 @@
import { createSignal } from 'solid-js'
import { authApiClient as apiClient } from '../graphql/client/_auth'
const [emailChecks, setEmailChecks] = createSignal<{ [email: string]: boolean }>({})
export const checkEmail = async (email: string): Promise<boolean> => {
if (emailChecks()[email]) {
return true
}
const checkResult = await apiClient.authCheckEmail({ email })
const checkResult = false // FIXME: secure endpoint for check email
if (checkResult) {
setEmailChecks((oldEmailChecks) => ({ ...oldEmailChecks, [email]: true }))

View File

@ -1,10 +1,9 @@
import type { Author } from '../../graphql/schema/core.gen'
import { createLazyMemo } from '@solid-primitives/memo'
import { createSignal } from 'solid-js'
import { apiClient } from '../../graphql/client/core'
import { byStat } from '../../utils/sortby'
import { Author } from '../../graphql/schema/core.gen'
export type AuthorsSortBy = 'shouts' | 'name' | 'followers'

View File

@ -1,6 +1,5 @@
import type { FollowingEntity } from '../../graphql/schema/core.gen'
import { apiClient } from '../../graphql/client/core'
import { FollowingEntity } from '../../graphql/schema/core.gen'
export const follow = async ({ what, slug }: { what: FollowingEntity; slug: string }) => {
await apiClient.follow({ what, slug })

View File

@ -8,13 +8,16 @@ export type LayoutType = 'article' | 'audio' | 'video' | 'image' | 'literature'
const [sortedLayoutShouts, setSortedLayoutShouts] = createSignal<Map<LayoutType, Shout[]>>(new Map())
const addLayoutShouts = (layout: LayoutType, shouts: Shout[]) => {
const addLayoutShouts = (layouts: LayoutType[], shouts: Shout[]) => {
setSortedLayoutShouts((prevSorted: Map<LayoutType, Shout[]>) => {
layouts.forEach((layout: LayoutType) => {
const siblings = prevSorted.get(layout)
if (siblings) {
const uniqued = [...new Set([...siblings, ...shouts])]
prevSorted.set(layout, uniqued)
}
})
return prevSorted
})
}
@ -34,13 +37,13 @@ export const loadLayoutShoutsBy = async (options: LoadShoutsOptions): Promise<{
if (hasMore) {
newLayoutShouts.splice(-1)
}
addLayoutShouts(options.filters.layout as LayoutType, newLayoutShouts)
addLayoutShouts(options.filters.layouts as LayoutType[], newLayoutShouts)
return { hasMore }
}
export const useLayoutsStore = (layout: LayoutType, initialData: Shout[]) => {
addLayoutShouts(layout, initialData || [])
export const useLayoutsStore = (layouts: LayoutType[], initialData: Shout[]) => {
addLayoutShouts(layouts, initialData || [])
return {
addLayoutShouts,