Skip to content

Commit 2cbec7e

Browse files
Wiki fixes (#1668)
* Add authentication to wiki page create/edit requests - Add useSession: true to postPackageWikiPageCreate and postPackageWikiPageEdit - Without session the server returns 403 * Fix wiki page navigation and rendering - Use page ID instead of slug in WikiPage loader - Add key prop to the component to re-render on navigation - Fix previous/next page navigation logic - Add missing return statement in WikiFirstPage component - Add empty pages array check in WikiFirstPage * Fix disabled button styling for wiki navigation - Add disabled prop to disabled navigation buttons in WikiContent - Add CSS support for aria-disabled attribute in Button.css * Improve error handling and fix the timestamps on the wiki page * Remove unnecessary promise wrapping * Enable the WikiPage's Edit button by hooking up the permissions
1 parent a6eae79 commit 2cbec7e

File tree

5 files changed

+193
-107
lines changed

5 files changed

+193
-107
lines changed

apps/cyberstorm-remix/app/p/tabs/Wiki/WikiContent.tsx

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import "./Wiki.css";
22

33
import { type PackageWikiPageResponseData } from "@thunderstore/thunderstore-api";
4-
import { Heading, NewButton, NewIcon } from "@thunderstore/cyberstorm";
4+
import {
5+
Heading,
6+
NewButton,
7+
NewIcon,
8+
RelativeTime,
9+
} from "@thunderstore/cyberstorm";
510
import {
611
faArrowLeftLong,
712
faArrowRightLong,
@@ -42,10 +47,19 @@ export const WikiContent = memo(function WikiContent({
4247
<NewIcon csMode="inline" noWrapper>
4348
<FontAwesomeIcon icon={faCalendarDay} />
4449
</NewIcon>
45-
{page.datetime_created}
50+
<RelativeTime
51+
time={page.datetime_created}
52+
suppressHydrationWarning
53+
/>
4654
</span>
4755
<span>
48-
<i>Updated {page.datetime_updated} ago</i>
56+
<i>
57+
Updated{" "}
58+
<RelativeTime
59+
time={page.datetime_updated}
60+
suppressHydrationWarning
61+
/>
62+
</i>
4963
</span>
5064
</div>
5165
</div>
@@ -101,7 +115,11 @@ export const WikiContent = memo(function WikiContent({
101115
Previous Page
102116
</NewButton>
103117
) : (
104-
<NewButton csModifiers={["ghost", "disabled"]} csVariant="secondary">
118+
<NewButton
119+
csModifiers={["ghost", "disabled"]}
120+
csVariant="secondary"
121+
disabled={true}
122+
>
105123
<NewIcon csMode="inline" noWrapper>
106124
<FontAwesomeIcon icon={faArrowLeftLong} />
107125
</NewIcon>
@@ -125,7 +143,11 @@ export const WikiContent = memo(function WikiContent({
125143
</NewIcon>
126144
</NewButton>
127145
) : (
128-
<NewButton csModifiers={["ghost", "disabled"]} csVariant="secondary">
146+
<NewButton
147+
csModifiers={["ghost", "disabled"]}
148+
csVariant="secondary"
149+
disabled={true}
150+
>
129151
Next Page
130152
<NewIcon csMode="inline" noWrapper>
131153
<FontAwesomeIcon icon={faArrowRightLong} />

apps/cyberstorm-remix/app/p/tabs/Wiki/WikiFirstPage.tsx

Lines changed: 70 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,26 @@ export async function loader({ params }: LoaderFunctionArgs) {
4949
params.namespaceId,
5050
params.packageId
5151
);
52-
const firstPage = dapper.getPackageWikiPage(wiki.pages[0].id);
53-
result = {
54-
wiki: wiki,
55-
firstPage: firstPage,
56-
communityId: params.communityId,
57-
namespaceId: params.namespaceId,
58-
packageId: params.packageId,
59-
permissions: undefined,
60-
};
52+
if (wiki.pages && wiki.pages.length > 0) {
53+
const firstPage = dapper.getPackageWikiPage(wiki.pages[0].id);
54+
result = {
55+
wiki: wiki,
56+
firstPage: firstPage,
57+
communityId: params.communityId,
58+
namespaceId: params.namespaceId,
59+
packageId: params.packageId,
60+
permissions: undefined,
61+
};
62+
} else {
63+
result = {
64+
wiki: wiki,
65+
firstPage: undefined,
66+
communityId: params.communityId,
67+
namespaceId: params.namespaceId,
68+
packageId: params.packageId,
69+
permissions: undefined,
70+
};
71+
}
6172
} catch (error) {
6273
if (isApiError(error)) {
6374
// There is no wiki or the User does not have permission to view the wiki, return empty wiki and undefined firstPage
@@ -113,15 +124,26 @@ export async function clientLoader({ params }: LoaderFunctionArgs) {
113124
params.namespaceId,
114125
params.packageId
115126
);
116-
const firstPage = dapper.getPackageWikiPage(wiki.pages[0].id);
117-
result = {
118-
wiki: wiki,
119-
firstPage: firstPage,
120-
communityId: params.communityId,
121-
namespaceId: params.namespaceId,
122-
packageId: params.packageId,
123-
permissions: permissions,
124-
};
127+
if (wiki.pages && wiki.pages.length > 0) {
128+
const firstPage = dapper.getPackageWikiPage(wiki.pages[0].id);
129+
result = {
130+
wiki: wiki,
131+
firstPage: firstPage,
132+
communityId: params.communityId,
133+
namespaceId: params.namespaceId,
134+
packageId: params.packageId,
135+
permissions: permissions,
136+
};
137+
} else {
138+
result = {
139+
wiki: wiki,
140+
firstPage: undefined,
141+
communityId: params.communityId,
142+
namespaceId: params.namespaceId,
143+
packageId: params.packageId,
144+
permissions: permissions,
145+
};
146+
}
125147
} catch (error) {
126148
if (isApiError(error)) {
127149
// There is no wiki or the User does not have permission to view the wiki, return empty wiki and undefined firstPage
@@ -152,33 +174,37 @@ export default function WikiFirstPage() {
152174
useLoaderData<typeof loader | typeof clientLoader>();
153175

154176
const wikiAndFirstPageMemo = useMemo(
155-
() => Promise.all([wiki, firstPage]),
177+
() => Promise.all([Promise.resolve(wiki), firstPage]),
156178
[wiki, firstPage]
157179
);
158180

159-
<Suspense fallback={<SkeletonBox className="package-wiki__skeleton" />}>
160-
<Await resolve={wikiAndFirstPageMemo}>
161-
{(resolvedValue) => {
162-
const [wiki, firstPage] = resolvedValue;
163-
if (wiki && firstPage) {
164-
return (
165-
<WikiContent
166-
page={firstPage}
167-
communityId={communityId}
168-
namespaceId={namespaceId}
169-
packageId={packageId}
170-
previousPage={undefined}
171-
nextPage={wiki.pages.length > 1 ? wiki.pages[1].slug : undefined}
172-
canManage={permissions?.then((perms) =>
173-
typeof perms === "undefined"
174-
? false
175-
: perms.permissions.can_manage
176-
)}
177-
/>
178-
);
179-
}
180-
return <>There are no wiki pages available.</>;
181-
}}
182-
</Await>
183-
</Suspense>;
181+
return (
182+
<Suspense fallback={<SkeletonBox className="package-wiki__skeleton" />}>
183+
<Await resolve={wikiAndFirstPageMemo}>
184+
{(resolvedValue) => {
185+
const [wiki, firstPage] = resolvedValue;
186+
if (wiki && firstPage) {
187+
return (
188+
<WikiContent
189+
page={firstPage}
190+
communityId={communityId}
191+
namespaceId={namespaceId}
192+
packageId={packageId}
193+
previousPage={undefined}
194+
nextPage={
195+
wiki.pages.length > 1 ? wiki.pages[1].slug : undefined
196+
}
197+
canManage={permissions?.then((perms) =>
198+
typeof perms === "undefined"
199+
? false
200+
: perms.permissions.can_manage
201+
)}
202+
/>
203+
);
204+
}
205+
return <>There are no wiki pages available.</>;
206+
}}
207+
</Await>
208+
</Suspense>
209+
);
184210
}

apps/cyberstorm-remix/app/p/tabs/Wiki/WikiPage.tsx

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import "./Wiki.css";
22

3-
import { Await, type LoaderFunctionArgs } from "react-router";
3+
import { Await, type LoaderFunctionArgs, useParams } from "react-router";
44
import { useLoaderData } from "react-router";
55
import { DapperTs } from "@thunderstore/dapper-ts";
66
import {
@@ -14,11 +14,11 @@ import {
1414
getPackagePermissions,
1515
} from "@thunderstore/dapper-ts/src/methods/package";
1616
import { isApiError } from "../../../../../../packages/thunderstore-api/src";
17-
import { Suspense, useMemo } from "react";
17+
import { Suspense } from "react";
1818
import { SkeletonBox } from "@thunderstore/cyberstorm";
1919

2020
type ResultType = {
21-
wiki: ReturnType<typeof getPackageWiki> | undefined;
21+
wiki: Awaited<ReturnType<typeof getPackageWiki>> | undefined;
2222
page: ReturnType<typeof getPackageWikiPage> | undefined;
2323
communityId: string;
2424
namespaceId: string;
@@ -51,8 +51,23 @@ export async function loader({ params }: LoaderFunctionArgs) {
5151
};
5252

5353
try {
54-
const wiki = dapper.getPackageWiki(params.namespaceId, params.packageId);
55-
const page = dapper.getPackageWikiPage(params.slug);
54+
const wiki = await dapper.getPackageWiki(
55+
params.namespaceId,
56+
params.packageId
57+
);
58+
const pageId = wiki.pages?.find((p) => p.slug === params.slug)?.id;
59+
if (!pageId) {
60+
result = {
61+
wiki,
62+
page: undefined,
63+
communityId: params.communityId,
64+
namespaceId: params.namespaceId,
65+
packageId: params.packageId,
66+
permissions: result.permissions,
67+
};
68+
return result;
69+
}
70+
const page = dapper.getPackageWikiPage(pageId);
5671
result = {
5772
wiki: wiki,
5873
page: page,
@@ -117,8 +132,23 @@ export async function clientLoader({ params }: LoaderFunctionArgs) {
117132
};
118133

119134
try {
120-
const wiki = dapper.getPackageWiki(params.namespaceId, params.packageId);
121-
const page = dapper.getPackageWikiPage(params.slug);
135+
const wiki = await dapper.getPackageWiki(
136+
params.namespaceId,
137+
params.packageId
138+
);
139+
const pageId = wiki.pages?.find((p) => p.slug === params.slug)?.id;
140+
if (!pageId) {
141+
result = {
142+
wiki,
143+
page: undefined,
144+
communityId: params.communityId,
145+
namespaceId: params.namespaceId,
146+
packageId: params.packageId,
147+
permissions: result.permissions,
148+
};
149+
return result;
150+
}
151+
const page = dapper.getPackageWikiPage(pageId);
122152
result = {
123153
wiki: wiki,
124154
page: page,
@@ -153,55 +183,60 @@ export async function clientLoader({ params }: LoaderFunctionArgs) {
153183
}
154184

155185
export default function WikiPage() {
156-
const { wiki, page, communityId, namespaceId, packageId } = useLoaderData<
157-
typeof loader | typeof clientLoader
158-
>();
159-
160-
const wikiAndPageMemo = useMemo(
161-
() => Promise.all([wiki, page]),
162-
[wiki, page]
163-
);
164-
165-
<Suspense fallback={<SkeletonBox className="package-wiki__skeleton" />}>
166-
<Await
167-
resolve={wikiAndPageMemo}
168-
errorElement={<div>Error occurred while loading wiki page</div>}
169-
>
170-
{(resolvedValue) => {
171-
const [wiki, page] = resolvedValue;
172-
if (wiki && page) {
173-
const currentPageIndex = wiki.pages.findIndex(
174-
(p) => p.id === page.id
175-
);
176-
177-
let previousPage = undefined;
178-
let nextPage = undefined;
179-
180-
if (currentPageIndex === 0) {
181-
previousPage = undefined;
182-
} else {
183-
previousPage = wiki.pages[currentPageIndex - 1]?.slug;
186+
const { wiki, page, communityId, namespaceId, packageId, permissions } =
187+
useLoaderData<typeof loader | typeof clientLoader>();
188+
const params = useParams();
189+
190+
const wikiAndPagePromise = Promise.all([wiki, page]);
191+
192+
const notFoundElement = <div>Wiki page not found.</div>;
193+
194+
return (
195+
<Suspense fallback={<SkeletonBox className="package-wiki__skeleton" />}>
196+
<Await
197+
key={params.slug}
198+
resolve={wikiAndPagePromise}
199+
errorElement={<div>Error occurred while loading wiki page</div>}
200+
>
201+
{(resolvedValue) => {
202+
const [wiki, page] = resolvedValue;
203+
if (wiki && page) {
204+
const currentPageIndex = wiki.pages.findIndex(
205+
(p) => p.id === page.id
206+
);
207+
208+
if (currentPageIndex < 0) {
209+
return notFoundElement;
210+
}
211+
212+
const previousPage =
213+
currentPageIndex > 0
214+
? wiki.pages[currentPageIndex - 1]?.slug
215+
: undefined;
216+
const nextPage =
217+
currentPageIndex < wiki.pages.length - 1
218+
? wiki.pages[currentPageIndex + 1]?.slug
219+
: undefined;
220+
221+
return (
222+
<WikiContent
223+
page={page}
224+
communityId={communityId}
225+
namespaceId={namespaceId}
226+
packageId={packageId}
227+
previousPage={previousPage}
228+
nextPage={nextPage}
229+
canManage={permissions?.then((perms) =>
230+
typeof perms === "undefined"
231+
? false
232+
: perms.permissions.can_manage
233+
)}
234+
/>
235+
);
184236
}
185-
186-
if (currentPageIndex === wiki.pages.length) {
187-
nextPage = undefined;
188-
} else {
189-
nextPage = wiki.pages[currentPageIndex + 1]?.slug;
190-
}
191-
192-
return (
193-
<WikiContent
194-
page={page}
195-
communityId={communityId}
196-
namespaceId={namespaceId}
197-
packageId={packageId}
198-
previousPage={previousPage}
199-
nextPage={nextPage}
200-
/>
201-
);
202-
}
203-
return <>Wiki Page Not Found</>;
204-
}}
205-
</Await>
206-
</Suspense>;
237+
return notFoundElement;
238+
}}
239+
</Await>
240+
</Suspense>
241+
);
207242
}

packages/cyberstorm-theme/src/components/Button/Button.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@
224224
background: transparent;
225225
}
226226

227-
.button[disabled] {
227+
.button[disabled],
228+
.button[aria-disabled="true"] {
228229
opacity: 0.5;
229230
pointer-events: none;
230231
}

0 commit comments

Comments
 (0)