Merge branch 'hotfix/editor-permission' into feature/rating
This commit is contained in:
commit
df90953138
|
@ -75,7 +75,7 @@ export const FullArticle = (props: Props) => {
|
||||||
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
|
||||||
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
|
||||||
const { t, formatDate, lang } = useLocalize()
|
const { t, formatDate, lang } = useLocalize()
|
||||||
const { author, session, isAuthenticated, requireAuthentication } = useSession()
|
const { author, session, requireAuthentication } = useSession()
|
||||||
|
|
||||||
const formattedDate = createMemo(() => formatDate(new Date(props.article.published_at * 1000)))
|
const formattedDate = createMemo(() => formatDate(new Date(props.article.published_at * 1000)))
|
||||||
|
|
||||||
|
@ -576,7 +576,7 @@ export const FullArticle = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={isAuthenticated() && !canEdit()}>
|
<Show when={author()?.id && !canEdit()}>
|
||||||
<div class={styles.help}>
|
<div class={styles.help}>
|
||||||
<button class="button">{t('Cooperate')}</button>
|
<button class="button">{t('Cooperate')}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,7 +12,7 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AuthGuard = (props: Props) => {
|
export const AuthGuard = (props: Props) => {
|
||||||
const { isAuthenticated, isSessionLoaded } = useSession()
|
const { author, isSessionLoaded } = useSession()
|
||||||
const { changeSearchParams } = useRouter<RootSearchParams & AuthModalSearchParams>()
|
const { changeSearchParams } = useRouter<RootSearchParams & AuthModalSearchParams>()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
@ -20,7 +20,7 @@ export const AuthGuard = (props: Props) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (isSessionLoaded()) {
|
if (isSessionLoaded()) {
|
||||||
if (isAuthenticated()) {
|
if (author()?.id) {
|
||||||
hideModal()
|
hideModal()
|
||||||
} else {
|
} else {
|
||||||
changeSearchParams(
|
changeSearchParams(
|
||||||
|
@ -37,5 +37,5 @@ export const AuthGuard = (props: Props) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return <Show when={(isSessionLoaded() && isAuthenticated()) || props.disabled}>{props.children}</Show>
|
return <Show when={(isSessionLoaded() && author()?.id) || props.disabled}>{props.children}</Show>
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,16 @@ type Props = {
|
||||||
|
|
||||||
export const Panel = (props: Props) => {
|
export const Panel = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { isEditorPanelVisible, wordCounter, editorRef, form, toggleEditorPanel, saveShout, publishShout } =
|
const {
|
||||||
useEditorContext()
|
isEditorPanelVisible,
|
||||||
|
wordCounter,
|
||||||
|
editorRef,
|
||||||
|
form,
|
||||||
|
toggleEditorPanel,
|
||||||
|
saveShout,
|
||||||
|
saveDraft,
|
||||||
|
publishShout,
|
||||||
|
} = useEditorContext()
|
||||||
|
|
||||||
const containerRef: { current: HTMLElement } = { current: null }
|
const containerRef: { current: HTMLElement } = { current: null }
|
||||||
const [isShortcutsVisible, setIsShortcutsVisible] = createSignal(false)
|
const [isShortcutsVisible, setIsShortcutsVisible] = createSignal(false)
|
||||||
|
@ -43,7 +51,12 @@ export const Panel = (props: Props) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleSaveClick = () => {
|
const handleSaveClick = () => {
|
||||||
|
const hasTopics = form.selectedTopics?.length > 0
|
||||||
|
if (hasTopics) {
|
||||||
saveShout(form)
|
saveShout(form)
|
||||||
|
} else {
|
||||||
|
saveDraft(form)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const html = useEditorHTML(() => editorRef.current())
|
const html = useEditorHTML(() => editorRef.current())
|
||||||
|
|
|
@ -32,14 +32,14 @@ const MD_WIDTH_BREAKPOINT = 992
|
||||||
export const HeaderAuth = (props: Props) => {
|
export const HeaderAuth = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { page } = useRouter()
|
const { page } = useRouter()
|
||||||
const { session, author, isAuthenticated, isSessionLoaded } = useSession()
|
const { session, author, isSessionLoaded } = useSession()
|
||||||
const { unreadNotificationsCount, showNotificationsPanel } = useNotifications()
|
const { unreadNotificationsCount, showNotificationsPanel } = useNotifications()
|
||||||
const { form, toggleEditorPanel, saveShout, publishShout } = useEditorContext()
|
const { form, toggleEditorPanel, saveShout, saveDraft, publishShout } = useEditorContext()
|
||||||
|
|
||||||
const handleBellIconClick = (event: Event) => {
|
const handleBellIconClick = (event: Event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
|
||||||
if (!isAuthenticated()) {
|
if (!author()?.id) {
|
||||||
showModal('auth')
|
showModal('auth')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -48,20 +48,22 @@ export const HeaderAuth = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEditorPage = createMemo(() => page().route === 'edit' || page().route === 'editSettings')
|
const isEditorPage = createMemo(() => page().route === 'edit' || page().route === 'editSettings')
|
||||||
const isNotificationsVisible = createMemo(() => isAuthenticated() && !isEditorPage())
|
const isNotificationsVisible = createMemo(() => author()?.id && !isEditorPage())
|
||||||
const isSaveButtonVisible = createMemo(() => isAuthenticated() && isEditorPage())
|
const isSaveButtonVisible = createMemo(() => author()?.id && isEditorPage())
|
||||||
const isCreatePostButtonVisible = createMemo(() => !isEditorPage())
|
const isCreatePostButtonVisible = createMemo(() => !isEditorPage())
|
||||||
const isAuthenticatedControlsVisible = createMemo(
|
const isAuthenticatedControlsVisible = createMemo(() => author()?.id && session()?.user?.email_verified)
|
||||||
() => isAuthenticated() && session()?.user?.email_verified,
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleBurgerButtonClick = () => {
|
const handleBurgerButtonClick = () => {
|
||||||
toggleEditorPanel()
|
toggleEditorPanel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: remove the code if not needed here
|
const handleSaveClick = () => {
|
||||||
const _handleSaveButtonClick = () => {
|
const hasTopics = form.selectedTopics?.length > 0
|
||||||
|
if (hasTopics) {
|
||||||
saveShout(form)
|
saveShout(form)
|
||||||
|
} else {
|
||||||
|
saveDraft(form)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [width, setWidth] = createSignal(0)
|
const [width, setWidth] = createSignal(0)
|
||||||
|
@ -107,7 +109,7 @@ export const HeaderAuth = (props: Props) => {
|
||||||
<Show when={isSessionLoaded()} keyed={true}>
|
<Show when={isSessionLoaded()} keyed={true}>
|
||||||
<div class={clsx('col-auto col-lg-7', styles.usernav)}>
|
<div class={clsx('col-auto col-lg-7', styles.usernav)}>
|
||||||
<div class={styles.userControl}>
|
<div class={styles.userControl}>
|
||||||
<Show when={isCreatePostButtonVisible() && isAuthenticated()}>
|
<Show when={isCreatePostButtonVisible() && author()?.id}>
|
||||||
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}>
|
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}>
|
||||||
<a href={getPagePath(router, 'create')}>
|
<a href={getPagePath(router, 'create')}>
|
||||||
<span class={styles.textLabel}>{t('Create post')}</span>
|
<span class={styles.textLabel}>{t('Create post')}</span>
|
||||||
|
@ -215,7 +217,7 @@ export const HeaderAuth = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={isCreatePostButtonVisible() && !isAuthenticated()}>
|
<Show when={isCreatePostButtonVisible() && !author()?.id}>
|
||||||
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}>
|
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}>
|
||||||
<a href={getPagePath(router, 'create')}>
|
<a href={getPagePath(router, 'create')}>
|
||||||
<span class={styles.textLabel}>{t('Create post')}</span>
|
<span class={styles.textLabel}>{t('Create post')}</span>
|
||||||
|
@ -228,7 +230,7 @@ export const HeaderAuth = (props: Props) => {
|
||||||
<Show
|
<Show
|
||||||
when={isAuthenticatedControlsVisible()}
|
when={isAuthenticatedControlsVisible()}
|
||||||
fallback={
|
fallback={
|
||||||
<Show when={!isAuthenticated()}>
|
<Show when={!author()?.id}>
|
||||||
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose, 'loginbtn')}>
|
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose, 'loginbtn')}>
|
||||||
<a href="?m=auth&mode=login">
|
<a href="?m=auth&mode=login">
|
||||||
<span class={styles.textLabel}>{t('Enter')}</span>
|
<span class={styles.textLabel}>{t('Enter')}</span>
|
||||||
|
@ -239,7 +241,9 @@ export const HeaderAuth = (props: Props) => {
|
||||||
</Show>
|
</Show>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Show when={!isSaveButtonVisible()}>
|
<Show
|
||||||
|
when={isSaveButtonVisible()}
|
||||||
|
fallback={
|
||||||
<div class={clsx(styles.userControlItem, styles.userControlItemInbox)}>
|
<div class={clsx(styles.userControlItem, styles.userControlItemInbox)}>
|
||||||
<a href={getPagePath(router, 'inbox')}>
|
<a href={getPagePath(router, 'inbox')}>
|
||||||
<div classList={{ entered: page().path === '/inbox' }}>
|
<div classList={{ entered: page().path === '/inbox' }}>
|
||||||
|
@ -248,11 +252,20 @@ export const HeaderAuth = (props: Props) => {
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class={clsx(styles.userControlItem, styles.userControlItemVerbose)}>
|
||||||
|
<button onClick={handleSaveClick}>
|
||||||
|
<span class={styles.textLabel}>{t('Save')}</span>
|
||||||
|
<Icon name="save" class={styles.icon} />
|
||||||
|
<Icon name="save" class={clsx(styles.icon, styles.iconHover)} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={isAuthenticated()}>
|
<Show when={author()?.id}>
|
||||||
<ProfilePopup
|
<ProfilePopup
|
||||||
onVisibilityChange={(isVisible) => {
|
onVisibilityChange={(isVisible) => {
|
||||||
props.setIsProfilePopupVisible(isVisible)
|
props.setIsProfilePopupVisible(isVisible)
|
||||||
|
|
|
@ -46,7 +46,7 @@ const isEarlier = (date: Date) => {
|
||||||
|
|
||||||
export const NotificationsPanel = (props: Props) => {
|
export const NotificationsPanel = (props: Props) => {
|
||||||
const [isLoading, setIsLoading] = createSignal(false)
|
const [isLoading, setIsLoading] = createSignal(false)
|
||||||
const { isAuthenticated } = useSession()
|
const { author } = useSession()
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const {
|
const {
|
||||||
after,
|
after,
|
||||||
|
@ -150,16 +150,13 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
createEffect(
|
createEffect(
|
||||||
on(
|
on(author, async (a) => {
|
||||||
() => isAuthenticated(),
|
if (a?.id) {
|
||||||
async () => {
|
|
||||||
if (isAuthenticated()) {
|
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
await loadNextPage()
|
await loadNextPage()
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -68,13 +68,14 @@ export const EditView = (props: Props) => {
|
||||||
} = useEditorContext()
|
} = useEditorContext()
|
||||||
const shoutTopics = props.shout.topics || []
|
const shoutTopics = props.shout.topics || []
|
||||||
|
|
||||||
// TODO: проверить сохранение черновика в local storage (не работает)
|
const draft = getDraftFromLocalStorage(props.shout.id)
|
||||||
const draft = props.shout || getDraftFromLocalStorage(props.shout.id)
|
|
||||||
if (draft) {
|
if (draft) {
|
||||||
// console.debug('draft: ', draft)
|
const draftForm = Object.keys(draft).length !== 0 ? draft : { shoutId: props.shout.id }
|
||||||
setForm(Object.keys(draft).length !== 0 ? draft : { shoutId: props.shout.id })
|
setForm(draftForm)
|
||||||
|
console.debug('draft from localstorage: ', draftForm)
|
||||||
} else {
|
} else {
|
||||||
setForm({
|
const draftForm = {
|
||||||
slug: props.shout.slug,
|
slug: props.shout.slug,
|
||||||
shoutId: props.shout.id,
|
shoutId: props.shout.id,
|
||||||
title: props.shout.title,
|
title: props.shout.title,
|
||||||
|
@ -87,7 +88,9 @@ export const EditView = (props: Props) => {
|
||||||
coverImageUrl: props.shout.cover,
|
coverImageUrl: props.shout.cover,
|
||||||
media: props.shout.media,
|
media: props.shout.media,
|
||||||
layout: props.shout.layout,
|
layout: props.shout.layout,
|
||||||
})
|
}
|
||||||
|
setForm(draftForm)
|
||||||
|
console.debug('draft from props data: ', draftForm)
|
||||||
}
|
}
|
||||||
|
|
||||||
const subtitleInput: { current: HTMLTextAreaElement } = { current: null }
|
const subtitleInput: { current: HTMLTextAreaElement } = { current: null }
|
||||||
|
@ -110,9 +113,6 @@ export const EditView = (props: Props) => {
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
window.removeEventListener('scroll', handleScroll)
|
window.removeEventListener('scroll', handleScroll)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
// eslint-disable-next-line unicorn/consistent-function-scoping
|
// eslint-disable-next-line unicorn/consistent-function-scoping
|
||||||
const handleBeforeUnload = (event) => {
|
const handleBeforeUnload = (event) => {
|
||||||
if (!deepEqual(prevForm, form)) {
|
if (!deepEqual(prevForm, form)) {
|
||||||
|
@ -188,17 +188,12 @@ export const EditView = (props: Props) => {
|
||||||
const hasChanges = !deepEqual(form, prevForm)
|
const hasChanges = !deepEqual(form, prevForm)
|
||||||
const hasTopic = Boolean(form.mainTopic)
|
const hasTopic = Boolean(form.mainTopic)
|
||||||
if (hasChanges || hasTopic) {
|
if (hasChanges || hasTopic) {
|
||||||
console.debug('[EditView.autoSave] shout has topic')
|
console.debug('saving draft', form)
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
if (props.shout?.published_at) {
|
|
||||||
saveDraftToLocalStorage(form)
|
saveDraftToLocalStorage(form)
|
||||||
} else {
|
|
||||||
await saveDraft(form)
|
await saveDraft(form)
|
||||||
}
|
|
||||||
setPrevForm(clone(form))
|
setPrevForm(clone(form))
|
||||||
setTimeout(() => {
|
setTimeout(() => setSaving(false), AUTO_SAVE_DELAY)
|
||||||
setSaving(false)
|
|
||||||
}, AUTO_SAVE_DELAY)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,11 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
const [unreadNotificationsCount, setUnreadNotificationsCount] = createSignal(0)
|
const [unreadNotificationsCount, setUnreadNotificationsCount] = createSignal(0)
|
||||||
const [totalNotificationsCount, setTotalNotificationsCount] = createSignal(0)
|
const [totalNotificationsCount, setTotalNotificationsCount] = createSignal(0)
|
||||||
const [notificationEntities, setNotificationEntities] = createStore<Record<string, NotificationGroup>>({})
|
const [notificationEntities, setNotificationEntities] = createStore<Record<string, NotificationGroup>>({})
|
||||||
const { isAuthenticated } = useSession()
|
const { author } = useSession()
|
||||||
const { addHandler } = useConnect()
|
const { addHandler } = useConnect()
|
||||||
|
|
||||||
const loadNotificationsGrouped = async (options: { after: number; limit?: number; offset?: number }) => {
|
const loadNotificationsGrouped = async (options: { after: number; limit?: number; offset?: number }) => {
|
||||||
if (isAuthenticated() && notifierClient?.private) {
|
if (author()?.id && notifierClient?.private) {
|
||||||
const notificationsResult = await notifierClient.getNotifications(options)
|
const notificationsResult = await notifierClient.getNotifications(options)
|
||||||
const groups = notificationsResult?.notifications || []
|
const groups = notificationsResult?.notifications || []
|
||||||
const total = notificationsResult?.total || 0
|
const total = notificationsResult?.total || 0
|
||||||
|
@ -74,7 +74,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
addHandler((data: SSEMessage) => {
|
addHandler((data: SSEMessage) => {
|
||||||
if (data.entity === 'reaction' && isAuthenticated()) {
|
if (data.entity === 'reaction' && author()?.id) {
|
||||||
console.info('[context.notifications] event', data)
|
console.info('[context.notifications] event', data)
|
||||||
loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) })
|
loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) })
|
||||||
}
|
}
|
||||||
|
@ -91,14 +91,14 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const markSeenAll = async () => {
|
const markSeenAll = async () => {
|
||||||
if (isAuthenticated() && notifierClient.private) {
|
if (author()?.id && notifierClient.private) {
|
||||||
await notifierClient.markSeenAfter({ after: after() })
|
await notifierClient.markSeenAfter({ after: after() })
|
||||||
await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() })
|
await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const markSeen = async (notification_id: number) => {
|
const markSeen = async (notification_id: number) => {
|
||||||
if (isAuthenticated() && notifierClient.private) {
|
if (author()?.id && notifierClient.private) {
|
||||||
await notifierClient.markSeen(notification_id)
|
await notifierClient.markSeen(notification_id)
|
||||||
await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() })
|
await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() })
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,6 @@ export type SessionContextType = {
|
||||||
author: Resource<Author | null>
|
author: Resource<Author | null>
|
||||||
authError: Accessor<string>
|
authError: Accessor<string>
|
||||||
isSessionLoaded: Accessor<boolean>
|
isSessionLoaded: Accessor<boolean>
|
||||||
isAuthenticated: Accessor<boolean>
|
|
||||||
loadSession: () => AuthToken | Promise<AuthToken>
|
loadSession: () => AuthToken | Promise<AuthToken>
|
||||||
setSession: (token: AuthToken | null) => void // setSession
|
setSession: (token: AuthToken | null) => void // setSession
|
||||||
loadAuthor: (info?: unknown) => Author | Promise<Author>
|
loadAuthor: (info?: unknown) => Author | Promise<Author>
|
||||||
|
@ -375,9 +374,6 @@ export const SessionProvider = (props: {
|
||||||
console.warn(error)
|
console.warn(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAuthenticated = createMemo(() => Boolean(author()))
|
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
loadSession,
|
loadSession,
|
||||||
requireAuthentication,
|
requireAuthentication,
|
||||||
|
@ -402,7 +398,6 @@ export const SessionProvider = (props: {
|
||||||
isSessionLoaded,
|
isSessionLoaded,
|
||||||
author,
|
author,
|
||||||
...actions,
|
...actions,
|
||||||
isAuthenticated,
|
|
||||||
resendVerifyEmail,
|
resendVerifyEmail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user