feat: public page sharing (#1012)

* Share - WIP

* - public attachment links
- WIP

* WIP

* WIP

* Share - WIP

* WIP

* WIP

* include userRole in space object

* WIP

* Server render shared page meta tags

* disable user select

* Close Navbar on outside click on mobile

* update shared page spaceId

* WIP

* fix

* close sidebar on click

* close sidebar

* defaults

* update copy

* Store share key in lowercase

* refactor page breadcrumbs

* Change copy

* add link ref

* open link button

* add meta og:title

* add twitter tags

* WIP

* make shares/info endpoint public

* fix

* * add /p/ segment to share urls
* minore fixes

* change mobile breadcrumb icon
This commit is contained in:
Philip Okugbe
2025-04-22 20:37:32 +01:00
committed by GitHub
parent 3e8824435d
commit 6c422011ac
66 changed files with 3331 additions and 512 deletions

View File

@ -0,0 +1,31 @@
import SettingsTitle from "@/components/settings/settings-title.tsx";
import { Helmet } from "react-helmet-async";
import { getAppName } from "@/lib/config.ts";
import { useTranslation } from "react-i18next";
import ShareList from "@/features/share/components/share-list.tsx";
import { Alert, Text } from "@mantine/core";
import { IconInfoCircle } from "@tabler/icons-react";
import React from "react";
export default function Shares() {
const { t } = useTranslation();
return (
<>
<Helmet>
<title>
{t("Public sharing")} - {getAppName()}
</title>
</Helmet>
<SettingsTitle title={t("Public sharing")} />
<Alert variant="light" color="blue" icon={<IconInfoCircle />}>
{t(
"Publicly shared pages from spaces you are a member of will appear here",
)}
</Alert>
<ShareList />
</>
);
}

View File

@ -0,0 +1,35 @@
import { useNavigate, useParams } from "react-router-dom";
import { useEffect } from "react";
import { buildSharedPageUrl } from "@/features/page/page.utils.ts";
import { Error404 } from "@/components/ui/error-404.tsx";
import { useGetShareByIdQuery } from "@/features/share/queries/share-query.ts";
export default function ShareRedirect() {
const { shareId } = useParams();
const navigate = useNavigate();
const { data: share, isLoading, isError } = useGetShareByIdQuery(shareId);
useEffect(() => {
if (share) {
navigate(
buildSharedPageUrl({
shareId: share.key,
pageSlugId: share?.sharedPage.slugId,
pageTitle: share?.sharedPage.title,
}),
{ replace: true },
);
}
}, [isLoading, share]);
if (isError) {
return <Error404 />;
}
if (isLoading) {
return <></>;
}
return null;
}

View File

@ -0,0 +1,58 @@
import { useNavigate, useParams } from "react-router-dom";
import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next";
import { useSharePageQuery } from "@/features/share/queries/share-query.ts";
import { Container } from "@mantine/core";
import React, { useEffect } from "react";
import ReadonlyPageEditor from "@/features/editor/readonly-page-editor.tsx";
import { extractPageSlugId } from "@/lib";
import { Error404 } from "@/components/ui/error-404.tsx";
export default function SingleSharedPage() {
const { t } = useTranslation();
const { pageSlug } = useParams();
const { shareId } = useParams();
const navigate = useNavigate();
const { data, isLoading, isError, error } = useSharePageQuery({
pageId: extractPageSlugId(pageSlug),
});
useEffect(() => {
if (shareId && data) {
if (data.share.key !== shareId) {
navigate(`/share/${data.share.key}/p/${pageSlug}`, { replace: true });
}
}
}, [shareId, data]);
if (isLoading) {
return <></>;
}
if (isError || !data) {
if ([401, 403, 404].includes(error?.["status"])) {
return <Error404 />;
}
return <div>{t("Error fetching page data.")}</div>;
}
return (
<div>
<Helmet>
<title>{`${data?.page?.title || t("untitled")}`}</title>
{!data?.share.searchIndexing && (
<meta name="robots" content="noindex" />
)}
</Helmet>
<Container size={900} p={0}>
<ReadonlyPageEditor
key={data.page.id}
title={data.page.title}
content={data.page.content}
/>
</Container>
</div>
);
}