mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
feat(settings): add per-user Requests visibility toggle
Let users hide request entry points and notifications while existing requests continue running. Keep the setting optional so older clients retain their current behavior. Ref #1476
This commit is contained in:
@@ -40,6 +40,7 @@ import SettingsSidebar from '@/features/settings/components/SettingsSidebar.vue'
|
||||
import { useWhatsNew } from '@/features/whats-new/composables/useWhatsNew'
|
||||
import { useBookRequestSummary } from '@/features/book-requests/composables/useBookRequestSummary'
|
||||
import { useBookRequestProgress } from '@/features/book-requests/composables/useBookRequestProgress'
|
||||
import { useBookRequestVisibility } from '@/features/book-requests/composables/useBookRequestVisibility'
|
||||
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
@@ -50,6 +51,7 @@ const { libraries, fetchLibraries, refreshLibraries, reorderLibraries } = useLib
|
||||
const { bookScopes, podcastScopes, fetchSmartScopes, reorderSmartScopes } = useSmartScopes()
|
||||
const { bookCollections, podcastCollections, fetchCollections, reorderCollections } = useCollections()
|
||||
const { hasPermission } = usePermissions()
|
||||
const { showBookRequests } = useBookRequestVisibility()
|
||||
const { subscribeLibrary, getProgress } = useScanProgress()
|
||||
const podcastImportProgress = APP_FEATURES.podcasts ? usePodcastImportProgress() : null
|
||||
const { handleLibraryCreated } = useLibraryCreationRedirect()
|
||||
@@ -268,7 +270,7 @@ onMounted(async () => {
|
||||
void fetchSmartScopes()
|
||||
void fetchCollections()
|
||||
void fetchBrowseCounts()
|
||||
if (hasPermission(Permission.BookRequestAccess)) void fetchBookRequestSummary()
|
||||
if (showBookRequests.value && hasPermission(Permission.BookRequestAccess)) void fetchBookRequestSummary()
|
||||
void loadAppInfo()
|
||||
if (hasPermission('book_dock_access')) {
|
||||
void fetchBookDockSummary()
|
||||
@@ -277,15 +279,19 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
requestProgress?.onRequestsChanged(() => {
|
||||
void refreshBookRequestSummary()
|
||||
if (showBookRequests.value) void refreshBookRequestSummary()
|
||||
})
|
||||
|
||||
if (requestProgress) {
|
||||
watch(requestProgress.connected, (connected) => {
|
||||
if (connected) void refreshBookRequestSummary()
|
||||
if (connected && showBookRequests.value) void refreshBookRequestSummary()
|
||||
})
|
||||
}
|
||||
|
||||
watch(showBookRequests, (visible) => {
|
||||
if (visible && hasPermission(Permission.BookRequestAccess)) void fetchBookRequestSummary()
|
||||
})
|
||||
|
||||
const { onLibraryUploadCompleted } = useLibraryUploadEvents()
|
||||
const stopLibraryUploadListener = onLibraryUploadCompleted((event) => {
|
||||
if (event.uploadedCount === 0) return
|
||||
|
||||
@@ -31,6 +31,7 @@ vi.mock('@/features/auth/composables/usePermissions', () => ({
|
||||
function makeContext(
|
||||
overrides: Partial<{
|
||||
permissions: string[]
|
||||
showBookRequests: boolean
|
||||
bookDockTotal: number
|
||||
outstandingRequestTotal: number
|
||||
outstandingRequestLabel: string
|
||||
@@ -41,6 +42,7 @@ function makeContext(
|
||||
const permissions = overrides.permissions ?? []
|
||||
return {
|
||||
hasPermission: (name: string) => permissions.includes(name),
|
||||
showBookRequests: overrides.showBookRequests,
|
||||
bookDockTotal: overrides.bookDockTotal ?? 0,
|
||||
outstandingRequestTotal: overrides.outstandingRequestTotal ?? 0,
|
||||
outstandingRequestLabel: overrides.outstandingRequestLabel ?? '',
|
||||
@@ -97,6 +99,12 @@ describe('sidebar nav registry', () => {
|
||||
expect(allowedIds(makeContext())).not.toContain('book-requests')
|
||||
})
|
||||
|
||||
it('hides Requests without changing permission when the user turns visibility off', () => {
|
||||
const context = makeContext({ permissions: ['book_request_access'], showBookRequests: false })
|
||||
expect(allowedIds(context)).not.toContain('book-requests')
|
||||
expect(context.hasPermission('book_request_access')).toBe(true)
|
||||
})
|
||||
|
||||
it.each(['book-requests', 'book-request-detail', 'book-request-releases'])('keeps Requests active on %s', (routeName) => {
|
||||
const resolved = resolveNavEntry(entry('book-requests'), makeContext({ permissions: ['book_request_access'] }), makeRoute(routeName), 'Requests')
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ import { useBookDockSummary } from '@/features/book-dock/composables/useBookDock
|
||||
import { useLibraries } from '@/features/library/composables/useLibraries'
|
||||
import { useBrowseCounts } from '@/composables/useBrowseCounts'
|
||||
import { useMediaMode } from '@/composables/useMediaMode'
|
||||
import { useBookRequestVisibility } from '@/features/book-requests/composables/useBookRequestVisibility'
|
||||
|
||||
export const SIDEBAR_ZONE_IDS = ['primary', 'browse'] as const
|
||||
export type SidebarZoneId = (typeof SIDEBAR_ZONE_IDS)[number]
|
||||
|
||||
interface NavContext {
|
||||
hasPermission: (name: string) => boolean
|
||||
showBookRequests?: boolean
|
||||
bookDockTotal: number
|
||||
outstandingRequestTotal: number
|
||||
outstandingRequestLabel: string
|
||||
@@ -115,6 +117,7 @@ export const SIDEBAR_NAV_REGISTRY: readonly SidebarNavEntry[] = [
|
||||
// The drawer routes are children of this one, so the row has to stay lit while one is open.
|
||||
isActive: (route) => routeNameStartsWith(route, 'book-request'),
|
||||
permission: 'book_request_access',
|
||||
visible: (context) => context.showBookRequests !== false,
|
||||
badge: (context) =>
|
||||
context.outstandingRequestTotal > 0 ? { value: context.outstandingRequestTotal, label: context.outstandingRequestLabel, tone: 'accent' } : null,
|
||||
},
|
||||
@@ -203,6 +206,7 @@ export function useSidebarNav(getOutstandingRequestTotal: () => number = () => 0
|
||||
const { counts: browseCounts } = useBrowseCounts()
|
||||
const { libraries } = useLibraries()
|
||||
const { mode } = useMediaMode()
|
||||
const { showBookRequests } = useBookRequestVisibility()
|
||||
|
||||
const context = computed<NavContext>(() => {
|
||||
const outstandingRequestTotal = getOutstandingRequestTotal()
|
||||
@@ -212,6 +216,7 @@ export function useSidebarNav(getOutstandingRequestTotal: () => number = () => 0
|
||||
|
||||
return {
|
||||
hasPermission,
|
||||
showBookRequests: showBookRequests.value,
|
||||
bookDockTotal: bookDockSummary.value.total,
|
||||
outstandingRequestTotal,
|
||||
outstandingRequestLabel: t(outstandingRequestLabelKey, { count: outstandingRequestTotal }),
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ref } from 'vue'
|
||||
import { useBookRequestVisibility } from './useBookRequestVisibility'
|
||||
|
||||
const state = vi.hoisted(() => ({
|
||||
user: null as { id: number; settings: { showBookRequests?: boolean } } | null,
|
||||
api: vi.fn<() => Promise<{ ok: boolean }>>(),
|
||||
fetchNotifications: vi.fn<() => Promise<void>>(),
|
||||
fetchUnreadCount: vi.fn<() => Promise<void>>(),
|
||||
}))
|
||||
|
||||
vi.mock('@/features/auth/composables/useAuth', () => ({ useAuth: () => ({ user: ref(state.user) }) }))
|
||||
vi.mock('@/lib/api', () => ({ api: state.api }))
|
||||
vi.mock('@/features/notifications/composables/useNotifications', () => ({
|
||||
useNotifications: () => ({ fetchNotifications: state.fetchNotifications, fetchUnreadCount: state.fetchUnreadCount }),
|
||||
}))
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
state.user = { id: 7, settings: {} }
|
||||
state.api.mockResolvedValue({ ok: true })
|
||||
})
|
||||
|
||||
describe('useBookRequestVisibility', () => {
|
||||
it('shows Requests when the preference is absent, including older server responses', () => {
|
||||
expect(useBookRequestVisibility().showBookRequests.value).toBe(true)
|
||||
})
|
||||
|
||||
it('saves only the new setting and updates the current account after success', async () => {
|
||||
const visibility = useBookRequestVisibility()
|
||||
expect(await visibility.setShowBookRequests(false)).toBe(true)
|
||||
expect(state.api).toHaveBeenCalledWith('/api/v1/users/me/settings', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ settings: { showBookRequests: false } }),
|
||||
})
|
||||
expect(visibility.showBookRequests.value).toBe(false)
|
||||
expect(state.fetchNotifications).toHaveBeenCalledWith(true)
|
||||
expect(state.fetchUnreadCount).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('leaves visibility unchanged when the save fails', async () => {
|
||||
state.api.mockResolvedValue({ ok: false })
|
||||
const visibility = useBookRequestVisibility()
|
||||
expect(await visibility.setShowBookRequests(false)).toBe(false)
|
||||
expect(visibility.showBookRequests.value).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { useAuth } from '@/features/auth/composables/useAuth'
|
||||
import { api } from '@/lib/api'
|
||||
import { useNotifications } from '@/features/notifications/composables/useNotifications'
|
||||
|
||||
const saving = ref(false)
|
||||
|
||||
export function useBookRequestVisibility() {
|
||||
const { user } = useAuth()
|
||||
const showBookRequests = computed(() => user.value?.settings?.showBookRequests !== false)
|
||||
|
||||
async function setShowBookRequests(show: boolean): Promise<boolean> {
|
||||
if (!user.value || saving.value) return false
|
||||
if (showBookRequests.value === show) return true
|
||||
|
||||
const userId = user.value.id
|
||||
saving.value = true
|
||||
try {
|
||||
const response = await api('/api/v1/users/me/settings', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ settings: { showBookRequests: show } }),
|
||||
})
|
||||
if (!response.ok || user.value?.id !== userId) return false
|
||||
user.value = { ...user.value, settings: { ...user.value.settings, showBookRequests: show } }
|
||||
const { fetchNotifications, fetchUnreadCount } = useNotifications()
|
||||
void Promise.all([fetchNotifications(true), fetchUnreadCount()])
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return { showBookRequests, saving, setShowBookRequests }
|
||||
}
|
||||
@@ -12,12 +12,14 @@ import SettingsPageHeader from '@/features/settings/SettingsPageHeader.vue'
|
||||
import ToggleSwitch from '@/components/ui/ToggleSwitch.vue'
|
||||
import NotificationLevelSegmented from './NotificationLevelSegmented.vue'
|
||||
import { NOTIFICATION_CATEGORY_GROUPS, NOTIFICATION_CATEGORY_ICONS } from '../lib/notification-category-groups'
|
||||
import { useBookRequestVisibility } from '@/features/book-requests/composables/useBookRequestVisibility'
|
||||
|
||||
const props = withDefaults(defineProps<{ embedded?: boolean }>(), { embedded: false })
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { user, me } = useAuth()
|
||||
const { showBookRequests } = useBookRequestVisibility()
|
||||
const { popupEnabled, setPopupEnabled, loadPrefs } = useWhatsNew()
|
||||
|
||||
const saving = ref(false)
|
||||
@@ -54,8 +56,15 @@ const hasChanges = computed(() => {
|
||||
return NOTIFICATION_CATEGORY_IDS.some((key) => preferences.value[key] !== resolveNotificationLevel(userPrefs?.[key]))
|
||||
})
|
||||
|
||||
const enabledCount = computed(() => NOTIFICATION_CATEGORY_IDS.filter((key) => preferences.value[key] !== NotificationLevel.Off).length)
|
||||
const totalCount = computed(() => NOTIFICATION_CATEGORY_IDS.length)
|
||||
const visibleGroups = computed(() =>
|
||||
NOTIFICATION_CATEGORY_GROUPS.map((group) => ({
|
||||
...group,
|
||||
categories: group.categories.filter((category) => category !== 'bookRequests' || showBookRequests.value),
|
||||
})).filter((group) => group.categories.length > 0),
|
||||
)
|
||||
const visibleCategoryIds = computed(() => visibleGroups.value.flatMap((group) => group.categories))
|
||||
const enabledCount = computed(() => visibleCategoryIds.value.filter((key) => preferences.value[key] !== NotificationLevel.Off).length)
|
||||
const totalCount = computed(() => visibleCategoryIds.value.length)
|
||||
|
||||
function categoryLabel(category: NotificationCategory): string {
|
||||
return t(`notifications.preferences.categories.${category}.label`)
|
||||
@@ -126,7 +135,7 @@ async function handleSave() {
|
||||
</div>
|
||||
|
||||
<div class="space-y-4" :class="{ 'mt-5 md:mt-0': !props.embedded }">
|
||||
<section v-for="group in NOTIFICATION_CATEGORY_GROUPS" :key="group.id" :aria-labelledby="`notification-group-${group.id}`" class="space-y-2">
|
||||
<section v-for="group in visibleGroups" :key="group.id" :aria-labelledby="`notification-group-${group.id}`" class="space-y-2">
|
||||
<div class="flex items-baseline justify-between gap-3">
|
||||
<h2 :id="`notification-group-${group.id}`" class="settings-group-label mb-0">
|
||||
{{ t(`notifications.preferences.groups.${group.id}`) }}
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { BookOpen, BookText } from '@lucide/vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
import ToggleSwitch from '@/components/ui/ToggleSwitch.vue'
|
||||
import { useDisplaySettings, type BookThumbnailClickAction } from '@/composables/useDisplaySettings'
|
||||
import { useSeriesCollapsePreference } from '@/features/book/composables/useSeriesCollapsePreference'
|
||||
import { useBookRequestVisibility } from '@/features/book-requests/composables/useBookRequestVisibility'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { smartScopeFilterExpanded, thumbnailClickAction } = useDisplaySettings()
|
||||
const { prefs, setPreference } = useSeriesCollapsePreference()
|
||||
const { showBookRequests, saving: savingBookRequestVisibility, setShowBookRequests } = useBookRequestVisibility()
|
||||
|
||||
const globalCollapseEnabled = computed(() => prefs.value?.global ?? false)
|
||||
|
||||
@@ -39,6 +42,10 @@ async function handleGlobalCollapseToggle(value: boolean) {
|
||||
await setPreference('global', value)
|
||||
}
|
||||
|
||||
async function handleBookRequestVisibilityToggle(value: boolean) {
|
||||
if (!(await setShowBookRequests(value))) toast.error(t('settings.appearance.behavior.bookRequests.saveFailed'))
|
||||
}
|
||||
|
||||
function setThumbnailClickAction(action: BookThumbnailClickAction) {
|
||||
thumbnailClickAction.value = action
|
||||
}
|
||||
@@ -100,6 +107,18 @@ function setThumbnailClickAction(action: BookThumbnailClickAction) {
|
||||
</div>
|
||||
<ToggleSwitch :model-value="globalCollapseEnabled" @update:model-value="handleGlobalCollapseToggle" />
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 px-4 py-3 md:px-5 md:py-3.5 bg-card">
|
||||
<div class="min-w-0">
|
||||
<p class="settings-label">{{ t('settings.appearance.behavior.bookRequests.label') }}</p>
|
||||
<p class="settings-hint">{{ t('settings.appearance.behavior.bookRequests.hint') }}</p>
|
||||
</div>
|
||||
<ToggleSwitch
|
||||
:model-value="showBookRequests"
|
||||
:disabled="savingBookRequestVisibility"
|
||||
:aria-label="t('settings.appearance.behavior.bookRequests.label')"
|
||||
@update:model-value="handleBookRequestVisibilityToggle"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -57,6 +57,13 @@ describe('visibleSettingsNav', () => {
|
||||
expect(visibleSettingsNav(SUPERUSER).map((group) => group.id)).toEqual(['you', 'library', 'devices', 'accounts', 'server'])
|
||||
})
|
||||
|
||||
it('hides only request administration when Requests are hidden', () => {
|
||||
const normal = visibleSettingsNav(SUPERUSER).flatMap((group) => group.items.map((item) => item.id))
|
||||
const hidden = visibleSettingsNav({ ...SUPERUSER, showBookRequests: false }).flatMap((group) => group.items.map((item) => item.id))
|
||||
expect(normal).toContain('requests')
|
||||
expect(hidden).toEqual(normal.filter((id) => id !== 'requests'))
|
||||
})
|
||||
|
||||
it('hides notifications without notification access', () => {
|
||||
const you = visibleSettingsNav(NOBODY)[0]
|
||||
expect(you?.items.map((item) => item.id)).not.toContain('notifications')
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { ArrowLeft, ChevronRight, Search, X } from '@lucide/vue'
|
||||
import { useModifierKey } from '@/composables/useModifierKey'
|
||||
import { usePermissions } from '@/features/auth/composables/usePermissions'
|
||||
import { useBookRequestVisibility } from '@/features/book-requests/composables/useBookRequestVisibility'
|
||||
import { useSettingsNavStatus } from '../composables/useSettingsNavStatus'
|
||||
import { visibleSettingsNav, type SettingsNavGroup, type SettingsNavItem } from '../lib/settings-nav'
|
||||
interface SearchHit {
|
||||
@@ -18,6 +19,7 @@ const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { isSuperuser, userPermissions, isDemoRestrictedAccount } = usePermissions()
|
||||
const { showBookRequests } = useBookRequestVisibility()
|
||||
const { isMac, modifierKey } = useModifierKey()
|
||||
|
||||
/** Apple writes the combo as one glyph pair, everyone else separates the two keys. */
|
||||
@@ -31,6 +33,7 @@ const groups = computed(() =>
|
||||
isSuperuser: isSuperuser.value,
|
||||
permissions: userPermissions.value,
|
||||
isDemoRestricted: isDemoRestrictedAccount.value,
|
||||
showBookRequests: showBookRequests.value,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ArrowLeft } from '@lucide/vue'
|
||||
import SidebarSectionPopover from '@/components/sidebar/SidebarSectionPopover.vue'
|
||||
import { usePermissions } from '@/features/auth/composables/usePermissions'
|
||||
import { useBookRequestVisibility } from '@/features/book-requests/composables/useBookRequestVisibility'
|
||||
import { visibleSettingsNav, type SettingsNavItem } from '../lib/settings-nav'
|
||||
import SettingsNav from './SettingsNav.vue'
|
||||
|
||||
@@ -13,12 +14,14 @@ withDefaults(defineProps<{ isRail?: boolean }>(), { isRail: false })
|
||||
const { t } = useI18n()
|
||||
const route = useRoute()
|
||||
const { isSuperuser, userPermissions, isDemoRestrictedAccount } = usePermissions()
|
||||
const { showBookRequests } = useBookRequestVisibility()
|
||||
|
||||
const groups = computed(() =>
|
||||
visibleSettingsNav({
|
||||
isSuperuser: isSuperuser.value,
|
||||
permissions: userPermissions.value,
|
||||
isDemoRestricted: isDemoRestrictedAccount.value,
|
||||
showBookRequests: showBookRequests.value,
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export interface SettingsNavContext {
|
||||
isSuperuser: boolean
|
||||
permissions: readonly string[]
|
||||
isDemoRestricted: boolean
|
||||
showBookRequests?: boolean
|
||||
}
|
||||
|
||||
/** Live signals the rail renders beside a row. Resolved by the nav, not stored in this file. */
|
||||
@@ -496,7 +497,7 @@ export const SETTINGS_NAV: readonly SettingsNavGroup[] = [
|
||||
descriptionKey: 'settings.nav.descriptions.requests',
|
||||
icon: DownloadCloud,
|
||||
keywords: 'request download client qbittorrent torrent magnet path mapping hardlink',
|
||||
isVisible: anyPermission(Permission.ManageAppSettings),
|
||||
isVisible: (context) => context.showBookRequests !== false && anyPermission(Permission.ManageAppSettings)(context),
|
||||
},
|
||||
{
|
||||
id: 'admin-tts',
|
||||
|
||||
@@ -428,6 +428,11 @@
|
||||
"collapseSeries": {
|
||||
"label": "Collapse series by default",
|
||||
"hint": "Group books in the same series into a single card in library and collection views"
|
||||
},
|
||||
"bookRequests": {
|
||||
"label": "Show Book Requests",
|
||||
"hint": "Show request pages, prompts, and settings in this account. Existing requests keep running when hidden.",
|
||||
"saveFailed": "Could not update Book Requests visibility"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
|
||||
@@ -51,6 +51,10 @@ function makeRouter() {
|
||||
{ path: '/setup', name: 'setup', component: blank, meta: { public: true } },
|
||||
{ path: '/forgot-password', name: 'forgot-password', component: blank, meta: { public: true } },
|
||||
{ path: '/reset-password', name: 'reset-password', component: blank, meta: { public: true } },
|
||||
{ path: '/requests', name: 'book-requests', component: blank },
|
||||
{ path: '/requests/:id', name: 'book-request-detail', component: blank },
|
||||
{ path: '/settings/admin/requests', name: 'settings-admin-requests', component: blank },
|
||||
{ path: '/settings/appearance/behavior', name: 'settings-appearance-behavior', component: blank },
|
||||
],
|
||||
})
|
||||
registerAuthGuard(router)
|
||||
@@ -65,6 +69,22 @@ beforeEach(() => {
|
||||
statusState.passwordLoginEnabled = true
|
||||
})
|
||||
|
||||
describe('auth guard: hidden book requests', () => {
|
||||
it.each(['/requests', '/requests/42', '/settings/admin/requests'])('redirects %s to the visibility setting', async (path) => {
|
||||
authState.user = { id: 1, isDefaultPassword: false, permissions: [], settings: { showBookRequests: false } }
|
||||
const router = makeRouter()
|
||||
await router.push(path)
|
||||
expect(router.currentRoute.value.name).toBe('settings-appearance-behavior')
|
||||
})
|
||||
|
||||
it('preserves existing request links when the preference is absent', async () => {
|
||||
authState.user = { id: 1, isDefaultPassword: false, permissions: [], settings: {} }
|
||||
const router = makeRouter()
|
||||
await router.push('/requests')
|
||||
expect(router.currentRoute.value.name).toBe('book-requests')
|
||||
})
|
||||
})
|
||||
|
||||
describe('auth guard: /register', () => {
|
||||
it('redirects a visitor to /login when self-registration is closed', async () => {
|
||||
const router = makeRouter()
|
||||
|
||||
@@ -72,6 +72,16 @@ export function registerAuthGuard(router: Router): void {
|
||||
return { name: 'settings-account-profile' }
|
||||
}
|
||||
|
||||
if (
|
||||
user.value.settings.showBookRequests === false &&
|
||||
(to.name === 'book-requests' ||
|
||||
to.name === 'book-request-detail' ||
|
||||
to.name === 'book-request-releases' ||
|
||||
to.name === 'settings-admin-requests')
|
||||
) {
|
||||
return { name: 'settings-appearance-behavior' }
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user