Add option to choose default TOC visibility on public shares (#10283)

* Add show TOC option

* Revert copy change
This commit is contained in:
Tom Moor
2025-10-02 12:53:56 +02:00
committed by GitHub
parent 1aa05b797c
commit fce02996f9
11 changed files with 118 additions and 4 deletions
@@ -71,6 +71,19 @@ function InnerPublicAccess({ collection, share }: Props) {
[share]
);
const handleShowTOCChanged = useCallback(
async (checked: boolean) => {
try {
await share?.save({
showTOC: checked,
});
} catch (err) {
toast.error(err.message);
}
},
[share]
);
const handlePublishedChange = useCallback(
async (checked: boolean) => {
try {
@@ -204,6 +217,31 @@ function InnerPublicAccess({ collection, share }: Props) {
/>
}
/>
<ListItem
title={
<Text type="tertiary" as={Flex}>
{t("Show table of contents")}&nbsp;
<Tooltip
content={t(
"Display the table of contents on documents by default"
)}
>
<NudeButton size={18}>
<QuestionMarkIcon size={18} />
</NudeButton>
</Tooltip>
</Text>
}
actions={
<Switch
aria-label={t("Show table of contents")}
checked={share?.showTOC ?? false}
onChange={handleShowTOCChanged}
width={26}
height={14}
/>
}
/>
<ShareLinkInput
type="text"
ref={inputRef}
@@ -77,6 +77,19 @@ function PublicAccess({ document, share, sharedParent }: Props) {
[share]
);
const handleShowTOCChanged = React.useCallback(
async (checked: boolean) => {
try {
await share?.save({
showTOC: checked,
});
} catch (err) {
toast.error(err.message);
}
},
[share]
);
const handlePublishedChange = React.useCallback(
async (checked: boolean) => {
try {
@@ -241,6 +254,31 @@ function PublicAccess({ document, share, sharedParent }: Props) {
/>
}
/>
<ListItem
title={
<Text type="tertiary" as={Flex}>
{t("Show table of contents")}&nbsp;
<Tooltip
content={t(
"Display the table of contents on documents by default"
)}
>
<NudeButton size={18}>
<QuestionMarkIcon size={18} />
</NudeButton>
</Tooltip>
</Text>
}
actions={
<Switch
aria-label={t("Show table of contents")}
checked={share?.showTOC ?? false}
onChange={handleShowTOCChanged}
width={26}
height={14}
/>
}
/>
</>
)}
+5
View File
@@ -22,6 +22,7 @@ import { SharedCollectionLink } from "./components/SharedCollectionLink";
import { SharedDocumentLink } from "./components/SharedDocumentLink";
import SidebarButton from "./components/SidebarButton";
import ToggleButton from "./components/ToggleButton";
import { useEffect } from "react";
type Props = {
share: Share;
@@ -37,6 +38,10 @@ function SharedSidebar({ share }: Props) {
const rootNode = share.tree;
const shareId = share.urlId || share.id;
useEffect(() => {
ui.tocVisible = share.showTOC;
}, []);
if (!rootNode?.children.length) {
return null;
}
+4
View File
@@ -75,6 +75,10 @@ class Share extends Model implements Searchable {
@observable
showLastUpdated: boolean;
@Field
@observable
showTOC: boolean;
@observable
views: number;
+3 -3
View File
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { EditorStyleHelper } from "@shared/editor/styles/EditorStyleHelper";
import { depths, s } from "@shared/styles";
import { depths, hideScrollbars, s } from "@shared/styles";
import { useDocumentContext } from "~/components/DocumentContext";
import useWindowScrollPosition from "~/hooks/useWindowScrollPosition";
import { decodeURIComponentSafe } from "~/utils/urls";
@@ -78,16 +78,16 @@ function Contents() {
const StickyWrapper = styled.div`
display: none;
position: sticky;
top: 90px;
max-height: calc(100vh - 90px);
width: ${EditorStyleHelper.tocWidth}px;
${hideScrollbars()}
padding: 0 16px;
overflow-y: auto;
border-radius: 8px;
background: ${s("background")};
@supports (backdrop-filter: blur(20px)) {
@@ -0,0 +1,15 @@
"use strict";
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn("shares", "showTOC", {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
});
},
async down(queryInterface) {
await queryInterface.removeColumn("shares", "showTOC");
},
};
+4
View File
@@ -150,6 +150,10 @@ class Share extends IdModel<
@Column
showLastUpdated: boolean;
@Default(false)
@Column
showTOC: boolean;
// hooks
@BeforeUpdate
+1
View File
@@ -17,6 +17,7 @@ export default function presentShare(share: Share, isAdmin = false) {
includeChildDocuments: share.includeChildDocuments,
allowIndexing: share.allowIndexing,
showLastUpdated: share.showLastUpdated,
showTOC: share.showTOC,
lastAccessedAt: share.lastAccessedAt || undefined,
views: share.views || 0,
domain: share.domain,
+2
View File
@@ -54,6 +54,7 @@ export const SharesUpdateSchema = BaseSchema.extend({
published: z.boolean().optional(),
allowIndexing: z.boolean().optional(),
showLastUpdated: z.boolean().optional(),
showTOC: z.boolean().optional(),
urlId: z
.string()
.regex(UrlHelper.SHARE_URL_SLUG_REGEX, {
@@ -73,6 +74,7 @@ export const SharesCreateSchema = BaseSchema.extend({
published: z.boolean().default(false),
allowIndexing: z.boolean().optional(),
showLastUpdated: z.boolean().optional(),
showTOC: z.boolean().optional(),
urlId: z
.string()
.regex(UrlHelper.SHARE_URL_SLUG_REGEX, {
+6 -1
View File
@@ -238,6 +238,7 @@ router.post(
includeChildDocuments,
allowIndexing,
showLastUpdated,
showTOC,
} = ctx.input.body;
const { user } = ctx.state.auth;
authorize(user, "createShare", user.team);
@@ -274,6 +275,7 @@ router.post(
includeChildDocuments,
allowIndexing,
showLastUpdated,
showTOC,
urlId,
},
});
@@ -303,6 +305,7 @@ router.post(
urlId,
allowIndexing,
showLastUpdated,
showTOC,
} = ctx.input.body;
const { user } = ctx.state.auth;
@@ -333,10 +336,12 @@ router.post(
if (allowIndexing !== undefined) {
share.allowIndexing = allowIndexing;
}
if (showLastUpdated !== undefined) {
share.showLastUpdated = showLastUpdated;
}
if (showTOC !== undefined) {
share.showTOC = showTOC;
}
await share.saveWithCtx(ctx);
@@ -365,6 +365,8 @@
"Disable this setting to discourage search engines from indexing the page": "Disable this setting to discourage search engines from indexing the page",
"Show last modified": "Show last modified",
"Display the last modified timestamp on the shared page": "Display the last modified timestamp on the shared page",
"Show table of contents": "Show table of contents",
"Display the table of contents on documents by default": "Display the table of contents on documents by default",
"All documents in this collection will be shared on the web, including any new documents added later": "All documents in this collection will be shared on the web, including any new documents added later",
"Invite": "Invite",
"{{ userName }} was added to the collection": "{{ userName }} was added to the collection",