auth-minor-fixes

This commit is contained in:
Untone 2024-05-24 17:59:15 +03:00
parent b53aa85337
commit 9a42086f08
13 changed files with 128 additions and 114 deletions

View File

@ -1,21 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/1.7.2/schema.json",
"files": {
"include": [
"*.tsx",
"*.ts",
"*.js",
"*.json"
],
"ignore": [
"./dist",
"./node_modules",
".husky",
"docs",
"gen",
"*.gen.ts",
"*.d.ts"
]
"include": ["*.tsx", "*.ts", "*.js", "*.json"],
"ignore": ["./dist", "./node_modules", ".husky", "docs", "gen", "*.gen.ts", "*.d.ts"]
},
"vcs": {
"defaultBranch": "dev",
@ -23,19 +10,13 @@
},
"organizeImports": {
"enabled": true,
"ignore": [
"./api",
"./gen"
]
"ignore": ["./api", "./gen"]
},
"formatter": {
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 108,
"ignore": [
"./src/graphql/schema",
"./gen"
]
"ignore": ["./src/graphql/schema", "./gen"]
},
"javascript": {
"formatter": {
@ -48,13 +29,7 @@
}
},
"linter": {
"ignore": [
"*.scss",
"*.md",
".DS_Store",
"*.svg",
"*.d.ts"
],
"ignore": ["*.scss", "*.md", ".DS_Store", "*.svg", "*.d.ts"],
"enabled": true,
"rules": {
"all": true,

View File

@ -95,7 +95,7 @@
"fast-deep-equal": "3.1.3",
"ga-gtag": "1.2.0",
"graphql": "16.8.1",
"graphql-tag": "2.12.6",
"graphql-tag": "^2.12.6",
"i18next": "22.4.15",
"i18next-http-backend": "2.2.0",
"i18next-icu": "2.3.0",
@ -137,5 +137,7 @@
"y-prosemirror": "1.2.5",
"yjs": "13.6.15"
},
"trustedDependencies": ["@biomejs/biome"]
}
"trustedDependencies": [
"@biomejs/biome"
]
}

View File

@ -541,4 +541,4 @@
"You've reached a non-existed page": "You've reached a non-existed page",
"Your email": "Your email",
"Your name will appear on your profile page and as your signature in publications, comments and responses.": "Your name will appear on your profile page and as your signature in publications, comments and responses"
}
}

View File

@ -568,4 +568,4 @@
"You've successfully logged out": "Вы успешно вышли из аккаунта",
"Your email": "Ваш email",
"Your name will appear on your profile page and as your signature in publications, comments and responses.": "Ваше имя появится на странице вашего профиля и как ваша подпись в публикациях, комментариях и откликах"
}
}

View File

@ -197,8 +197,10 @@ export const AuthorCard = (props: Props) => {
<Show when={props.followers && props.followers.length > 0}>
<a href="?m=followers" class={styles.followers}>
<For each={props.followers.slice(0, 3)}>
{(f) => (
<Userpic size={'XS'} name={f.name} userpic={f.pic} class={styles.followersItem} />
{(f: Author) => (
<Show when={f?.name}>
<Userpic size={'XS'} name={f?.name || ''} userpic={f?.pic || ''} class={styles.followersItem} />
</Show>
)}
</For>
<div class={styles.followsCounter}>

View File

@ -15,20 +15,24 @@ import styles from './DraftsView.module.scss'
export const DraftsView = () => {
const { author, loadSession } = useSession()
const [drafts, setDrafts] = createSignal<Shout[]>([])
const [loading, setLoading] = createSignal(false)
createEffect(
on(
() => author(),
async (a) => {
if (a) {
setLoading(true)
const { shouts: loadedDrafts, error } = await apiClient.getDrafts()
if (error) {
console.warn(error)
await loadSession()
}
setDrafts(loadedDrafts || [])
setLoading(false)
}
},
{ defer: true },
),
)
@ -50,7 +54,7 @@ export const DraftsView = () => {
return (
<div class={clsx(styles.DraftsView)}>
<Show when={author()?.id} fallback={<Loading />}>
<Show when={!loading() && author()?.id} fallback={<Loading />}>
<div class="wide-container">
<div class="row">
<div class="col-md-19 col-lg-18 col-xl-16 offset-md-5">

View File

@ -28,6 +28,7 @@ import { EditorSwiper } from '../../_shared/SolidSwiper'
import { PublishSettings } from '../PublishSettings'
import styles from './EditView.module.scss'
import { Loading } from '../../_shared/Loading'
const SimplifiedEditor = lazy(() => import('../../Editor/SimplifiedEditor'))
const GrowingTextarea = lazy(() => import('../../_shared/GrowingTextarea/GrowingTextarea'))
@ -403,7 +404,7 @@ export const EditView = (props: Props) => {
</Show>
</div>
</div>
<Show when={page().route === 'edit'}>
<Show when={page().route === 'edit' && form?.shoutId} fallback={<Loading />}>
<Editor
shoutId={form.shoutId}
initialContent={form.body}

View File

@ -40,7 +40,18 @@ const EMPTY_TOPIC: Topic = {
id: -1,
slug: '',
}
const emptyConfig = {
interface FormConfig {
coverImageUrl?: string
mainTopic?: Topic
slug?: string
title?: string
subtitle?: string
description?: string
selectedTopics?: Topic[]
}
const emptyConfig: FormConfig = {
coverImageUrl: '',
mainTopic: EMPTY_TOPIC,
slug: '',
@ -78,7 +89,7 @@ export const PublishSettings = (props: Props) => {
}
})
const [settingsForm, setSettingsForm] = createStore(emptyConfig)
const [settingsForm, setSettingsForm] = createStore<FormConfig>(emptyConfig)
onMount(() => {
setSettingsForm(initialData())
@ -96,12 +107,12 @@ export const PublishSettings = (props: Props) => {
setSettingsForm('coverImageUrl', '')
}
const handleTopicSelectChange = (newSelectedTopics) => {
const handleTopicSelectChange = (newSelectedTopics: Topic[]) => {
if (
props.form.selectedTopics.length === 0 ||
newSelectedTopics.every((topic) => topic.id !== props.form.mainTopic?.id)
newSelectedTopics.every((topic: Topic) => topic.id !== props.form.mainTopic?.id)
) {
setSettingsForm((prev) => {
setSettingsForm((prev: Topic) => {
return {
...prev,
mainTopic: newSelectedTopics[0],
@ -193,7 +204,8 @@ export const PublishSettings = (props: Props) => {
fieldName={t('Header')}
placeholder={t('Come up with a title for your story')}
initialValue={settingsForm.title}
value={(value) => setSettingsForm('title', value)}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
value={(value: any) => setSettingsForm('title', value)}
allowEnterKey={false}
maxLength={100}
/>
@ -203,7 +215,8 @@ export const PublishSettings = (props: Props) => {
fieldName={t('Subheader')}
placeholder={t('Come up with a subtitle for your story')}
initialValue={settingsForm.subtitle || ''}
value={(value) => setSettingsForm('subtitle', value)}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
value={(value: any) => setSettingsForm('subtitle', value)}
allowEnterKey={false}
maxLength={100}
/>
@ -214,7 +227,8 @@ export const PublishSettings = (props: Props) => {
placeholder={t('Write a short introduction')}
label={t('Description')}
initialContent={composeDescription()}
onChange={(value) => setForm('description', value)}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
onChange={(value: any) => setForm('description', value)}
maxLength={DESCRIPTION_MAX_LENGTH}
/>
</div>

View File

@ -42,7 +42,7 @@ export const ConnectProvider = (props: { children: JSX.Element }) => {
createEffect(
on(
() => session()?.access_token,
async ([tkn]) => {
async (tkn) => {
if (!sseUrl) return
if (!tkn) return
if (!connected() && retried() <= RECONNECT_TIMES) {
@ -67,7 +67,7 @@ export const ConnectProvider = (props: { children: JSX.Element }) => {
return Promise.resolve()
}
return Promise.reject(
`SSE: cannot connect to real-time updates, status: ${response.status}`,
`SSE: cannot connect to real-time updates: ${response.status}`,
)
},
onclose() {
@ -75,7 +75,7 @@ export const ConnectProvider = (props: { children: JSX.Element }) => {
setConnected(false)
if (retried() < RECONNECT_TIMES) {
setRetried((r) => r + 1)
}
} else throw Error('closed by server')
},
onerror(err) {
console.error('[context.connect] SSE connection error:', err)

View File

@ -34,13 +34,14 @@ import { useRouter } from '../stores/router'
import { showModal } from '../stores/ui'
import { addAuthors } from '../stores/zine/authors'
import { authApiUrl } from '../utils/config'
import { useLocalize } from './localize'
import { useSnackbar } from './snackbar'
const defaultConfig: ConfigType = {
authorizerURL: 'https://auth.discours.io',
authorizerURL: authApiUrl.replace('/graphql', ''),
redirectURL: 'https://testing.discours.io',
clientID: 'b9038a34-ca59-41ae-a105-c7fbea603e24', // FIXME: use env?
clientID: '',
}
export type SessionContextType = {
@ -73,9 +74,32 @@ export type SessionContextType = {
resendVerifyEmail: (params: ResendVerifyEmailInput) => Promise<GenericResponse>
}
// biome-ignore lint/suspicious/noEmptyBlockStatements: <explanation>
const noop = () => {}
const noop = () => null
const metaRes = {
data: {
meta: {
version: 'latest',
// client_id: 'b9038a34-ca59-41ae-a105-c7fbea603e24',
is_google_login_enabled: true,
is_facebook_login_enabled: true,
is_github_login_enabled: true,
is_linkedin_login_enabled: false,
is_apple_login_enabled: false,
is_twitter_login_enabled: true,
is_microsoft_login_enabled: false,
is_twitch_login_enabled: false,
is_roblox_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: false,
is_multi_factor_auth_enabled: true,
is_mobile_basic_authentication_enabled: true,
is_phone_verification_enabled: false,
},
},
}
const SessionContext = createContext<SessionContextType>()
export function useSession() {
@ -212,44 +236,41 @@ export const SessionProvider = (props: {
})
// when session is loaded
createEffect(() => {
if (session()) {
const token = session()?.access_token
if (token) {
if (!inboxClient.private) {
apiClient.connect(token)
inboxClient.connect(token)
}
try {
const appdata = session()?.user.app_data
if (appdata) {
const { profile } = appdata
if (profile?.id) {
setAuthor(profile)
addAuthors([profile])
} else {
setTimeout(loadAuthor, 15)
createEffect(
on(
session,
(s: AuthToken) => {
if (s) {
const token = s?.access_token
if (token) {
if (!inboxClient.private) {
apiClient.connect(token)
inboxClient.connect(token)
}
try {
const appdata = session()?.user.app_data
if (appdata) {
const { profile } = appdata
if (profile?.id) {
setAuthor(profile)
addAuthors([profile])
} else {
setTimeout(loadAuthor, 15)
}
}
} catch (e) {
console.error(e)
}
setIsSessionLoaded(true)
} else {
reset()
}
} catch (e) {
console.error(e)
}
setIsSessionLoaded(true)
}
}
})
// when author is loaded
createEffect(() => {
if (author()) {
addAuthors([author()])
} else {
reset()
}
})
},
{ defer: true },
),
)
const reset = () => {
setIsSessionLoaded(true)
setSession(null)
@ -257,20 +278,13 @@ export const SessionProvider = (props: {
}
// initial effect
onMount(async () => {
const metaRes = await authorizer().getMetaData()
onMount(() => {
setConfig({
...defaultConfig,
...metaRes,
redirectURL: window.location.origin,
})
let s: AuthToken
try {
s = await loadSession()
} catch (error) {
console.warn('[context.session] load session failed', error)
}
if (!s) reset()
loadSession()
})
// callback state updater
@ -318,6 +332,7 @@ export const SessionProvider = (props: {
console.debug(authResult)
reset()
showSnackbar({ body: t("You've successfully logged out") })
console.debug(session())
}
const changePassword = async (password: string, token: string) => {

View File

@ -17,7 +17,7 @@ import styles from '../styles/Create.module.scss'
const handleCreate = async (layout: LayoutType) => {
const shout = await apiClient.createArticle({ article: { layout: layout } })
redirectPage(router, 'edit', {
shout?.id && redirectPage(router, 'edit', {
shoutId: shout?.id.toString(),
})
}

View File

@ -7,7 +7,7 @@ import { useLocalize } from '../context/localize'
import { useSession } from '../context/session'
import { apiClient } from '../graphql/client/core'
import { Shout } from '../graphql/schema/core.gen'
import { router, useRouter } from '../stores/router'
import { router } from '../stores/router'
import { redirectPage } from '@nanostores/router'
import { useSnackbar } from '../context/snackbar'
@ -33,7 +33,6 @@ const getContentTypeTitle = (layout: LayoutType) => {
export const EditPage = () => {
const { t } = useLocalize()
const { session } = useSession()
const { page } = useRouter()
const snackbar = useSnackbar()
const fail = async (error: string) => {
@ -48,15 +47,17 @@ export const EditPage = () => {
createEffect(
on(
() => page(),
() => window?.location.pathname,
(p) => {
if (p?.path) {
console.debug(p?.path)
const shoutId = p?.path.split('/').pop()
const shoutIdFromUrl = Number.parseInt(shoutId ?? '0', 10)
console.debug(`editing shout ${shoutIdFromUrl}`)
if (shoutIdFromUrl) {
setShoutId(shoutIdFromUrl)
if (p) {
console.debug(p)
const shoutId = p.split('/').pop()
if (shoutId) {
const shoutIdFromUrl = Number.parseInt(shoutId ?? '0', 10)
console.debug(`editing shout ${shoutIdFromUrl}`)
if (shoutIdFromUrl) {
setShoutId(shoutIdFromUrl)
}
}
}
},

View File

@ -2,7 +2,7 @@ export const isDev = import.meta.env.MODE === 'development'
export const cdnUrl = 'https://cdn.discours.io'
export const thumborUrl = import.meta.env.PUBLIC_THUMBOR_URL || 'https://images.discours.io'
export const errorsReportingDsn = import.meta.env.PUBLIC_GLITCHTIP_DSN || import.meta.env.PUBLIC_SENTRY_DSN || ''
export const coreApiUrl = 'https://coretest.discours.io'
export const chatApiUrl = 'https://inboxtest.discours.io'
export const authApiUrl = 'https://authtest.discours.io/graphql'
export const sseUrl = 'https://presencetest.discours.io'
export const coreApiUrl = import.meta.env.PUBLIC_API_BASE || 'https://coretest.discours.io'
export const chatApiUrl = import.meta.env.PUBLIC_CHAT_API || 'https://inboxtest.discours.io'
export const authApiUrl = import.meta.env.PUBLIC_AUTH_API || 'https://authtest.discours.io/graphql'
export const sseUrl = import.meta.env.PUBLIC_REALTIME_EVENTS || 'https://presencetest.discours.io'