convex-feedback-ui 0.1.2 → 0.1.3
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/README.md +115 -18
- package/dist/.tsbuildinfo +1 -1
- package/dist/native/expo/ExpoFeedbackBody.d.ts +4 -0
- package/dist/native/expo/ExpoFeedbackBody.d.ts.map +1 -0
- package/dist/native/expo/ExpoFeedbackBody.js +26 -0
- package/dist/native/expo/ExpoFeedbackBody.js.map +1 -0
- package/dist/native/expo/FeedbackScreen.d.ts +3 -0
- package/dist/native/expo/FeedbackScreen.d.ts.map +1 -0
- package/dist/native/expo/FeedbackScreen.js +9 -0
- package/dist/native/expo/FeedbackScreen.js.map +1 -0
- package/dist/native/expo/FeedbackStack.d.ts +3 -0
- package/dist/native/expo/FeedbackStack.d.ts.map +1 -0
- package/dist/native/expo/FeedbackStack.js +41 -0
- package/dist/native/expo/FeedbackStack.js.map +1 -0
- package/dist/native/expo/index.d.ts +9 -0
- package/dist/native/expo/index.d.ts.map +1 -0
- package/dist/native/expo/index.js +7 -0
- package/dist/native/expo/index.js.map +1 -0
- package/dist/native/expo/types.d.ts +20 -0
- package/dist/native/expo/types.d.ts.map +1 -0
- package/dist/native/expo/types.js +2 -0
- package/dist/native/expo/types.js.map +1 -0
- package/dist/native/native/FeedbackScreen.d.ts +3 -0
- package/dist/native/native/FeedbackScreen.d.ts.map +1 -0
- package/dist/native/native/FeedbackScreen.js +9 -0
- package/dist/native/native/FeedbackScreen.js.map +1 -0
- package/dist/native/native/index.d.ts +8 -0
- package/dist/native/native/index.d.ts.map +1 -0
- package/dist/native/native/index.js +7 -0
- package/dist/native/native/index.js.map +1 -0
- package/dist/native/shared/helpers.d.ts +3 -0
- package/dist/native/shared/helpers.d.ts.map +1 -0
- package/dist/native/shared/helpers.js +4 -0
- package/dist/native/shared/helpers.js.map +1 -0
- package/dist/native/shared/ui/BodyContent.d.ts +3 -0
- package/dist/native/shared/ui/BodyContent.d.ts.map +1 -0
- package/dist/native/shared/ui/BodyContent.js +18 -0
- package/dist/native/shared/ui/BodyContent.js.map +1 -0
- package/dist/native/shared/ui/Button.d.ts +5 -0
- package/dist/native/shared/ui/Button.d.ts.map +1 -0
- package/dist/native/shared/ui/Button.js +15 -0
- package/dist/native/shared/ui/Button.js.map +1 -0
- package/dist/native/shared/ui/CommentBranch.d.ts +3 -0
- package/dist/native/shared/ui/CommentBranch.d.ts.map +1 -0
- package/dist/native/shared/ui/CommentBranch.js +35 -0
- package/dist/native/shared/ui/CommentBranch.js.map +1 -0
- package/dist/native/shared/ui/EntryCard.d.ts +3 -0
- package/dist/native/shared/ui/EntryCard.d.ts.map +1 -0
- package/dist/native/shared/ui/EntryCard.js +13 -0
- package/dist/native/shared/ui/EntryCard.js.map +1 -0
- package/dist/native/shared/ui/EntryDetail.d.ts +3 -0
- package/dist/native/shared/ui/EntryDetail.d.ts.map +1 -0
- package/dist/native/shared/ui/EntryDetail.js +37 -0
- package/dist/native/shared/ui/EntryDetail.js.map +1 -0
- package/dist/native/shared/ui/FeedbackScreenHeader.d.ts +2 -0
- package/dist/native/shared/ui/FeedbackScreenHeader.d.ts.map +1 -0
- package/dist/native/shared/ui/FeedbackScreenHeader.js +20 -0
- package/dist/native/shared/ui/FeedbackScreenHeader.js.map +1 -0
- package/dist/native/shared/ui/FeedbackScreenList.d.ts +3 -0
- package/dist/native/shared/ui/FeedbackScreenList.d.ts.map +1 -0
- package/dist/native/shared/ui/FeedbackScreenList.js +19 -0
- package/dist/native/shared/ui/FeedbackScreenList.js.map +1 -0
- package/dist/native/shared/ui/NewEntry.d.ts +5 -0
- package/dist/native/shared/ui/NewEntry.d.ts.map +1 -0
- package/dist/native/shared/ui/NewEntry.js +158 -0
- package/dist/native/shared/ui/NewEntry.js.map +1 -0
- package/dist/native/shared/ui/ReplyList.d.ts +3 -0
- package/dist/native/shared/ui/ReplyList.d.ts.map +1 -0
- package/dist/native/shared/ui/ReplyList.js +19 -0
- package/dist/native/shared/ui/ReplyList.js.map +1 -0
- package/dist/native/{primitives.d.ts → shared/ui/primitives.d.ts} +10 -12
- package/dist/native/shared/ui/primitives.d.ts.map +1 -0
- package/dist/native/{primitives.js → shared/ui/primitives.js} +19 -25
- package/dist/native/shared/ui/primitives.js.map +1 -0
- package/dist/react/{context.d.ts → css.d.ts} +2 -4
- package/dist/react/css.d.ts.map +1 -0
- package/dist/react/css.js +18 -0
- package/dist/react/css.js.map +1 -0
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/primitives.d.ts.map +1 -1
- package/dist/react/primitives.js +2 -1
- package/dist/react/primitives.js.map +1 -1
- package/dist/react/screen.d.ts +3 -3
- package/dist/react/screen.d.ts.map +1 -1
- package/dist/react/screen.js +25 -22
- package/dist/react/screen.js.map +1 -1
- package/dist/shared/context/FeedbackBodyProvider.d.ts +5 -0
- package/dist/shared/context/FeedbackBodyProvider.d.ts.map +1 -0
- package/dist/shared/context/FeedbackBodyProvider.js +43 -0
- package/dist/shared/context/FeedbackBodyProvider.js.map +1 -0
- package/dist/{native/context.d.ts → shared/context/FeedbackProvider.d.ts} +2 -2
- package/dist/shared/context/FeedbackProvider.d.ts.map +1 -0
- package/dist/{native/context.js → shared/context/FeedbackProvider.js} +3 -3
- package/dist/shared/context/FeedbackProvider.js.map +1 -0
- package/dist/shared/helpers.d.ts +7 -0
- package/dist/shared/helpers.d.ts.map +1 -0
- package/dist/shared/helpers.js +24 -0
- package/dist/shared/helpers.js.map +1 -0
- package/dist/shared/messages.d.ts.map +1 -1
- package/dist/shared/messages.js +23 -14
- package/dist/shared/messages.js.map +1 -1
- package/dist/shared/theme.d.ts.map +1 -1
- package/dist/shared/theme.js +1 -0
- package/dist/shared/theme.js.map +1 -1
- package/dist/shared/types/context.d.ts +4 -0
- package/dist/shared/types/context.d.ts.map +1 -1
- package/dist/shared/types/messages.d.ts +12 -1
- package/dist/shared/types/messages.d.ts.map +1 -1
- package/dist/shared/types/screen.d.ts +55 -18
- package/dist/shared/types/screen.d.ts.map +1 -1
- package/dist/shared/types/theme.d.ts +4 -0
- package/dist/shared/types/theme.d.ts.map +1 -1
- package/package.json +27 -12
- package/src/native/expo/ExpoFeedbackBody.tsx +48 -0
- package/src/native/expo/FeedbackScreen.tsx +37 -0
- package/src/native/expo/FeedbackStack.tsx +99 -0
- package/src/native/expo/README.md +17 -0
- package/src/native/expo/index.ts +8 -0
- package/src/native/expo/types.ts +23 -0
- package/src/native/native/FeedbackScreen.tsx +37 -0
- package/src/native/native/index.ts +7 -0
- package/src/native/shared/helpers.ts +9 -0
- package/src/native/shared/ui/BodyContent.tsx +43 -0
- package/src/native/shared/ui/Button.tsx +29 -0
- package/src/native/shared/ui/CommentBranch.tsx +88 -0
- package/src/native/shared/ui/EntryCard.tsx +37 -0
- package/src/native/shared/ui/EntryDetail.tsx +103 -0
- package/src/native/shared/ui/FeedbackScreenHeader.tsx +40 -0
- package/src/native/shared/ui/FeedbackScreenList.tsx +61 -0
- package/src/native/shared/ui/NewEntry.tsx +375 -0
- package/src/native/shared/ui/ReplyList.tsx +41 -0
- package/src/native/{primitives.tsx → shared/ui/primitives.tsx} +84 -92
- package/src/react/{context.tsx → css.tsx} +2 -44
- package/src/react/index.ts +1 -1
- package/src/react/primitives.tsx +2 -1
- package/src/react/screen.tsx +67 -123
- package/src/shared/context/FeedbackBodyProvider.tsx +82 -0
- package/src/{native/context.tsx → shared/context/FeedbackProvider.tsx} +3 -3
- package/src/shared/helpers.ts +33 -0
- package/src/shared/messages.ts +23 -22
- package/src/shared/theme.ts +1 -0
- package/src/shared/types/context.ts +5 -0
- package/src/shared/types/messages.ts +16 -1
- package/src/shared/types/screen.ts +69 -23
- package/src/shared/types/theme.ts +5 -0
- package/dist/native/context.d.ts.map +0 -1
- package/dist/native/context.js.map +0 -1
- package/dist/native/index.d.ts +0 -7
- package/dist/native/index.d.ts.map +0 -1
- package/dist/native/index.js +0 -6
- package/dist/native/index.js.map +0 -1
- package/dist/native/primitives.d.ts.map +0 -1
- package/dist/native/primitives.js.map +0 -1
- package/dist/native/screen.d.ts +0 -3
- package/dist/native/screen.d.ts.map +0 -1
- package/dist/native/screen.js +0 -284
- package/dist/native/screen.js.map +0 -1
- package/dist/react/context.d.ts.map +0 -1
- package/dist/react/context.js +0 -41
- package/dist/react/context.js.map +0 -1
- package/src/native/index.ts +0 -6
- package/src/native/screen.tsx +0 -831
- /package/src/native/{README.md → native/README.md} +0 -0
package/src/react/screen.tsx
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
import type { FeedbackHooks } from "convex-feedback/react";
|
|
3
|
+
import type { EntryKind } from "convex-feedback";
|
|
5
4
|
import { useMemo, useState, type SyntheticEvent } from "react";
|
|
6
5
|
|
|
6
|
+
import {
|
|
7
|
+
FeedbackBodyProvider,
|
|
8
|
+
useFeedbackBody,
|
|
9
|
+
} from "../shared/context/FeedbackBodyProvider.js";
|
|
10
|
+
import {
|
|
11
|
+
FeedbackProvider,
|
|
12
|
+
useFeedbackUi,
|
|
13
|
+
} from "../shared/context/FeedbackProvider";
|
|
7
14
|
import type {
|
|
8
|
-
FeedbackColorProps,
|
|
9
15
|
FeedbackScreenCommentBranchProps,
|
|
16
|
+
FeedbackScreenContentProps,
|
|
10
17
|
FeedbackScreenEntryCardProps,
|
|
11
18
|
FeedbackScreenEntryDetailProps,
|
|
12
19
|
FeedbackScreenEntryModalProps,
|
|
13
20
|
FeedbackScreenReplyListProps,
|
|
14
21
|
FeedbackScreenRootProps,
|
|
15
22
|
} from "../shared/types/";
|
|
16
|
-
import { FeedbackProvider, useFeedbackUi } from "./context.js";
|
|
17
23
|
import {
|
|
18
24
|
Comment,
|
|
19
25
|
FeedbackBoard,
|
|
@@ -27,8 +33,7 @@ const entryKinds: readonly EntryKind[] = [
|
|
|
27
33
|
"bug_report",
|
|
28
34
|
];
|
|
29
35
|
|
|
30
|
-
export interface FeedbackScreenProps
|
|
31
|
-
extends FeedbackColorProps, FeedbackScreenRootProps {
|
|
36
|
+
export interface FeedbackScreenProps extends FeedbackScreenRootProps {
|
|
32
37
|
className?: string | undefined;
|
|
33
38
|
}
|
|
34
39
|
|
|
@@ -43,55 +48,52 @@ export function FeedbackScreen({
|
|
|
43
48
|
maxCommentDepth = 5,
|
|
44
49
|
transformComments,
|
|
45
50
|
renderActor,
|
|
46
|
-
|
|
47
|
-
...
|
|
51
|
+
debounceDuration = 300,
|
|
52
|
+
...props
|
|
48
53
|
}: FeedbackScreenProps) {
|
|
49
54
|
return (
|
|
50
55
|
<FeedbackProvider messages={messages} theme={theme} unstyled={unstyled}>
|
|
51
|
-
<
|
|
56
|
+
<FeedbackBodyProvider
|
|
52
57
|
hooks={hooks}
|
|
53
58
|
entrySort={entrySort}
|
|
54
59
|
commentSort={commentSort}
|
|
55
60
|
enabledKinds={enabledKinds}
|
|
56
61
|
maxCommentDepth={maxCommentDepth}
|
|
62
|
+
debounceDuration={debounceDuration}
|
|
57
63
|
transformComments={transformComments}
|
|
58
64
|
renderActor={renderActor}
|
|
59
|
-
|
|
60
|
-
{...
|
|
61
|
-
|
|
65
|
+
>
|
|
66
|
+
<FeedbackScreenInner {...props} />
|
|
67
|
+
</FeedbackBodyProvider>
|
|
62
68
|
</FeedbackProvider>
|
|
63
69
|
);
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
interface FeedbackScreenInnerProps extends
|
|
67
|
-
hooks: FeedbackHooks;
|
|
68
|
-
entrySort: EntrySort;
|
|
69
|
-
commentSort: CommentSort;
|
|
70
|
-
enabledKinds: readonly EntryKind[];
|
|
71
|
-
maxCommentDepth: number;
|
|
72
|
-
transformComments?: FeedbackScreenProps["transformComments"];
|
|
73
|
-
renderActor?: FeedbackScreenProps["renderActor"];
|
|
72
|
+
interface FeedbackScreenInnerProps extends FeedbackScreenContentProps {
|
|
74
73
|
className?: string | undefined;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
function FeedbackScreenInner({
|
|
78
|
-
hooks,
|
|
79
|
-
entrySort,
|
|
80
|
-
commentSort,
|
|
81
|
-
enabledKinds,
|
|
82
|
-
maxCommentDepth,
|
|
83
|
-
transformComments,
|
|
84
|
-
renderActor,
|
|
85
77
|
className,
|
|
86
78
|
...colors
|
|
87
79
|
}: FeedbackScreenInnerProps) {
|
|
80
|
+
const {
|
|
81
|
+
hooks,
|
|
82
|
+
entrySort,
|
|
83
|
+
enabledKinds,
|
|
84
|
+
showForm,
|
|
85
|
+
setShowForm,
|
|
86
|
+
setSelectedEntryId,
|
|
87
|
+
selectedEntryId,
|
|
88
|
+
query,
|
|
89
|
+
setQuery,
|
|
90
|
+
debouncedQuery,
|
|
91
|
+
} = useFeedbackBody();
|
|
88
92
|
const { messages } = useFeedbackUi();
|
|
89
|
-
|
|
90
|
-
const [showForm, setShowForm] = useState(false);
|
|
91
|
-
const [selectedEntryId, setSelectedEntryId] = useState<string | null>(null);
|
|
93
|
+
|
|
92
94
|
const list = hooks.useEntries({ sort: entrySort, kinds: enabledKinds });
|
|
93
95
|
const searchResults = hooks.useSearchEntries({
|
|
94
|
-
searchQuery,
|
|
96
|
+
searchQuery: debouncedQuery,
|
|
95
97
|
kinds: enabledKinds,
|
|
96
98
|
});
|
|
97
99
|
|
|
@@ -99,19 +101,14 @@ function FeedbackScreenInner({
|
|
|
99
101
|
return (
|
|
100
102
|
<FeedbackBoard.Root className={className} {...colors}>
|
|
101
103
|
<EntryDetail
|
|
102
|
-
hooks={hooks}
|
|
103
104
|
entryId={selectedEntryId}
|
|
104
105
|
onBack={() => setSelectedEntryId(null)}
|
|
105
|
-
commentSort={commentSort}
|
|
106
|
-
maxCommentDepth={maxCommentDepth}
|
|
107
|
-
transformComments={transformComments}
|
|
108
|
-
renderActor={renderActor}
|
|
109
106
|
/>
|
|
110
107
|
</FeedbackBoard.Root>
|
|
111
108
|
);
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
const searching =
|
|
111
|
+
const searching = query.trim().length > 0;
|
|
115
112
|
const entries = searching ? searchResults : list.results;
|
|
116
113
|
const loading = searching
|
|
117
114
|
? searchResults === undefined
|
|
@@ -133,30 +130,25 @@ function FeedbackScreenInner({
|
|
|
133
130
|
</button>
|
|
134
131
|
</FeedbackBoard.Header>
|
|
135
132
|
|
|
136
|
-
{showForm
|
|
133
|
+
{showForm && (
|
|
137
134
|
<CreateEntryForm
|
|
138
|
-
hooks={hooks}
|
|
139
|
-
enabledKinds={enabledKinds}
|
|
140
135
|
onCreated={(entryId) => {
|
|
141
136
|
setShowForm(false);
|
|
142
137
|
setSelectedEntryId(entryId);
|
|
143
138
|
}}
|
|
144
139
|
/>
|
|
145
|
-
)
|
|
140
|
+
)}
|
|
146
141
|
|
|
147
|
-
<FeedbackBoard.Search
|
|
148
|
-
value={searchQuery}
|
|
149
|
-
onValueChange={setSearchQuery}
|
|
150
|
-
/>
|
|
142
|
+
<FeedbackBoard.Search value={query} onValueChange={setQuery} />
|
|
151
143
|
|
|
152
|
-
{loading
|
|
153
|
-
{!loading && entries !== undefined && entries.length === 0
|
|
144
|
+
{loading && <p className="cf-state">{messages.board.loading}</p>}
|
|
145
|
+
{!loading && entries !== undefined && entries.length === 0 && (
|
|
154
146
|
<p className="cf-state">
|
|
155
147
|
{searching
|
|
156
148
|
? messages.board.noSearchResults
|
|
157
149
|
: messages.board.noEntries}
|
|
158
150
|
</p>
|
|
159
|
-
)
|
|
151
|
+
)}
|
|
160
152
|
|
|
161
153
|
<FeedbackBoard.List>
|
|
162
154
|
{(entries ?? []).map((entry) => (
|
|
@@ -169,7 +161,7 @@ function FeedbackScreenInner({
|
|
|
169
161
|
))}
|
|
170
162
|
</FeedbackBoard.List>
|
|
171
163
|
|
|
172
|
-
{!searching && list.status === "CanLoadMore"
|
|
164
|
+
{!searching && list.status === "CanLoadMore" && (
|
|
173
165
|
<button
|
|
174
166
|
type="button"
|
|
175
167
|
className="cf-button"
|
|
@@ -177,7 +169,7 @@ function FeedbackScreenInner({
|
|
|
177
169
|
>
|
|
178
170
|
{messages.board.loadMore}
|
|
179
171
|
</button>
|
|
180
|
-
)
|
|
172
|
+
)}
|
|
181
173
|
</FeedbackBoard.Root>
|
|
182
174
|
);
|
|
183
175
|
}
|
|
@@ -214,11 +206,8 @@ function EntryCard({ entry, hooks, onOpen }: FeedbackScreenEntryCardProps) {
|
|
|
214
206
|
);
|
|
215
207
|
}
|
|
216
208
|
|
|
217
|
-
function CreateEntryForm({
|
|
218
|
-
hooks,
|
|
219
|
-
enabledKinds,
|
|
220
|
-
onCreated,
|
|
221
|
-
}: FeedbackScreenEntryModalProps) {
|
|
209
|
+
function CreateEntryForm({ onCreated }: FeedbackScreenEntryModalProps) {
|
|
210
|
+
const { hooks, enabledKinds } = useFeedbackBody();
|
|
222
211
|
const { messages } = useFeedbackUi();
|
|
223
212
|
const [kind, setKind] = useState<EntryKind>(enabledKinds[0] ?? "feedback");
|
|
224
213
|
const [title, setTitle] = useState("");
|
|
@@ -385,15 +374,8 @@ function CreateEntryForm({
|
|
|
385
374
|
);
|
|
386
375
|
}
|
|
387
376
|
|
|
388
|
-
function EntryDetail({
|
|
389
|
-
hooks,
|
|
390
|
-
entryId,
|
|
391
|
-
onBack,
|
|
392
|
-
commentSort,
|
|
393
|
-
maxCommentDepth,
|
|
394
|
-
transformComments,
|
|
395
|
-
renderActor,
|
|
396
|
-
}: FeedbackScreenEntryDetailProps) {
|
|
377
|
+
function EntryDetail({ entryId, onBack }: FeedbackScreenEntryDetailProps) {
|
|
378
|
+
const { hooks, commentSort, transformComments } = useFeedbackBody();
|
|
397
379
|
const { messages } = useFeedbackUi();
|
|
398
380
|
const entry = hooks.useEntry(entryId);
|
|
399
381
|
const setUpvote = hooks.useSetEntryUpvote();
|
|
@@ -461,28 +443,23 @@ function EntryDetail({
|
|
|
461
443
|
</button>
|
|
462
444
|
</FeedbackForm.Root>
|
|
463
445
|
|
|
464
|
-
{comments.status === "LoadingFirstPage"
|
|
446
|
+
{comments.status === "LoadingFirstPage" && (
|
|
465
447
|
<p className="cf-state">{messages.board.loading}</p>
|
|
466
|
-
)
|
|
448
|
+
)}
|
|
467
449
|
{comments.status !== "LoadingFirstPage" &&
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
450
|
+
visibleComments.length === 0 && (
|
|
451
|
+
<p className="cf-state">{messages.comments.noComments}</p>
|
|
452
|
+
)}
|
|
471
453
|
<div className="cf-comments">
|
|
472
454
|
{visibleComments.map((comment) => (
|
|
473
455
|
<CommentBranch
|
|
474
456
|
key={comment.id}
|
|
475
457
|
comment={comment}
|
|
476
458
|
entryId={entryId}
|
|
477
|
-
hooks={hooks}
|
|
478
|
-
commentSort={commentSort}
|
|
479
|
-
maxCommentDepth={maxCommentDepth}
|
|
480
|
-
transformComments={transformComments}
|
|
481
|
-
renderActor={renderActor}
|
|
482
459
|
/>
|
|
483
460
|
))}
|
|
484
461
|
</div>
|
|
485
|
-
{comments.status === "CanLoadMore"
|
|
462
|
+
{comments.status === "CanLoadMore" && (
|
|
486
463
|
<button
|
|
487
464
|
type="button"
|
|
488
465
|
className="cf-button"
|
|
@@ -490,21 +467,14 @@ function EntryDetail({
|
|
|
490
467
|
>
|
|
491
468
|
{messages.comments.loadMore}
|
|
492
469
|
</button>
|
|
493
|
-
)
|
|
470
|
+
)}
|
|
494
471
|
</section>
|
|
495
472
|
</div>
|
|
496
473
|
);
|
|
497
474
|
}
|
|
498
475
|
|
|
499
|
-
function CommentBranch({
|
|
500
|
-
|
|
501
|
-
entryId,
|
|
502
|
-
hooks,
|
|
503
|
-
commentSort,
|
|
504
|
-
maxCommentDepth,
|
|
505
|
-
transformComments,
|
|
506
|
-
renderActor,
|
|
507
|
-
}: FeedbackScreenCommentBranchProps) {
|
|
476
|
+
function CommentBranch({ comment, entryId }: FeedbackScreenCommentBranchProps) {
|
|
477
|
+
const { hooks, maxCommentDepth, renderActor } = useFeedbackBody();
|
|
508
478
|
const { messages } = useFeedbackUi();
|
|
509
479
|
const [expanded, setExpanded] = useState(false);
|
|
510
480
|
const [replying, setReplying] = useState(false);
|
|
@@ -524,15 +494,15 @@ function CommentBranch({
|
|
|
524
494
|
void setLike({ commentId: comment.id, desiredState: active })
|
|
525
495
|
}
|
|
526
496
|
/>
|
|
527
|
-
{comment.body !== null && comment.depth < maxCommentDepth
|
|
497
|
+
{comment.body !== null && comment.depth < maxCommentDepth && (
|
|
528
498
|
<Comment.Reply onActivate={() => setReplying((value) => !value)} />
|
|
529
|
-
)
|
|
499
|
+
)}
|
|
530
500
|
<Comment.RepliesButton
|
|
531
501
|
expanded={expanded}
|
|
532
502
|
onExpandedChange={setExpanded}
|
|
533
503
|
/>
|
|
534
504
|
</div>
|
|
535
|
-
{replying
|
|
505
|
+
{replying && (
|
|
536
506
|
<FeedbackForm.Root
|
|
537
507
|
onSubmit={async (event) => {
|
|
538
508
|
event.preventDefault();
|
|
@@ -566,31 +536,14 @@ function CommentBranch({
|
|
|
566
536
|
</button>
|
|
567
537
|
</div>
|
|
568
538
|
</FeedbackForm.Root>
|
|
569
|
-
)
|
|
570
|
-
{expanded
|
|
571
|
-
<ReplyList
|
|
572
|
-
hooks={hooks}
|
|
573
|
-
entryId={entryId}
|
|
574
|
-
parentCommentId={comment.id}
|
|
575
|
-
commentSort={commentSort}
|
|
576
|
-
maxCommentDepth={maxCommentDepth}
|
|
577
|
-
transformComments={transformComments}
|
|
578
|
-
renderActor={renderActor}
|
|
579
|
-
/>
|
|
580
|
-
) : null}
|
|
539
|
+
)}
|
|
540
|
+
{expanded && <ReplyList entryId={entryId} parentCommentId={comment.id} />}
|
|
581
541
|
</Comment.Root>
|
|
582
542
|
);
|
|
583
543
|
}
|
|
584
544
|
|
|
585
|
-
function ReplyList({
|
|
586
|
-
hooks,
|
|
587
|
-
entryId,
|
|
588
|
-
parentCommentId,
|
|
589
|
-
commentSort,
|
|
590
|
-
maxCommentDepth,
|
|
591
|
-
transformComments,
|
|
592
|
-
renderActor,
|
|
593
|
-
}: FeedbackScreenReplyListProps) {
|
|
545
|
+
function ReplyList({ entryId, parentCommentId }: FeedbackScreenReplyListProps) {
|
|
546
|
+
const { hooks, commentSort, transformComments } = useFeedbackBody();
|
|
594
547
|
const { messages } = useFeedbackUi();
|
|
595
548
|
const replies = hooks.useComments({
|
|
596
549
|
entryId,
|
|
@@ -604,22 +557,13 @@ function ReplyList({
|
|
|
604
557
|
|
|
605
558
|
return (
|
|
606
559
|
<Comment.Children>
|
|
607
|
-
{replies.status === "LoadingFirstPage"
|
|
560
|
+
{replies.status === "LoadingFirstPage" && (
|
|
608
561
|
<p className="cf-state">{messages.board.loading}</p>
|
|
609
|
-
)
|
|
562
|
+
)}
|
|
610
563
|
{visibleReplies.map((reply) => (
|
|
611
|
-
<CommentBranch
|
|
612
|
-
key={reply.id}
|
|
613
|
-
comment={reply}
|
|
614
|
-
entryId={entryId}
|
|
615
|
-
hooks={hooks}
|
|
616
|
-
commentSort={commentSort}
|
|
617
|
-
maxCommentDepth={maxCommentDepth}
|
|
618
|
-
transformComments={transformComments}
|
|
619
|
-
renderActor={renderActor}
|
|
620
|
-
/>
|
|
564
|
+
<CommentBranch key={reply.id} comment={reply} entryId={entryId} />
|
|
621
565
|
))}
|
|
622
|
-
{replies.status === "CanLoadMore"
|
|
566
|
+
{replies.status === "CanLoadMore" && (
|
|
623
567
|
<button
|
|
624
568
|
type="button"
|
|
625
569
|
className="cf-button"
|
|
@@ -627,7 +571,7 @@ function ReplyList({
|
|
|
627
571
|
>
|
|
628
572
|
{messages.comments.loadMore}
|
|
629
573
|
</button>
|
|
630
|
-
)
|
|
574
|
+
)}
|
|
631
575
|
</Comment.Children>
|
|
632
576
|
);
|
|
633
577
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
type PropsWithChildren,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useState,
|
|
8
|
+
} from "react";
|
|
9
|
+
import { debounce } from "../helpers";
|
|
10
|
+
import type {
|
|
11
|
+
FeedbackScreenBodyContextValue,
|
|
12
|
+
FeedbackScreenProviderProps,
|
|
13
|
+
} from "../types";
|
|
14
|
+
|
|
15
|
+
const FeedbackBodyContext =
|
|
16
|
+
createContext<FeedbackScreenBodyContextValue | null>(null);
|
|
17
|
+
|
|
18
|
+
export function FeedbackBodyProvider({
|
|
19
|
+
hooks,
|
|
20
|
+
entrySort,
|
|
21
|
+
commentSort,
|
|
22
|
+
enabledKinds,
|
|
23
|
+
maxCommentDepth,
|
|
24
|
+
debounceDuration,
|
|
25
|
+
children,
|
|
26
|
+
}: PropsWithChildren<FeedbackScreenProviderProps>) {
|
|
27
|
+
const [selectedEntryId, setSelectedEntryId] = useState<string | null>(null);
|
|
28
|
+
const [showForm, setShowForm] = useState(false);
|
|
29
|
+
const [isSearching, setIsSearching] = useState(false);
|
|
30
|
+
const [query, setQuery] = useState("");
|
|
31
|
+
const [debouncedQuery, setDebouncedQuery] = useState("");
|
|
32
|
+
|
|
33
|
+
const updateDebouncedQuery = useMemo(
|
|
34
|
+
() => debounce(setDebouncedQuery, debounceDuration),
|
|
35
|
+
[],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
updateDebouncedQuery(query);
|
|
40
|
+
|
|
41
|
+
return updateDebouncedQuery.cancel;
|
|
42
|
+
}, [query, updateDebouncedQuery]);
|
|
43
|
+
|
|
44
|
+
const value = {
|
|
45
|
+
query,
|
|
46
|
+
debouncedQuery,
|
|
47
|
+
showForm,
|
|
48
|
+
isSearching,
|
|
49
|
+
selectedEntryId,
|
|
50
|
+
|
|
51
|
+
setQuery,
|
|
52
|
+
setDebouncedQuery,
|
|
53
|
+
setShowForm,
|
|
54
|
+
setIsSearching,
|
|
55
|
+
setSelectedEntryId,
|
|
56
|
+
|
|
57
|
+
hooks,
|
|
58
|
+
entrySort,
|
|
59
|
+
commentSort,
|
|
60
|
+
enabledKinds,
|
|
61
|
+
maxCommentDepth,
|
|
62
|
+
debounceDuration,
|
|
63
|
+
} satisfies FeedbackScreenBodyContextValue;
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<FeedbackBodyContext.Provider value={value}>
|
|
67
|
+
{children}
|
|
68
|
+
</FeedbackBodyContext.Provider>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function useFeedbackBody(): FeedbackScreenBodyContextValue {
|
|
73
|
+
const context = useContext(FeedbackBodyContext);
|
|
74
|
+
|
|
75
|
+
if (!context) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
"useOnboardingFlow must be used inside OnboardingFlowProvider",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return context;
|
|
82
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createContext, useContext, useMemo, type ReactNode } from "react";
|
|
2
2
|
|
|
3
|
-
import { mergeFeedbackMessages } from "
|
|
3
|
+
import { mergeFeedbackMessages } from "../../shared/messages.js";
|
|
4
4
|
|
|
5
|
-
import { mergeFeedbackTheme } from "
|
|
5
|
+
import { mergeFeedbackTheme } from "../../shared/theme.js";
|
|
6
6
|
import type {
|
|
7
7
|
FeedbackProviderProps,
|
|
8
8
|
FeedbackTheme,
|
|
9
9
|
FeedbackUiContextValue,
|
|
10
|
-
} from "
|
|
10
|
+
} from "../../shared/types/index.js";
|
|
11
11
|
|
|
12
12
|
const FeedbackNativeContext = createContext<FeedbackUiContextValue | null>(
|
|
13
13
|
null,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { EntryKind } from "convex-feedback";
|
|
2
|
+
|
|
3
|
+
export const kinds: readonly EntryKind[] = [
|
|
4
|
+
"feedback",
|
|
5
|
+
"feature_request",
|
|
6
|
+
"bug_report",
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
export function debounce<TArgs extends unknown[]>(
|
|
10
|
+
callback: (...args: TArgs) => void,
|
|
11
|
+
delay: number,
|
|
12
|
+
) {
|
|
13
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
14
|
+
|
|
15
|
+
const debounced = (...args: TArgs) => {
|
|
16
|
+
if (timeout !== undefined) {
|
|
17
|
+
clearTimeout(timeout);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
timeout = setTimeout(() => {
|
|
21
|
+
callback(...args);
|
|
22
|
+
}, delay);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
debounced.cancel = () => {
|
|
26
|
+
if (timeout !== undefined) {
|
|
27
|
+
clearTimeout(timeout);
|
|
28
|
+
timeout = undefined;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return debounced;
|
|
33
|
+
}
|
package/src/shared/messages.ts
CHANGED
|
@@ -16,6 +16,16 @@ export const englishFeedbackMessages: FeedbackMessages = {
|
|
|
16
16
|
feature_request: "Feature request",
|
|
17
17
|
bug_report: "Bug report",
|
|
18
18
|
},
|
|
19
|
+
kindsPlural: {
|
|
20
|
+
feedback: "Feedback",
|
|
21
|
+
feature_request: "Feature requests",
|
|
22
|
+
bug_report: "Bug reports",
|
|
23
|
+
},
|
|
24
|
+
newFeedback: {
|
|
25
|
+
feedback: "New feedback",
|
|
26
|
+
feature_request: "New feature request",
|
|
27
|
+
bug_report: "New bug report",
|
|
28
|
+
},
|
|
19
29
|
statuses: {
|
|
20
30
|
open: "Open",
|
|
21
31
|
under_review: "Under review",
|
|
@@ -29,7 +39,7 @@ export const englishFeedbackMessages: FeedbackMessages = {
|
|
|
29
39
|
removeUpvote: "Remove upvote",
|
|
30
40
|
comments: (count) => `${count} ${count === 1 ? "comment" : "comments"}`,
|
|
31
41
|
open: "Open",
|
|
32
|
-
back: "
|
|
42
|
+
back: "Go back",
|
|
33
43
|
},
|
|
34
44
|
form: {
|
|
35
45
|
kind: "Type",
|
|
@@ -68,29 +78,20 @@ export function mergeFeedbackMessages(
|
|
|
68
78
|
return {
|
|
69
79
|
board: { ...englishFeedbackMessages.board, ...overrides.board },
|
|
70
80
|
kinds: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
...englishFeedbackMessages.kinds,
|
|
82
|
+
...overrides.kinds,
|
|
83
|
+
},
|
|
84
|
+
kindsPlural: {
|
|
85
|
+
...englishFeedbackMessages.kindsPlural,
|
|
86
|
+
...overrides.kindsPlural,
|
|
87
|
+
},
|
|
88
|
+
newFeedback: {
|
|
89
|
+
...englishFeedbackMessages.newFeedback,
|
|
90
|
+
...overrides.newFeedback,
|
|
78
91
|
},
|
|
79
92
|
statuses: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
overrides.statuses?.under_review ??
|
|
83
|
-
englishFeedbackMessages.statuses.under_review,
|
|
84
|
-
planned:
|
|
85
|
-
overrides.statuses?.planned ?? englishFeedbackMessages.statuses.planned,
|
|
86
|
-
in_progress:
|
|
87
|
-
overrides.statuses?.in_progress ??
|
|
88
|
-
englishFeedbackMessages.statuses.in_progress,
|
|
89
|
-
completed:
|
|
90
|
-
overrides.statuses?.completed ??
|
|
91
|
-
englishFeedbackMessages.statuses.completed,
|
|
92
|
-
closed:
|
|
93
|
-
overrides.statuses?.closed ?? englishFeedbackMessages.statuses.closed,
|
|
93
|
+
...englishFeedbackMessages.statuses,
|
|
94
|
+
...overrides.statuses,
|
|
94
95
|
},
|
|
95
96
|
entry: { ...englishFeedbackMessages.entry, ...overrides.entry },
|
|
96
97
|
form: { ...englishFeedbackMessages.form, ...overrides.form },
|
package/src/shared/theme.ts
CHANGED
|
@@ -61,6 +61,11 @@ export interface FeedbackColorProps {
|
|
|
61
61
|
/** Primary interaction and accent color. */
|
|
62
62
|
primaryColor?: string;
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Foreground color for primary interactions and accents.
|
|
66
|
+
*/
|
|
67
|
+
primaryForeground?: string;
|
|
68
|
+
|
|
64
69
|
/** Main background color. */
|
|
65
70
|
backgroundColor?: string;
|
|
66
71
|
|
|
@@ -18,7 +18,10 @@ export interface FeedbackMessages {
|
|
|
18
18
|
/** Search input placeholder. */
|
|
19
19
|
searchPlaceholder: string;
|
|
20
20
|
|
|
21
|
-
/** CTA for opening the create-entry flow.
|
|
21
|
+
/** CTA for opening the create-entry flow.
|
|
22
|
+
* This is shown when enabled kinds are two or more.
|
|
23
|
+
* Use messages.newFeedback for a single-kind entry.
|
|
24
|
+
*/
|
|
22
25
|
createEntry: string;
|
|
23
26
|
|
|
24
27
|
/** Empty-state copy when no entries exist. */
|
|
@@ -37,6 +40,12 @@ export interface FeedbackMessages {
|
|
|
37
40
|
/** Localized display labels for each fixed entry kind. */
|
|
38
41
|
kinds: Record<EntryKind, string>;
|
|
39
42
|
|
|
43
|
+
/** Localized plural display labels for each fixed entry kind. */
|
|
44
|
+
kindsPlural: Record<EntryKind, string>;
|
|
45
|
+
|
|
46
|
+
/** Localized display labels for creating a new entry. */
|
|
47
|
+
newFeedback: Record<EntryKind, string>;
|
|
48
|
+
|
|
40
49
|
/** Localized display labels for each fixed workflow status. */
|
|
41
50
|
statuses: Record<EntryStatus, string>;
|
|
42
51
|
|
|
@@ -146,6 +155,12 @@ export interface FeedbackMessageOverrides {
|
|
|
146
155
|
/** Entry-kind label overrides. */
|
|
147
156
|
kinds?: Partial<FeedbackMessages["kinds"]> | undefined;
|
|
148
157
|
|
|
158
|
+
/** Entry-kind plural label overrides. */
|
|
159
|
+
kindsPlural?: Partial<FeedbackMessages["kindsPlural"]> | undefined;
|
|
160
|
+
|
|
161
|
+
/** Create single entry copy overrides. */
|
|
162
|
+
newFeedback?: Partial<FeedbackMessages["newFeedback"]> | undefined;
|
|
163
|
+
|
|
149
164
|
/** Workflow-status label overrides. */
|
|
150
165
|
statuses?: Partial<FeedbackMessages["statuses"]> | undefined;
|
|
151
166
|
|