@btst/stack 2.11.7 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +2 -6
- package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +5 -9
- package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.cjs +44 -0
- package/dist/packages/stack/src/plugins/blog/client/components/shared/collapsible-tag-list.mjs +42 -0
- package/dist/packages/stack/src/plugins/blog/client/components/shared/tags-list.cjs +2 -8
- package/dist/packages/stack/src/plugins/blog/client/components/shared/tags-list.mjs +2 -8
- package/dist/packages/stack/src/plugins/blog/client/localization/blog-common.cjs +3 -1
- package/dist/packages/stack/src/plugins/blog/client/localization/blog-common.mjs +3 -1
- package/dist/packages/stack/src/plugins/comments/api/query-key-defs.cjs +2 -1
- package/dist/packages/stack/src/plugins/comments/api/query-key-defs.mjs +2 -1
- package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.cjs +6 -2
- package/dist/packages/stack/src/plugins/comments/client/components/comment-thread.mjs +6 -2
- package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.cjs +5 -3
- package/dist/packages/stack/src/plugins/comments/client/hooks/use-comments.mjs +5 -3
- package/dist/plugins/blog/api/index.d.cts +2 -2
- package/dist/plugins/blog/api/index.d.mts +2 -2
- package/dist/plugins/blog/api/index.d.ts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
- package/dist/plugins/blog/client/index.d.cts +4 -2
- package/dist/plugins/blog/client/index.d.mts +4 -2
- package/dist/plugins/blog/client/index.d.ts +4 -2
- package/dist/plugins/blog/query-keys.d.cts +2 -2
- package/dist/plugins/blog/query-keys.d.mts +2 -2
- package/dist/plugins/blog/query-keys.d.ts +2 -2
- package/dist/plugins/comments/api/index.d.cts +4 -4
- package/dist/plugins/comments/api/index.d.mts +4 -4
- package/dist/plugins/comments/api/index.d.ts +4 -4
- package/dist/plugins/comments/client/components/index.d.cts +11 -0
- package/dist/plugins/comments/client/components/index.d.mts +11 -0
- package/dist/plugins/comments/client/components/index.d.ts +11 -0
- package/dist/plugins/comments/client/hooks/index.d.cts +17 -1
- package/dist/plugins/comments/client/hooks/index.d.mts +17 -1
- package/dist/plugins/comments/client/hooks/index.d.ts +17 -1
- package/dist/plugins/comments/client/index.d.cts +9 -0
- package/dist/plugins/comments/client/index.d.mts +9 -0
- package/dist/plugins/comments/client/index.d.ts +9 -0
- package/dist/plugins/comments/query-keys.cjs +1 -0
- package/dist/plugins/comments/query-keys.d.cts +3 -2
- package/dist/plugins/comments/query-keys.d.mts +3 -2
- package/dist/plugins/comments/query-keys.d.ts +3 -2
- package/dist/plugins/comments/query-keys.mjs +1 -0
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/shared/{stack.CLWhMntf.d.cts → stack.04aMRz_W.d.cts} +5 -5
- package/dist/shared/{stack.CJE9sAjV.d.ts → stack.Bq3kiUaM.d.ts} +1 -1
- package/dist/shared/{stack.DNLuzDb3.d.cts → stack.BvuqACtA.d.cts} +3 -3
- package/dist/shared/{stack.C-b3Sn8j.d.cts → stack.C7KNNiJm.d.cts} +3 -1
- package/dist/shared/{stack.C-b3Sn8j.d.mts → stack.C7KNNiJm.d.mts} +3 -1
- package/dist/shared/{stack.C-b3Sn8j.d.ts → stack.C7KNNiJm.d.ts} +3 -1
- package/dist/shared/{stack.Dcz6636A.d.mts → stack.CCncwK6g.d.cts} +1 -1
- package/dist/shared/{stack.CmHRdhl8.d.cts → stack.CO1g47IS.d.mts} +1 -1
- package/dist/shared/{stack.Dfc44JiE.d.mts → stack.Cs8R5HxD.d.mts} +3 -3
- package/dist/shared/{stack.ufcExQIR.d.ts → stack.CxWEN62M.d.ts} +5 -5
- package/dist/shared/{stack.BnSAhKJi.d.ts → stack.DPTRuLUH.d.ts} +3 -3
- package/dist/shared/{stack.DiodAFAa.d.mts → stack.ueI-K4xy.d.mts} +5 -5
- package/package.json +1 -1
- package/src/plugins/blog/client/components/pages/post-page.internal.tsx +4 -21
- package/src/plugins/blog/client/components/shared/collapsible-tag-list.tsx +76 -0
- package/src/plugins/blog/client/components/shared/tags-list.tsx +2 -18
- package/src/plugins/blog/client/localization/blog-common.ts +2 -0
- package/src/plugins/comments/api/query-key-defs.ts +5 -1
- package/src/plugins/comments/client/components/comment-thread.tsx +15 -0
- package/src/plugins/comments/client/hooks/use-comments.tsx +26 -3
- package/src/plugins/comments/client/overrides.ts +10 -0
- package/src/plugins/comments/query-keys.ts +2 -0
- package/dist/shared/{stack.Rtcvl8sS.d.cts → stack.B6S3cgwN.d.cts} +13 -13
- package/dist/shared/{stack.HE_IvqV5.d.mts → stack.Bzfx-_lq.d.mts} +13 -13
- package/dist/shared/{stack.CuhcFsAx.d.cts → stack.DuQOcInS.d.cts} +3 -3
- package/dist/shared/{stack.CuhcFsAx.d.mts → stack.DuQOcInS.d.mts} +3 -3
- package/dist/shared/{stack.CuhcFsAx.d.ts → stack.DuQOcInS.d.ts} +3 -3
- package/dist/shared/{stack.D4Cea8II.d.ts → stack.j5SFLC1d.d.ts} +13 -13
|
@@ -6,7 +6,7 @@ import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
|
|
|
6
6
|
import { DBAdapter } from '@btst/db';
|
|
7
7
|
import * as better_call from 'better-call';
|
|
8
8
|
import { z } from 'zod';
|
|
9
|
-
import { c as createPostSchema, u as updatePostSchema } from './stack.
|
|
9
|
+
import { c as createPostSchema, u as updatePostSchema } from './stack.DuQOcInS.mjs';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Parameters for filtering/paginating posts.
|
|
@@ -325,11 +325,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
325
325
|
slug: string;
|
|
326
326
|
})[] | undefined;
|
|
327
327
|
slug?: string | undefined;
|
|
328
|
-
published?: boolean | undefined;
|
|
329
|
-
publishedAt?: unknown;
|
|
330
328
|
createdAt?: unknown;
|
|
331
329
|
updatedAt?: unknown;
|
|
330
|
+
publishedAt?: unknown;
|
|
332
331
|
image?: string | undefined;
|
|
332
|
+
published?: boolean | undefined;
|
|
333
333
|
}, {
|
|
334
334
|
title: string;
|
|
335
335
|
content: string;
|
|
@@ -342,11 +342,11 @@ declare const blogBackendPlugin: (hooks?: BlogBackendHooks) => _btst_stack_plugi
|
|
|
342
342
|
slug: string;
|
|
343
343
|
})[] | undefined;
|
|
344
344
|
slug?: string | undefined;
|
|
345
|
-
published?: boolean | undefined;
|
|
346
|
-
publishedAt?: unknown;
|
|
347
345
|
createdAt?: unknown;
|
|
348
346
|
updatedAt?: unknown;
|
|
347
|
+
publishedAt?: unknown;
|
|
349
348
|
image?: string | undefined;
|
|
349
|
+
published?: boolean | undefined;
|
|
350
350
|
}>;
|
|
351
351
|
}, Post>;
|
|
352
352
|
readonly updatePost: better_call.StrictEndpoint<"/posts/:id", {} & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { usePluginOverrides
|
|
3
|
+
import { usePluginOverrides } from "@btst/stack/context";
|
|
4
4
|
import { formatDate } from "date-fns";
|
|
5
5
|
import {
|
|
6
6
|
useSuspensePost,
|
|
@@ -12,11 +12,10 @@ import { MarkdownContent } from "../shared/markdown-content";
|
|
|
12
12
|
import { PageHeader } from "../shared/page-header";
|
|
13
13
|
import { PageWrapper } from "../shared/page-wrapper";
|
|
14
14
|
import type { BlogPluginOverrides } from "../../overrides";
|
|
15
|
-
import { DefaultImage
|
|
15
|
+
import { DefaultImage } from "../shared/defaults";
|
|
16
16
|
import { BLOG_LOCALIZATION } from "../../localization";
|
|
17
17
|
import { PostNavigation } from "../shared/post-navigation";
|
|
18
18
|
import { RecentPostsCarousel } from "../shared/recent-posts-carousel";
|
|
19
|
-
import { Badge } from "@workspace/ui/components/badge";
|
|
20
19
|
import { useRouteLifecycle } from "@workspace/ui/hooks/use-route-lifecycle";
|
|
21
20
|
import { OnThisPage, OnThisPageSelect } from "../shared/on-this-page";
|
|
22
21
|
import type { SerializedPost } from "../../../types";
|
|
@@ -24,6 +23,7 @@ import { useRegisterPageAIContext } from "@btst/stack/plugins/ai-chat/client/con
|
|
|
24
23
|
import { WhenVisible } from "@workspace/ui/components/when-visible";
|
|
25
24
|
import { PostNavigationSkeleton } from "../loading/post-navigation-skeleton";
|
|
26
25
|
import { RecentPostsCarouselSkeleton } from "../loading/recent-posts-carousel-skeleton";
|
|
26
|
+
import { CollapsibleTagList } from "../shared/collapsible-tag-list";
|
|
27
27
|
|
|
28
28
|
// Internal component with actual page content
|
|
29
29
|
export function PostPage({ slug }: { slug: string }) {
|
|
@@ -151,29 +151,12 @@ export function PostPage({ slug }: { slug: string }) {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
function PostHeaderTop({ post }: { post: SerializedPost }) {
|
|
154
|
-
const { Link } = usePluginOverrides<
|
|
155
|
-
BlogPluginOverrides,
|
|
156
|
-
Partial<BlogPluginOverrides>
|
|
157
|
-
>("blog", {
|
|
158
|
-
Link: DefaultLink,
|
|
159
|
-
});
|
|
160
|
-
const basePath = useBasePath();
|
|
161
154
|
return (
|
|
162
155
|
<div className="flex flex-row items-center justify-center gap-2 flex-wrap mt-8">
|
|
163
156
|
<span className="font-light text-muted-foreground text-sm">
|
|
164
157
|
{formatDate(post.createdAt, "MMMM d, yyyy")}
|
|
165
158
|
</span>
|
|
166
|
-
{post.tags
|
|
167
|
-
<>
|
|
168
|
-
{post.tags.map((tag) => (
|
|
169
|
-
<Link key={tag.id} href={`${basePath}/blog/tag/${tag.slug}`}>
|
|
170
|
-
<Badge variant="secondary" className="text-xs">
|
|
171
|
-
{tag.name}
|
|
172
|
-
</Badge>
|
|
173
|
-
</Link>
|
|
174
|
-
))}
|
|
175
|
-
</>
|
|
176
|
-
)}
|
|
159
|
+
<CollapsibleTagList tags={post.tags ?? []} />
|
|
177
160
|
</div>
|
|
178
161
|
);
|
|
179
162
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePluginOverrides, useBasePath } from "@btst/stack/context";
|
|
4
|
+
import type { BlogPluginOverrides } from "../../overrides";
|
|
5
|
+
import { DefaultLink } from "./defaults";
|
|
6
|
+
import { Badge } from "@workspace/ui/components/badge";
|
|
7
|
+
import { BLOG_LOCALIZATION } from "../../localization";
|
|
8
|
+
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import type { SerializedTag } from "../../../types";
|
|
11
|
+
|
|
12
|
+
const MAX_VISIBLE_TAGS = 15;
|
|
13
|
+
|
|
14
|
+
interface CollapsibleTagListProps {
|
|
15
|
+
tags: SerializedTag[];
|
|
16
|
+
maxVisible?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function CollapsibleTagList({
|
|
20
|
+
tags,
|
|
21
|
+
maxVisible = MAX_VISIBLE_TAGS,
|
|
22
|
+
}: CollapsibleTagListProps) {
|
|
23
|
+
const { Link, localization } = usePluginOverrides<
|
|
24
|
+
BlogPluginOverrides,
|
|
25
|
+
Partial<BlogPluginOverrides>
|
|
26
|
+
>("blog", {
|
|
27
|
+
Link: DefaultLink,
|
|
28
|
+
localization: BLOG_LOCALIZATION,
|
|
29
|
+
});
|
|
30
|
+
const basePath = useBasePath();
|
|
31
|
+
const [showAll, setShowAll] = useState(false);
|
|
32
|
+
|
|
33
|
+
if (!tags || tags.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const hasMore = tags.length > maxVisible;
|
|
38
|
+
const visibleTags = showAll || !hasMore ? tags : tags.slice(0, maxVisible);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
{visibleTags.map((tag) => (
|
|
43
|
+
<Link key={tag.id} href={`${basePath}/blog/tag/${tag.slug}`}>
|
|
44
|
+
<Badge variant="secondary" className="text-xs">
|
|
45
|
+
{tag.name}
|
|
46
|
+
</Badge>
|
|
47
|
+
</Link>
|
|
48
|
+
))}
|
|
49
|
+
{hasMore && (
|
|
50
|
+
<Badge asChild variant="secondary" className="text-xs cursor-pointer">
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
onClick={() => setShowAll((prev) => !prev)}
|
|
54
|
+
aria-expanded={showAll}
|
|
55
|
+
aria-label={
|
|
56
|
+
showAll
|
|
57
|
+
? localization.BLOG_TAGS_SHOW_LESS
|
|
58
|
+
: localization.BLOG_TAGS_SHOW_ALL
|
|
59
|
+
}
|
|
60
|
+
title={
|
|
61
|
+
showAll
|
|
62
|
+
? localization.BLOG_TAGS_SHOW_LESS
|
|
63
|
+
: localization.BLOG_TAGS_SHOW_ALL
|
|
64
|
+
}
|
|
65
|
+
>
|
|
66
|
+
{showAll ? (
|
|
67
|
+
<ChevronUp aria-hidden="true" />
|
|
68
|
+
) : (
|
|
69
|
+
<ChevronDown aria-hidden="true" />
|
|
70
|
+
)}
|
|
71
|
+
</button>
|
|
72
|
+
</Badge>
|
|
73
|
+
)}
|
|
74
|
+
</>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { usePluginOverrides, useBasePath } from "@btst/stack/context";
|
|
4
|
-
import type { BlogPluginOverrides } from "../../overrides";
|
|
5
|
-
import { DefaultLink } from "./defaults";
|
|
6
|
-
import { Badge } from "@workspace/ui/components/badge";
|
|
7
3
|
import { useSuspenseTags } from "../../hooks/blog-hooks";
|
|
4
|
+
import { CollapsibleTagList } from "./collapsible-tag-list";
|
|
8
5
|
|
|
9
6
|
export function TagsList() {
|
|
10
7
|
const { tags } = useSuspenseTags();
|
|
11
|
-
const { Link } = usePluginOverrides<
|
|
12
|
-
BlogPluginOverrides,
|
|
13
|
-
Partial<BlogPluginOverrides>
|
|
14
|
-
>("blog", {
|
|
15
|
-
Link: DefaultLink,
|
|
16
|
-
});
|
|
17
|
-
const basePath = useBasePath();
|
|
18
8
|
|
|
19
9
|
if (!tags || tags.length === 0) {
|
|
20
10
|
return null;
|
|
@@ -22,13 +12,7 @@ export function TagsList() {
|
|
|
22
12
|
|
|
23
13
|
return (
|
|
24
14
|
<div className="flex flex-wrap gap-2 justify-center">
|
|
25
|
-
{tags
|
|
26
|
-
<Link key={tag.id} href={`${basePath}/blog/tag/${tag.slug}`}>
|
|
27
|
-
<Badge variant="secondary" className="text-xs">
|
|
28
|
-
{tag.name}
|
|
29
|
-
</Badge>
|
|
30
|
-
</Link>
|
|
31
|
-
))}
|
|
15
|
+
<CollapsibleTagList tags={tags} />
|
|
32
16
|
</div>
|
|
33
17
|
);
|
|
34
18
|
}
|
|
@@ -71,6 +71,7 @@ export interface CommentsThreadDiscriminator {
|
|
|
71
71
|
parentId: string | null | undefined;
|
|
72
72
|
status: string | undefined;
|
|
73
73
|
currentUserId: string | undefined;
|
|
74
|
+
sort: string | undefined;
|
|
74
75
|
limit: number;
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -80,6 +81,7 @@ export function commentsThreadDiscriminator(params?: {
|
|
|
80
81
|
parentId?: string | null;
|
|
81
82
|
status?: string;
|
|
82
83
|
currentUserId?: string;
|
|
84
|
+
sort?: string;
|
|
83
85
|
limit?: number;
|
|
84
86
|
}): CommentsThreadDiscriminator {
|
|
85
87
|
return {
|
|
@@ -88,6 +90,7 @@ export function commentsThreadDiscriminator(params?: {
|
|
|
88
90
|
parentId: params?.parentId,
|
|
89
91
|
status: params?.status,
|
|
90
92
|
currentUserId: params?.currentUserId,
|
|
93
|
+
sort: params?.sort,
|
|
91
94
|
limit: params?.limit ?? 20,
|
|
92
95
|
};
|
|
93
96
|
}
|
|
@@ -128,7 +131,7 @@ export const COMMENTS_QUERY_KEYS = {
|
|
|
128
131
|
|
|
129
132
|
/**
|
|
130
133
|
* Key for the infinite thread query (top-level comments, load-more).
|
|
131
|
-
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, limit }]
|
|
134
|
+
* Full key: ["commentsThread", "list", { resourceId, resourceType, parentId, status, currentUserId, sort, limit }]
|
|
132
135
|
* Offset is excluded — it is driven by `pageParam`, not baked into the key.
|
|
133
136
|
*/
|
|
134
137
|
commentsThread: (params?: {
|
|
@@ -137,6 +140,7 @@ export const COMMENTS_QUERY_KEYS = {
|
|
|
137
140
|
parentId?: string | null;
|
|
138
141
|
status?: string;
|
|
139
142
|
currentUserId?: string;
|
|
143
|
+
sort?: string;
|
|
140
144
|
limit?: number;
|
|
141
145
|
}) =>
|
|
142
146
|
["commentsThread", "list", commentsThreadDiscriminator(params)] as const,
|
|
@@ -98,6 +98,17 @@ export interface CommentThreadProps {
|
|
|
98
98
|
* Defaults to true.
|
|
99
99
|
*/
|
|
100
100
|
allowEditing?: boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Sort direction for top-level comments by `createdAt`.
|
|
103
|
+
* - `"desc"` (default): newest first.
|
|
104
|
+
* - `"asc"`: oldest first.
|
|
105
|
+
*
|
|
106
|
+
* Replies inside each thread always render chronologically (oldest → newest)
|
|
107
|
+
* and are unaffected by this prop.
|
|
108
|
+
*
|
|
109
|
+
* Overrides the global `defaultCommentSort` from `CommentsPluginOverrides`.
|
|
110
|
+
*/
|
|
111
|
+
sort?: "asc" | "desc";
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
const DEFAULT_RENDERER: ComponentType<CommentRendererProps> = ({ body }) => (
|
|
@@ -325,6 +336,7 @@ function CommentThreadInner({
|
|
|
325
336
|
pageSize: pageSizeProp,
|
|
326
337
|
allowPosting: allowPostingProp,
|
|
327
338
|
allowEditing: allowEditingProp,
|
|
339
|
+
sort: sortProp,
|
|
328
340
|
}: CommentThreadProps) {
|
|
329
341
|
const overrides = usePluginOverrides<
|
|
330
342
|
CommentsPluginOverrides,
|
|
@@ -334,6 +346,7 @@ function CommentThreadInner({
|
|
|
334
346
|
pageSizeProp ?? overrides.defaultCommentPageSize ?? DEFAULT_PAGE_SIZE;
|
|
335
347
|
const allowPosting = allowPostingProp ?? overrides.allowPosting ?? true;
|
|
336
348
|
const allowEditing = allowEditingProp ?? overrides.allowEditing ?? true;
|
|
349
|
+
const sort = sortProp ?? overrides.defaultCommentSort ?? "desc";
|
|
337
350
|
const loc = { ...COMMENTS_LOCALIZATION, ...localizationProp };
|
|
338
351
|
const [replyingTo, setReplyingTo] = useState<string | null>(null);
|
|
339
352
|
const [expandedReplies, setExpandedReplies] = useState<Set<string>>(
|
|
@@ -357,6 +370,7 @@ function CommentThreadInner({
|
|
|
357
370
|
status: "approved",
|
|
358
371
|
parentId: null,
|
|
359
372
|
currentUserId,
|
|
373
|
+
sort,
|
|
360
374
|
pageSize,
|
|
361
375
|
});
|
|
362
376
|
|
|
@@ -366,6 +380,7 @@ function CommentThreadInner({
|
|
|
366
380
|
currentUserId,
|
|
367
381
|
infiniteKey: threadQueryKey,
|
|
368
382
|
pageSize,
|
|
383
|
+
sort,
|
|
369
384
|
});
|
|
370
385
|
|
|
371
386
|
const handlePost = async (body: string) => {
|
|
@@ -164,6 +164,11 @@ export function useInfiniteComments(
|
|
|
164
164
|
parentId?: string | null;
|
|
165
165
|
status?: "pending" | "approved" | "spam";
|
|
166
166
|
currentUserId?: string;
|
|
167
|
+
/**
|
|
168
|
+
* Sort direction by `createdAt`. Default: `"asc"` (oldest first) — matches
|
|
169
|
+
* the server-side default. Pass `"desc"` for newest-first threads.
|
|
170
|
+
*/
|
|
171
|
+
sort?: "asc" | "desc";
|
|
167
172
|
pageSize?: number;
|
|
168
173
|
},
|
|
169
174
|
options?: { enabled?: boolean },
|
|
@@ -178,6 +183,7 @@ export function useInfiniteComments(
|
|
|
178
183
|
parentId: params.parentId ?? null,
|
|
179
184
|
status: params.status,
|
|
180
185
|
currentUserId: params.currentUserId,
|
|
186
|
+
sort: params.sort,
|
|
181
187
|
limit: pageSize,
|
|
182
188
|
});
|
|
183
189
|
|
|
@@ -266,6 +272,17 @@ export function usePostComment(
|
|
|
266
272
|
* `nextOffset` from `lastPage.limit` instead of a hardcoded fallback.
|
|
267
273
|
*/
|
|
268
274
|
pageSize?: number;
|
|
275
|
+
/**
|
|
276
|
+
* Sort direction of the surrounding infinite thread.
|
|
277
|
+
* - `"asc"` (default): newest comments belong on the LAST page → optimistic
|
|
278
|
+
* item is appended to `pages[last].items`.
|
|
279
|
+
* - `"desc"`: newest comments belong on the FIRST page → optimistic item
|
|
280
|
+
* is prepended to `pages[0].items`.
|
|
281
|
+
*
|
|
282
|
+
* Must match the `sort` passed to `useInfiniteComments` so the optimistic
|
|
283
|
+
* item appears in the same position the server will place it.
|
|
284
|
+
*/
|
|
285
|
+
sort?: "asc" | "desc";
|
|
269
286
|
},
|
|
270
287
|
) {
|
|
271
288
|
const queryClient = useQueryClient();
|
|
@@ -350,6 +367,7 @@ export function usePostComment(
|
|
|
350
367
|
const previous =
|
|
351
368
|
queryClient.getQueryData<InfiniteData<CommentListResult>>(listKey);
|
|
352
369
|
|
|
370
|
+
const isDesc = params.sort === "desc";
|
|
353
371
|
queryClient.setQueryData<InfiniteData<CommentListResult>>(
|
|
354
372
|
listKey,
|
|
355
373
|
(old) => {
|
|
@@ -366,16 +384,21 @@ export function usePostComment(
|
|
|
366
384
|
pageParams: [0],
|
|
367
385
|
};
|
|
368
386
|
}
|
|
369
|
-
|
|
387
|
+
// For asc (oldest-first) threads the new comment lives at the end →
|
|
388
|
+
// append to the last page. For desc (newest-first) threads it lives
|
|
389
|
+
// at the top → prepend to the first page.
|
|
390
|
+
const targetIdx = isDesc ? 0 : old.pages.length - 1;
|
|
370
391
|
return {
|
|
371
392
|
...old,
|
|
372
393
|
// Increment `total` on every page so the header count (which reads
|
|
373
394
|
// pages[0].total) stays in sync even after multiple pages are loaded.
|
|
374
395
|
pages: old.pages.map((page, idx) =>
|
|
375
|
-
idx ===
|
|
396
|
+
idx === targetIdx
|
|
376
397
|
? {
|
|
377
398
|
...page,
|
|
378
|
-
items:
|
|
399
|
+
items: isDesc
|
|
400
|
+
? [optimistic, ...page.items]
|
|
401
|
+
: [...page.items, optimistic],
|
|
379
402
|
total: page.total + 1,
|
|
380
403
|
}
|
|
381
404
|
: { ...page, total: page.total + 1 },
|
|
@@ -75,6 +75,16 @@ export interface CommentsPluginOverrides {
|
|
|
75
75
|
*/
|
|
76
76
|
defaultCommentPageSize?: number;
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Default sort direction (by `createdAt`) for top-level comments in
|
|
80
|
+
* `CommentThread`.
|
|
81
|
+
* - `"desc"` (default): newest comments first.
|
|
82
|
+
* - `"asc"`: oldest comments first.
|
|
83
|
+
*
|
|
84
|
+
* Can be overridden per-instance via the `sort` prop on `CommentThread`.
|
|
85
|
+
*/
|
|
86
|
+
defaultCommentSort?: "asc" | "desc";
|
|
87
|
+
|
|
78
88
|
/**
|
|
79
89
|
* When false, the comment form and reply buttons are hidden in all
|
|
80
90
|
* `CommentThread` instances. Users can still read existing comments.
|
|
@@ -142,6 +142,7 @@ function createCommentsThreadQueries(
|
|
|
142
142
|
parentId?: string | null;
|
|
143
143
|
status?: "pending" | "approved" | "spam";
|
|
144
144
|
currentUserId?: string;
|
|
145
|
+
sort?: "asc" | "desc";
|
|
145
146
|
limit?: number;
|
|
146
147
|
}) => ({
|
|
147
148
|
// Offset is excluded from the key — it is driven by pageParam.
|
|
@@ -162,6 +163,7 @@ function createCommentsThreadQueries(
|
|
|
162
163
|
// The server resolves the caller's identity server-side via the
|
|
163
164
|
// resolveCurrentUserId hook. It is still included in the queryKey
|
|
164
165
|
// above for client-side cache segregation.
|
|
166
|
+
sort: params?.sort,
|
|
165
167
|
limit: params?.limit ?? 20,
|
|
166
168
|
offset: pageParam ?? 0,
|
|
167
169
|
},
|
|
@@ -7,12 +7,12 @@ import { QueryClient } from '@tanstack/react-query';
|
|
|
7
7
|
|
|
8
8
|
declare const createBoardSchema: z.ZodObject<{
|
|
9
9
|
description: z.ZodOptional<z.ZodString>;
|
|
10
|
-
name: z.ZodString;
|
|
11
|
-
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
12
|
-
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
13
10
|
slug: z.ZodOptional<z.ZodString>;
|
|
14
11
|
ownerId: z.ZodOptional<z.ZodString>;
|
|
15
12
|
organizationId: z.ZodOptional<z.ZodString>;
|
|
13
|
+
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
14
|
+
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
15
|
+
name: z.ZodString;
|
|
16
16
|
}, z.core.$strip>;
|
|
17
17
|
declare const updateBoardSchema: z.ZodObject<{
|
|
18
18
|
createdAt: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
|
|
@@ -331,19 +331,19 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
|
|
|
331
331
|
body: better_call.StandardSchemaV1<{
|
|
332
332
|
name: string;
|
|
333
333
|
description?: string | undefined;
|
|
334
|
-
createdAt?: unknown;
|
|
335
|
-
updatedAt?: unknown;
|
|
336
334
|
slug?: string | undefined;
|
|
337
335
|
ownerId?: string | undefined;
|
|
338
336
|
organizationId?: string | undefined;
|
|
337
|
+
createdAt?: unknown;
|
|
338
|
+
updatedAt?: unknown;
|
|
339
339
|
}, {
|
|
340
340
|
name: string;
|
|
341
341
|
description?: string | undefined;
|
|
342
|
-
createdAt?: unknown;
|
|
343
|
-
updatedAt?: unknown;
|
|
344
342
|
slug?: string | undefined;
|
|
345
343
|
ownerId?: string | undefined;
|
|
346
344
|
organizationId?: string | undefined;
|
|
345
|
+
createdAt?: unknown;
|
|
346
|
+
updatedAt?: unknown;
|
|
347
347
|
}>;
|
|
348
348
|
}, {
|
|
349
349
|
columns: ColumnWithTasks[];
|
|
@@ -360,20 +360,20 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
|
|
|
360
360
|
method: "PUT";
|
|
361
361
|
body: better_call.StandardSchemaV1<{
|
|
362
362
|
description?: string | undefined;
|
|
363
|
-
name?: string | undefined;
|
|
364
|
-
createdAt?: unknown;
|
|
365
|
-
updatedAt?: unknown;
|
|
366
363
|
slug?: string | undefined;
|
|
367
364
|
ownerId?: string | undefined;
|
|
368
365
|
organizationId?: string | undefined;
|
|
369
|
-
}, {
|
|
370
|
-
description?: string | undefined;
|
|
371
|
-
name?: string | undefined;
|
|
372
366
|
createdAt?: unknown;
|
|
373
367
|
updatedAt?: unknown;
|
|
368
|
+
name?: string | undefined;
|
|
369
|
+
}, {
|
|
370
|
+
description?: string | undefined;
|
|
374
371
|
slug?: string | undefined;
|
|
375
372
|
ownerId?: string | undefined;
|
|
376
373
|
organizationId?: string | undefined;
|
|
374
|
+
createdAt?: unknown;
|
|
375
|
+
updatedAt?: unknown;
|
|
376
|
+
name?: string | undefined;
|
|
377
377
|
}>;
|
|
378
378
|
}, Board>;
|
|
379
379
|
readonly deleteBoard: better_call.StrictEndpoint<"/boards/:id", {} & {
|
|
@@ -7,12 +7,12 @@ import { QueryClient } from '@tanstack/react-query';
|
|
|
7
7
|
|
|
8
8
|
declare const createBoardSchema: z.ZodObject<{
|
|
9
9
|
description: z.ZodOptional<z.ZodString>;
|
|
10
|
-
name: z.ZodString;
|
|
11
|
-
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
12
|
-
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
13
10
|
slug: z.ZodOptional<z.ZodString>;
|
|
14
11
|
ownerId: z.ZodOptional<z.ZodString>;
|
|
15
12
|
organizationId: z.ZodOptional<z.ZodString>;
|
|
13
|
+
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
14
|
+
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
15
|
+
name: z.ZodString;
|
|
16
16
|
}, z.core.$strip>;
|
|
17
17
|
declare const updateBoardSchema: z.ZodObject<{
|
|
18
18
|
createdAt: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
|
|
@@ -331,19 +331,19 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
|
|
|
331
331
|
body: better_call.StandardSchemaV1<{
|
|
332
332
|
name: string;
|
|
333
333
|
description?: string | undefined;
|
|
334
|
-
createdAt?: unknown;
|
|
335
|
-
updatedAt?: unknown;
|
|
336
334
|
slug?: string | undefined;
|
|
337
335
|
ownerId?: string | undefined;
|
|
338
336
|
organizationId?: string | undefined;
|
|
337
|
+
createdAt?: unknown;
|
|
338
|
+
updatedAt?: unknown;
|
|
339
339
|
}, {
|
|
340
340
|
name: string;
|
|
341
341
|
description?: string | undefined;
|
|
342
|
-
createdAt?: unknown;
|
|
343
|
-
updatedAt?: unknown;
|
|
344
342
|
slug?: string | undefined;
|
|
345
343
|
ownerId?: string | undefined;
|
|
346
344
|
organizationId?: string | undefined;
|
|
345
|
+
createdAt?: unknown;
|
|
346
|
+
updatedAt?: unknown;
|
|
347
347
|
}>;
|
|
348
348
|
}, {
|
|
349
349
|
columns: ColumnWithTasks[];
|
|
@@ -360,20 +360,20 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
|
|
|
360
360
|
method: "PUT";
|
|
361
361
|
body: better_call.StandardSchemaV1<{
|
|
362
362
|
description?: string | undefined;
|
|
363
|
-
name?: string | undefined;
|
|
364
|
-
createdAt?: unknown;
|
|
365
|
-
updatedAt?: unknown;
|
|
366
363
|
slug?: string | undefined;
|
|
367
364
|
ownerId?: string | undefined;
|
|
368
365
|
organizationId?: string | undefined;
|
|
369
|
-
}, {
|
|
370
|
-
description?: string | undefined;
|
|
371
|
-
name?: string | undefined;
|
|
372
366
|
createdAt?: unknown;
|
|
373
367
|
updatedAt?: unknown;
|
|
368
|
+
name?: string | undefined;
|
|
369
|
+
}, {
|
|
370
|
+
description?: string | undefined;
|
|
374
371
|
slug?: string | undefined;
|
|
375
372
|
ownerId?: string | undefined;
|
|
376
373
|
organizationId?: string | undefined;
|
|
374
|
+
createdAt?: unknown;
|
|
375
|
+
updatedAt?: unknown;
|
|
376
|
+
name?: string | undefined;
|
|
377
377
|
}>;
|
|
378
378
|
}, Board>;
|
|
379
379
|
readonly deleteBoard: better_call.StrictEndpoint<"/boards/:id", {} & {
|
|
@@ -8,15 +8,15 @@ declare const createPostSchema: z.ZodObject<{
|
|
|
8
8
|
name: z.ZodString;
|
|
9
9
|
slug: z.ZodString;
|
|
10
10
|
}, z.core.$strip>]>>>>;
|
|
11
|
+
title: z.ZodString;
|
|
11
12
|
slug: z.ZodOptional<z.ZodString>;
|
|
12
|
-
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
13
|
-
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
14
13
|
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
15
14
|
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
16
|
-
|
|
15
|
+
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
17
16
|
content: z.ZodString;
|
|
18
17
|
excerpt: z.ZodString;
|
|
19
18
|
image: z.ZodOptional<z.ZodString>;
|
|
19
|
+
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
20
20
|
}, z.core.$strip>;
|
|
21
21
|
declare const updatePostSchema: z.ZodObject<{
|
|
22
22
|
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
@@ -8,15 +8,15 @@ declare const createPostSchema: z.ZodObject<{
|
|
|
8
8
|
name: z.ZodString;
|
|
9
9
|
slug: z.ZodString;
|
|
10
10
|
}, z.core.$strip>]>>>>;
|
|
11
|
+
title: z.ZodString;
|
|
11
12
|
slug: z.ZodOptional<z.ZodString>;
|
|
12
|
-
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
13
|
-
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
14
13
|
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
15
14
|
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
16
|
-
|
|
15
|
+
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
17
16
|
content: z.ZodString;
|
|
18
17
|
excerpt: z.ZodString;
|
|
19
18
|
image: z.ZodOptional<z.ZodString>;
|
|
19
|
+
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
20
20
|
}, z.core.$strip>;
|
|
21
21
|
declare const updatePostSchema: z.ZodObject<{
|
|
22
22
|
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
@@ -8,15 +8,15 @@ declare const createPostSchema: z.ZodObject<{
|
|
|
8
8
|
name: z.ZodString;
|
|
9
9
|
slug: z.ZodString;
|
|
10
10
|
}, z.core.$strip>]>>>>;
|
|
11
|
+
title: z.ZodString;
|
|
11
12
|
slug: z.ZodOptional<z.ZodString>;
|
|
12
|
-
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
13
|
-
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
14
13
|
createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
15
14
|
updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
16
|
-
|
|
15
|
+
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|
|
17
16
|
content: z.ZodString;
|
|
18
17
|
excerpt: z.ZodString;
|
|
19
18
|
image: z.ZodOptional<z.ZodString>;
|
|
19
|
+
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
20
20
|
}, z.core.$strip>;
|
|
21
21
|
declare const updatePostSchema: z.ZodObject<{
|
|
22
22
|
publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
|