gitalk-react 1.0.0-beta.6 → 1.0.0-beta.8
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-zh-CN.md +13 -0
- package/README.md +13 -0
- package/dist/gitalk-dark.css +1 -1
- package/dist/gitalk-light.css +1 -1
- package/dist/gitalk.d.ts +5 -1
- package/dist/gitalk.js +3403 -3291
- package/dist/gitalk.umd.cjs +25 -25
- package/lib/components/comment-textarea.tsx +164 -0
- package/lib/components/comment.tsx +38 -6
- package/lib/components/comments-list.tsx +106 -0
- package/lib/components/meta.tsx +129 -0
- package/lib/constants/index.ts +9 -1
- package/lib/contexts/I18nContext.ts +4 -2
- package/lib/gitalk.tsx +152 -533
- package/lib/interfaces/index.ts +166 -0
- package/lib/services/graphql/comment.ts +1 -2
- package/lib/themes/base.scss +42 -4
- package/lib/themes/gitalk-dark.scss +4 -0
- package/lib/themes/gitalk-light.scss +4 -0
- package/package.json +1 -1
package/lib/gitalk.tsx
CHANGED
|
@@ -2,35 +2,31 @@ import "./i18n";
|
|
|
2
2
|
|
|
3
3
|
import { useRequest } from "ahooks";
|
|
4
4
|
import React, {
|
|
5
|
-
forwardRef,
|
|
6
5
|
useCallback,
|
|
7
6
|
useEffect,
|
|
8
7
|
useMemo,
|
|
9
8
|
useRef,
|
|
10
9
|
useState,
|
|
11
10
|
} from "react";
|
|
12
|
-
import FlipMove from "react-flip-move";
|
|
13
11
|
|
|
14
|
-
import ArrowDown from "./assets/arrow-down.svg?raw";
|
|
15
|
-
import Github from "./assets/github.svg?raw";
|
|
16
|
-
import Tip from "./assets/tip.svg?raw";
|
|
17
|
-
import Action from "./components/action";
|
|
18
|
-
import Avatar from "./components/avatar";
|
|
19
12
|
import Button from "./components/button";
|
|
20
|
-
import
|
|
21
|
-
import
|
|
13
|
+
import { type CommentProps } from "./components/comment";
|
|
14
|
+
import CommentTextarea from "./components/comment-textarea";
|
|
15
|
+
import CommentsList from "./components/comments-list";
|
|
16
|
+
import Meta from "./components/meta";
|
|
22
17
|
import {
|
|
23
18
|
ACCESS_TOKEN_KEY,
|
|
24
19
|
DATE_FNS_LOCALE_MAP,
|
|
20
|
+
DEFAULT_FLIP_MOVE_OPTIONS,
|
|
25
21
|
DEFAULT_LABELS,
|
|
22
|
+
DEFAULT_PROXY,
|
|
26
23
|
DEFAULT_USER,
|
|
27
|
-
HOMEPAGE,
|
|
28
|
-
VERSION,
|
|
29
24
|
} from "./constants";
|
|
30
|
-
import I18nContext from "./contexts/I18nContext";
|
|
25
|
+
import I18nContext, { type I18nContextValue } from "./contexts/I18nContext";
|
|
31
26
|
import i18n, { type Lang } from "./i18n";
|
|
32
27
|
import type {
|
|
33
28
|
Comment as CommentType,
|
|
29
|
+
GitalkProps,
|
|
34
30
|
Issue as IssueType,
|
|
35
31
|
User as UserType,
|
|
36
32
|
} from "./interfaces";
|
|
@@ -41,168 +37,9 @@ import {
|
|
|
41
37
|
import getOctokitInstance from "./services/request";
|
|
42
38
|
import { getAccessToken, getAuthorizeUrl } from "./services/user";
|
|
43
39
|
import { supportsCSSVariables, supportsES2020 } from "./utils/compatibility";
|
|
44
|
-
import { hasClassInParent } from "./utils/dom";
|
|
45
40
|
import logger from "./utils/logger";
|
|
46
41
|
import { parseSearchQuery, stringifySearchQuery } from "./utils/query";
|
|
47
42
|
|
|
48
|
-
export interface GitalkProps
|
|
49
|
-
extends Omit<
|
|
50
|
-
React.DetailedHTMLProps<
|
|
51
|
-
React.HTMLAttributes<HTMLDivElement>,
|
|
52
|
-
HTMLDivElement
|
|
53
|
-
>,
|
|
54
|
-
"id" | "title"
|
|
55
|
-
> {
|
|
56
|
-
/**
|
|
57
|
-
* GitHub Application Client ID.
|
|
58
|
-
*/
|
|
59
|
-
clientID: string;
|
|
60
|
-
/**
|
|
61
|
-
* GitHub Application Client Secret.
|
|
62
|
-
*/
|
|
63
|
-
clientSecret: string;
|
|
64
|
-
/**
|
|
65
|
-
* GitHub repository owner.
|
|
66
|
-
* Can be personal user or organization.
|
|
67
|
-
*/
|
|
68
|
-
owner: string;
|
|
69
|
-
/**
|
|
70
|
-
* Name of Github repository.
|
|
71
|
-
*/
|
|
72
|
-
repo: string;
|
|
73
|
-
/**
|
|
74
|
-
* GitHub repository owner and collaborators.
|
|
75
|
-
* (Users who having write access to this repository)
|
|
76
|
-
*/
|
|
77
|
-
admin: string[];
|
|
78
|
-
/**
|
|
79
|
-
* The unique id of the page.
|
|
80
|
-
* Length must less than 50.
|
|
81
|
-
*
|
|
82
|
-
* @default location.host + location.pathname
|
|
83
|
-
*/
|
|
84
|
-
id?: string;
|
|
85
|
-
/**
|
|
86
|
-
* The issue ID of the page.
|
|
87
|
-
* If the number attribute is not defined, issue will be located using id.
|
|
88
|
-
*/
|
|
89
|
-
number?: number;
|
|
90
|
-
/**
|
|
91
|
-
* GitHub issue labels.
|
|
92
|
-
*
|
|
93
|
-
* @default ['Gitalk']
|
|
94
|
-
*/
|
|
95
|
-
labels?: string[];
|
|
96
|
-
/**
|
|
97
|
-
* GitHub issue title.
|
|
98
|
-
*
|
|
99
|
-
* @default document.title
|
|
100
|
-
*/
|
|
101
|
-
title?: string;
|
|
102
|
-
/**
|
|
103
|
-
* GitHub issue body.
|
|
104
|
-
*
|
|
105
|
-
* @default location.href + header.meta[description]
|
|
106
|
-
*/
|
|
107
|
-
body?: string;
|
|
108
|
-
/**
|
|
109
|
-
* Localization language key.
|
|
110
|
-
*
|
|
111
|
-
* @default navigator.language
|
|
112
|
-
*/
|
|
113
|
-
language?: Lang;
|
|
114
|
-
/**
|
|
115
|
-
* Pagination size, with maximum 100.
|
|
116
|
-
*
|
|
117
|
-
* @default 10
|
|
118
|
-
*/
|
|
119
|
-
perPage?: number;
|
|
120
|
-
/**
|
|
121
|
-
* Comment sorting direction.
|
|
122
|
-
* Available values are `last` and `first`.
|
|
123
|
-
*
|
|
124
|
-
* @default "last"
|
|
125
|
-
*/
|
|
126
|
-
pagerDirection?: "last" | "first";
|
|
127
|
-
/**
|
|
128
|
-
* By default, Gitalk will create a corresponding github issue for your every single page automatically when the logined user is belong to the admin users.
|
|
129
|
-
* You can create it manually by setting this option to true.
|
|
130
|
-
*
|
|
131
|
-
* @default false
|
|
132
|
-
*/
|
|
133
|
-
createIssueManually?: boolean;
|
|
134
|
-
/**
|
|
135
|
-
* Enable hot key (cmd|ctrl + enter) submit comment.
|
|
136
|
-
*
|
|
137
|
-
* @default true
|
|
138
|
-
*/
|
|
139
|
-
enableHotKey?: boolean;
|
|
140
|
-
/**
|
|
141
|
-
* Facebook-like distraction free mode.
|
|
142
|
-
*
|
|
143
|
-
* @default false
|
|
144
|
-
*/
|
|
145
|
-
distractionFreeMode?: boolean;
|
|
146
|
-
/**
|
|
147
|
-
* Comment list animation.
|
|
148
|
-
*
|
|
149
|
-
* @default
|
|
150
|
-
* ```ts
|
|
151
|
-
* {
|
|
152
|
-
* staggerDelayBy: 150,
|
|
153
|
-
* appearAnimation: 'accordionVertical',
|
|
154
|
-
* enterAnimation: 'accordionVertical',
|
|
155
|
-
* leaveAnimation: 'accordionVertical',
|
|
156
|
-
* }
|
|
157
|
-
* ```
|
|
158
|
-
* @link https://github.com/joshwcomeau/react-flip-move/blob/master/documentation/enter_leave_animations.md
|
|
159
|
-
*/
|
|
160
|
-
flipMoveOptions?: FlipMove.FlipMoveProps;
|
|
161
|
-
/**
|
|
162
|
-
* GitHub oauth request reverse proxy for CORS.
|
|
163
|
-
* [Why need this?](https://github.com/isaacs/github/issues/330)
|
|
164
|
-
*
|
|
165
|
-
* @default "https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token"
|
|
166
|
-
*/
|
|
167
|
-
proxy?: string;
|
|
168
|
-
/**
|
|
169
|
-
* Default user field if comments' author is not provided
|
|
170
|
-
*
|
|
171
|
-
* @default
|
|
172
|
-
* ```ts
|
|
173
|
-
* {
|
|
174
|
-
* avatar_url: "//avatars1.githubusercontent.com/u/29697133?s=50",
|
|
175
|
-
* login: "null",
|
|
176
|
-
* html_url: ""
|
|
177
|
-
* }
|
|
178
|
-
*/
|
|
179
|
-
defaultUser?: CommentType["user"];
|
|
180
|
-
/**
|
|
181
|
-
* Default user field if comments' author is not provided
|
|
182
|
-
*
|
|
183
|
-
* @deprecated use `defaultUser`
|
|
184
|
-
*/
|
|
185
|
-
defaultAuthor?: IssueCommentsQLResponse["repository"]["issue"]["comments"]["nodes"][number]["author"];
|
|
186
|
-
/**
|
|
187
|
-
* Callback method invoked when updating the number of comments.
|
|
188
|
-
*
|
|
189
|
-
* @param count comments number
|
|
190
|
-
*/
|
|
191
|
-
updateCountCallback?: (count: number) => void;
|
|
192
|
-
/**
|
|
193
|
-
* Callback method invoked when a new issue is successfully created.
|
|
194
|
-
*
|
|
195
|
-
* @param issue created issue
|
|
196
|
-
*/
|
|
197
|
-
onCreateIssue?: (issue: IssueType) => void;
|
|
198
|
-
/**
|
|
199
|
-
* Callback method invoked when a new comment is successfully created.
|
|
200
|
-
*
|
|
201
|
-
* @param comment created comment
|
|
202
|
-
*/
|
|
203
|
-
onCreateComment?: (comment: CommentType) => void;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
43
|
const isModernBrowser = supportsCSSVariables() && supportsES2020();
|
|
207
44
|
|
|
208
45
|
const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
@@ -227,15 +64,11 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
227
64
|
createIssueManually = false,
|
|
228
65
|
enableHotKey = true,
|
|
229
66
|
distractionFreeMode = false,
|
|
230
|
-
flipMoveOptions =
|
|
231
|
-
|
|
232
|
-
appearAnimation: "accordionVertical",
|
|
233
|
-
enterAnimation: "accordionVertical",
|
|
234
|
-
leaveAnimation: "accordionVertical",
|
|
235
|
-
},
|
|
236
|
-
proxy = "https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token",
|
|
67
|
+
flipMoveOptions = DEFAULT_FLIP_MOVE_OPTIONS,
|
|
68
|
+
proxy = DEFAULT_PROXY,
|
|
237
69
|
defaultUser: propsDefaultUser,
|
|
238
70
|
defaultAuthor: propsDefaultAuthor,
|
|
71
|
+
collapsedHeight: propsCollapsedHeight,
|
|
239
72
|
updateCountCallback,
|
|
240
73
|
onCreateIssue,
|
|
241
74
|
onCreateComment,
|
|
@@ -258,7 +91,6 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
258
91
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
259
92
|
const [inputComment, setInputComment] = useState<string>("");
|
|
260
93
|
const [isInputFocused, setIsInputFocused] = useState<boolean>(false);
|
|
261
|
-
const [isPreviewComment, setIsPreviewComment] = useState<boolean>(false);
|
|
262
94
|
|
|
263
95
|
const [commentsCount, setCommentsCount] = useState<number>(0);
|
|
264
96
|
const [commentsCursor, setCommentsCursor] = useState("");
|
|
@@ -283,12 +115,22 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
283
115
|
: DEFAULT_USER,
|
|
284
116
|
[propsDefaultAuthor, propsDefaultUser],
|
|
285
117
|
);
|
|
286
|
-
|
|
287
|
-
|
|
118
|
+
const collapsedHeight =
|
|
119
|
+
propsCollapsedHeight && propsCollapsedHeight > 0
|
|
120
|
+
? propsCollapsedHeight
|
|
121
|
+
: undefined;
|
|
288
122
|
|
|
289
123
|
const [alert, setAlert] = useState<string>("");
|
|
290
124
|
|
|
291
125
|
const polyglot = useMemo(() => i18n(language), [language]);
|
|
126
|
+
const i18nContextValue: I18nContextValue = useMemo(
|
|
127
|
+
() => ({
|
|
128
|
+
language,
|
|
129
|
+
polyglot,
|
|
130
|
+
dateFnsLocaleMap: DATE_FNS_LOCALE_MAP,
|
|
131
|
+
}),
|
|
132
|
+
[language, polyglot],
|
|
133
|
+
);
|
|
292
134
|
|
|
293
135
|
const {
|
|
294
136
|
data: accessToken = localStorage.getItem(ACCESS_TOKEN_KEY) ?? undefined,
|
|
@@ -619,7 +461,7 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
619
461
|
data: localComments = [],
|
|
620
462
|
mutate: setLocalComments,
|
|
621
463
|
loading: createIssueCommentLoading,
|
|
622
|
-
|
|
464
|
+
runAsync: runCreateIssueComment,
|
|
623
465
|
} = useRequest(
|
|
624
466
|
async (): Promise<CommentType[]> => {
|
|
625
467
|
const { number: currentIssueNumber } = issue as IssueType;
|
|
@@ -715,35 +557,6 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
715
557
|
updateCountCallback?.(commentsCount);
|
|
716
558
|
}, [commentsCount, updateCountCallback]);
|
|
717
559
|
|
|
718
|
-
const {
|
|
719
|
-
data: commentHtml = "",
|
|
720
|
-
mutate: setCommentHtml,
|
|
721
|
-
loading: getCommentHtmlLoading,
|
|
722
|
-
run: runGetCommentHtml,
|
|
723
|
-
cancel: cancelGetCommentHtml,
|
|
724
|
-
} = useRequest(
|
|
725
|
-
async () => {
|
|
726
|
-
const getPreviewedHtmlRes = await octokit.request("POST /markdown", {
|
|
727
|
-
text: inputComment,
|
|
728
|
-
});
|
|
729
|
-
|
|
730
|
-
if (getPreviewedHtmlRes.status === 200) {
|
|
731
|
-
const _commentHtml = getPreviewedHtmlRes.data;
|
|
732
|
-
return _commentHtml;
|
|
733
|
-
} else {
|
|
734
|
-
setAlert(`Preview rendered comment failed: ${getPreviewedHtmlRes}`);
|
|
735
|
-
logger.e(`Preview rendered comment failed:`, getPreviewedHtmlRes);
|
|
736
|
-
return "";
|
|
737
|
-
}
|
|
738
|
-
},
|
|
739
|
-
{
|
|
740
|
-
manual: true,
|
|
741
|
-
onBefore: () => {
|
|
742
|
-
setCommentHtml("");
|
|
743
|
-
},
|
|
744
|
-
},
|
|
745
|
-
);
|
|
746
|
-
|
|
747
560
|
const { loading: likeOrDislikeCommentLoading, run: runLikeOrDislikeComment } =
|
|
748
561
|
useRequest(
|
|
749
562
|
async (like: boolean, commentId: number, reactionId?: number) => {
|
|
@@ -863,29 +676,6 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
863
676
|
[initialized, issue],
|
|
864
677
|
);
|
|
865
678
|
|
|
866
|
-
const hidePopup = useCallback((e: MouseEvent) => {
|
|
867
|
-
const target = e.target as HTMLElement;
|
|
868
|
-
if (target && hasClassInParent(target, "gt-user", "gt-popup")) {
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
document.removeEventListener("click", hidePopup);
|
|
872
|
-
setShowPopup(false);
|
|
873
|
-
}, []);
|
|
874
|
-
|
|
875
|
-
const onShowOrHidePopup: React.MouseEventHandler<HTMLDivElement> = (e) => {
|
|
876
|
-
e.preventDefault();
|
|
877
|
-
e.stopPropagation();
|
|
878
|
-
|
|
879
|
-
setShowPopup((visible) => {
|
|
880
|
-
if (visible) {
|
|
881
|
-
document.removeEventListener("click", hidePopup);
|
|
882
|
-
} else {
|
|
883
|
-
document.addEventListener("click", hidePopup);
|
|
884
|
-
}
|
|
885
|
-
return !visible;
|
|
886
|
-
});
|
|
887
|
-
};
|
|
888
|
-
|
|
889
679
|
const onLogin = () => {
|
|
890
680
|
const url = getAuthorizeUrl(clientID);
|
|
891
681
|
window.location.href = url;
|
|
@@ -911,6 +701,14 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
911
701
|
setIsInputFocused(false);
|
|
912
702
|
};
|
|
913
703
|
|
|
704
|
+
useEffect(() => {
|
|
705
|
+
const textarea = textareaRef.current;
|
|
706
|
+
if (textarea) {
|
|
707
|
+
textarea.style.height = "auto";
|
|
708
|
+
textarea.style.height = `${textarea.scrollHeight + 2}px`;
|
|
709
|
+
}
|
|
710
|
+
}, [inputComment]);
|
|
711
|
+
|
|
914
712
|
const onCommentInputKeyDown: React.KeyboardEventHandler<
|
|
915
713
|
HTMLTextAreaElement
|
|
916
714
|
> = (e) => {
|
|
@@ -919,41 +717,35 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
919
717
|
}
|
|
920
718
|
};
|
|
921
719
|
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
} else {
|
|
929
|
-
setIsPreviewComment(true);
|
|
930
|
-
runGetCommentHtml();
|
|
931
|
-
}
|
|
932
|
-
};
|
|
720
|
+
const onReplyComment: CommentProps["onReply"] = useCallback(
|
|
721
|
+
(repliedComment) => {
|
|
722
|
+
const { body: repliedCommentBody = "", user: repliedCommentUser } =
|
|
723
|
+
repliedComment;
|
|
724
|
+
let repliedCommentBodyArray = repliedCommentBody.split("\n");
|
|
725
|
+
const repliedCommentUsername = repliedCommentUser?.login;
|
|
933
726
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
if (repliedCommentUsername) {
|
|
941
|
-
repliedCommentBodyArray.unshift(`@${repliedCommentUsername}`);
|
|
942
|
-
}
|
|
943
|
-
repliedCommentBodyArray = repliedCommentBodyArray.map(
|
|
944
|
-
(text) => `> ${text}`,
|
|
945
|
-
);
|
|
946
|
-
|
|
947
|
-
if (inputComment) {
|
|
948
|
-
repliedCommentBodyArray.unshift("", "");
|
|
949
|
-
}
|
|
727
|
+
if (repliedCommentUsername) {
|
|
728
|
+
repliedCommentBodyArray.unshift(`@${repliedCommentUsername}`);
|
|
729
|
+
}
|
|
730
|
+
repliedCommentBodyArray = repliedCommentBodyArray.map(
|
|
731
|
+
(text) => `> ${text}`,
|
|
732
|
+
);
|
|
950
733
|
|
|
951
|
-
|
|
734
|
+
setInputComment((prevComment) => {
|
|
735
|
+
if (prevComment) {
|
|
736
|
+
repliedCommentBodyArray.unshift("", "");
|
|
737
|
+
}
|
|
738
|
+
repliedCommentBodyArray.push("");
|
|
739
|
+
const newComment = `${prevComment}${repliedCommentBodyArray.join("\n")}`;
|
|
740
|
+
return newComment;
|
|
741
|
+
});
|
|
952
742
|
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
743
|
+
setTimeout(() => {
|
|
744
|
+
textareaRef.current?.focus();
|
|
745
|
+
});
|
|
746
|
+
},
|
|
747
|
+
[],
|
|
748
|
+
);
|
|
957
749
|
|
|
958
750
|
if (!isModernBrowser) {
|
|
959
751
|
logger.e(
|
|
@@ -980,284 +772,111 @@ const Gitalk: React.FC<GitalkProps> = (props) => {
|
|
|
980
772
|
return null;
|
|
981
773
|
}
|
|
982
774
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
<div
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
const renderIssueNotInitialized = () => {
|
|
993
|
-
return (
|
|
994
|
-
<div className="gt-no-init" key="no-init">
|
|
995
|
-
<p
|
|
996
|
-
dangerouslySetInnerHTML={{
|
|
997
|
-
__html: polyglot.t("no-found-related", {
|
|
998
|
-
link: `<a href="https://github.com/${owner}/${repo}/issues" target="_blank" rel="noopener noreferrer">Issues</a>`,
|
|
999
|
-
}),
|
|
1000
|
-
}}
|
|
1001
|
-
/>
|
|
1002
|
-
<p>
|
|
1003
|
-
{polyglot.t("please-contact", {
|
|
1004
|
-
user: admin.map((u) => `@${u}`).join(" "),
|
|
1005
|
-
})}
|
|
1006
|
-
</p>
|
|
1007
|
-
{isAdmin ? (
|
|
1008
|
-
<p>
|
|
1009
|
-
<Button
|
|
1010
|
-
onClick={runCreateIssue}
|
|
1011
|
-
isLoading={createIssueLoading}
|
|
1012
|
-
text={polyglot.t("init-issue")}
|
|
1013
|
-
/>
|
|
1014
|
-
</p>
|
|
1015
|
-
) : null}
|
|
1016
|
-
{!user && (
|
|
1017
|
-
<Button
|
|
1018
|
-
className="gt-btn-login"
|
|
1019
|
-
onClick={onLogin}
|
|
1020
|
-
text={polyglot.t("login-with-github")}
|
|
1021
|
-
/>
|
|
1022
|
-
)}
|
|
1023
|
-
</div>
|
|
1024
|
-
);
|
|
1025
|
-
};
|
|
775
|
+
return (
|
|
776
|
+
<I18nContext.Provider value={i18nContextValue}>
|
|
777
|
+
<div
|
|
778
|
+
className={`gt-container${isInputFocused ? " gt-input-focused" : ""} ${className}`}
|
|
779
|
+
{...restProps}
|
|
780
|
+
>
|
|
781
|
+
{/* Alert */}
|
|
782
|
+
{alert && <div className="gt-error">{alert}</div>}
|
|
1026
783
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
<Svg className="gt-ico-github" icon={Github} />
|
|
1040
|
-
</a>
|
|
1041
|
-
)}
|
|
1042
|
-
<div className="gt-header-comment">
|
|
1043
|
-
<textarea
|
|
1044
|
-
ref={textareaRef}
|
|
1045
|
-
className="gt-header-textarea"
|
|
1046
|
-
style={{ display: isPreviewComment ? "none" : undefined }}
|
|
1047
|
-
value={inputComment}
|
|
1048
|
-
onChange={(e) => setInputComment(e.target.value)}
|
|
1049
|
-
onFocus={onCommentInputFocus}
|
|
1050
|
-
onBlur={onCommentInputBlur}
|
|
1051
|
-
onKeyDown={onCommentInputKeyDown}
|
|
1052
|
-
placeholder={polyglot.t("leave-a-comment")}
|
|
1053
|
-
/>
|
|
1054
|
-
<div
|
|
1055
|
-
className="gt-header-preview markdown-body"
|
|
1056
|
-
style={{ display: isPreviewComment ? undefined : "none" }}
|
|
1057
|
-
dangerouslySetInnerHTML={{
|
|
1058
|
-
__html: commentHtml,
|
|
1059
|
-
}}
|
|
1060
|
-
/>
|
|
1061
|
-
<div className="gt-header-controls">
|
|
1062
|
-
<a
|
|
1063
|
-
className="gt-header-controls-tip"
|
|
1064
|
-
href="https://guides.github.com/features/mastering-markdown/"
|
|
1065
|
-
target="_blank"
|
|
1066
|
-
rel="noopener noreferrer"
|
|
1067
|
-
>
|
|
1068
|
-
<Svg
|
|
1069
|
-
className="gt-ico-tip"
|
|
1070
|
-
icon={Tip}
|
|
1071
|
-
text={polyglot.t("support-markdown")}
|
|
1072
|
-
/>
|
|
1073
|
-
</a>
|
|
1074
|
-
|
|
1075
|
-
<Button
|
|
1076
|
-
className="gt-btn-preview gt-btn--secondary"
|
|
1077
|
-
onClick={onCommentInputPreview}
|
|
1078
|
-
text={
|
|
1079
|
-
isPreviewComment ? polyglot.t("edit") : polyglot.t("preview")
|
|
1080
|
-
}
|
|
1081
|
-
isLoading={getCommentHtmlLoading}
|
|
1082
|
-
disabled={false}
|
|
1083
|
-
/>
|
|
1084
|
-
|
|
1085
|
-
{user ? (
|
|
1086
|
-
<Button
|
|
1087
|
-
className="gt-btn-public"
|
|
1088
|
-
onClick={runCreateIssueComment}
|
|
1089
|
-
text={polyglot.t("comment")}
|
|
1090
|
-
isLoading={createIssueCommentLoading}
|
|
1091
|
-
disabled={createIssueCommentLoading || !inputComment}
|
|
1092
|
-
/>
|
|
1093
|
-
) : (
|
|
1094
|
-
<Button
|
|
1095
|
-
className="gt-btn-login"
|
|
1096
|
-
onClick={onLogin}
|
|
1097
|
-
text={polyglot.t("login-with-github")}
|
|
784
|
+
{initialized ? (
|
|
785
|
+
issueCreated ? (
|
|
786
|
+
<>
|
|
787
|
+
{/* Meta */}
|
|
788
|
+
<Meta
|
|
789
|
+
issue={issue}
|
|
790
|
+
user={user}
|
|
791
|
+
commentsCount={commentsCount}
|
|
792
|
+
pagerDirection={commentsPagerDirection}
|
|
793
|
+
onPagerDirectionChange={setCommentsPagerDirection}
|
|
794
|
+
onLogin={onLogin}
|
|
795
|
+
onLogout={onLogout}
|
|
1098
796
|
/>
|
|
1099
|
-
)}
|
|
1100
|
-
</div>
|
|
1101
|
-
</div>
|
|
1102
|
-
</div>
|
|
1103
|
-
);
|
|
1104
|
-
};
|
|
1105
|
-
|
|
1106
|
-
// Why forwardRef? https://www.npmjs.com/package/react-flip-move#usage-with-functional-components
|
|
1107
|
-
const CommentWithForwardedRef = forwardRef<
|
|
1108
|
-
HTMLDivElement,
|
|
1109
|
-
{ comment: CommentType }
|
|
1110
|
-
>(({ comment }, ref) => {
|
|
1111
|
-
const {
|
|
1112
|
-
id: commentId,
|
|
1113
|
-
user: commentAuthor,
|
|
1114
|
-
reactionsHeart: commentReactionsHeart,
|
|
1115
|
-
} = comment;
|
|
1116
|
-
|
|
1117
|
-
const commentAuthorName = commentAuthor?.login;
|
|
1118
|
-
const isAuthor =
|
|
1119
|
-
!!user && !!commentAuthorName && user.login === commentAuthorName;
|
|
1120
|
-
const isAdmin =
|
|
1121
|
-
!!commentAuthorName &&
|
|
1122
|
-
!!admin.find(
|
|
1123
|
-
(username) =>
|
|
1124
|
-
username.toLowerCase() === commentAuthorName.toLowerCase(),
|
|
1125
|
-
);
|
|
1126
|
-
const heartReactionId = commentReactionsHeart?.nodes.find(
|
|
1127
|
-
(node) => node.user.login === user?.login,
|
|
1128
|
-
)?.databaseId;
|
|
1129
|
-
|
|
1130
|
-
return (
|
|
1131
|
-
<div ref={ref}>
|
|
1132
|
-
<Comment
|
|
1133
|
-
comment={comment}
|
|
1134
|
-
isAuthor={isAuthor}
|
|
1135
|
-
isAdmin={isAdmin}
|
|
1136
|
-
onReply={onReplyComment}
|
|
1137
|
-
onLike={(like) => {
|
|
1138
|
-
runLikeOrDislikeComment(like, commentId, heartReactionId);
|
|
1139
|
-
}}
|
|
1140
|
-
likeLoading={likeOrDislikeCommentLoading}
|
|
1141
|
-
/>
|
|
1142
|
-
</div>
|
|
1143
|
-
);
|
|
1144
|
-
});
|
|
1145
797
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
</div>
|
|
1168
|
-
) : null}
|
|
1169
|
-
</div>
|
|
1170
|
-
);
|
|
1171
|
-
};
|
|
1172
|
-
|
|
1173
|
-
const renderMeta = () => {
|
|
1174
|
-
const isDesc = commentsPagerDirection === "last";
|
|
798
|
+
{/* Comment textarea */}
|
|
799
|
+
<CommentTextarea
|
|
800
|
+
ref={textareaRef}
|
|
801
|
+
value={inputComment}
|
|
802
|
+
onChange={(e) => setInputComment(e.target.value)}
|
|
803
|
+
onFocus={onCommentInputFocus}
|
|
804
|
+
onBlur={onCommentInputBlur}
|
|
805
|
+
onKeyDown={onCommentInputKeyDown}
|
|
806
|
+
placeholder={polyglot.t("leave-a-comment")}
|
|
807
|
+
octokit={octokit}
|
|
808
|
+
user={user}
|
|
809
|
+
onLogin={onLogin}
|
|
810
|
+
onCreateComment={async () => {
|
|
811
|
+
await runCreateIssueComment();
|
|
812
|
+
}}
|
|
813
|
+
createCommentLoading={createIssueCommentLoading}
|
|
814
|
+
onPreviewError={(e) => {
|
|
815
|
+
setAlert(`Preview rendered comment failed: ${e}`);
|
|
816
|
+
logger.e(`Preview rendered comment failed:`, e);
|
|
817
|
+
}}
|
|
818
|
+
/>
|
|
1175
819
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
onClick={() => setCommentsPagerDirection("last")}
|
|
1201
|
-
text={polyglot.t("sort-desc")}
|
|
1202
|
-
/>,
|
|
1203
|
-
]
|
|
1204
|
-
: null}
|
|
1205
|
-
{user ? (
|
|
1206
|
-
<Action
|
|
1207
|
-
className="gt-action-logout"
|
|
1208
|
-
onClick={onLogout}
|
|
1209
|
-
text={polyglot.t("logout")}
|
|
820
|
+
{/* Comments */}
|
|
821
|
+
<CommentsList
|
|
822
|
+
comments={loadedComments}
|
|
823
|
+
commentsCount={commentsCount}
|
|
824
|
+
onGetComments={runGetComments}
|
|
825
|
+
getCommentsLoading={getCommentsLoading}
|
|
826
|
+
flipMoveOptions={flipMoveOptions}
|
|
827
|
+
user={user}
|
|
828
|
+
admin={admin}
|
|
829
|
+
onReply={onReplyComment}
|
|
830
|
+
onLike={runLikeOrDislikeComment}
|
|
831
|
+
likeLoading={likeOrDislikeCommentLoading}
|
|
832
|
+
collapsedHeight={collapsedHeight}
|
|
833
|
+
/>
|
|
834
|
+
</>
|
|
835
|
+
) : (
|
|
836
|
+
// Issue not created placeholder
|
|
837
|
+
<div className="gt-no-init" key="no-init">
|
|
838
|
+
<p
|
|
839
|
+
dangerouslySetInnerHTML={{
|
|
840
|
+
__html: polyglot.t("no-found-related", {
|
|
841
|
+
link: `<a href="https://github.com/${owner}/${repo}/issues" target="_blank" rel="noopener noreferrer">Issues</a>`,
|
|
842
|
+
}),
|
|
843
|
+
}}
|
|
1210
844
|
/>
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
845
|
+
<p>
|
|
846
|
+
{polyglot.t("please-contact", {
|
|
847
|
+
user: admin.map((u) => `@${u}`).join(" "),
|
|
848
|
+
})}
|
|
849
|
+
</p>
|
|
850
|
+
{isAdmin ? (
|
|
851
|
+
<p>
|
|
852
|
+
<Button
|
|
853
|
+
onClick={runCreateIssue}
|
|
854
|
+
isLoading={createIssueLoading}
|
|
855
|
+
text={polyglot.t("init-issue")}
|
|
856
|
+
/>
|
|
857
|
+
</p>
|
|
858
|
+
) : null}
|
|
859
|
+
{!user && (
|
|
860
|
+
<Button
|
|
861
|
+
className="gt-btn-login"
|
|
862
|
+
onClick={onLogin}
|
|
863
|
+
text={polyglot.t("login-with-github")}
|
|
864
|
+
/>
|
|
865
|
+
)}
|
|
1226
866
|
</div>
|
|
867
|
+
)
|
|
868
|
+
) : (
|
|
869
|
+
// Loading issue placeholder
|
|
870
|
+
<div className="gt-initing">
|
|
871
|
+
<i className="gt-loader" />
|
|
872
|
+
<p className="gt-initing-text">{polyglot.t("init")}</p>
|
|
1227
873
|
</div>
|
|
1228
874
|
)}
|
|
1229
|
-
<div className="gt-user">
|
|
1230
|
-
<div
|
|
1231
|
-
className={`gt-user-inner${showPopup ? " is--poping" : ""}`}
|
|
1232
|
-
onClick={onShowOrHidePopup}
|
|
1233
|
-
>
|
|
1234
|
-
<span className="gt-user-name">
|
|
1235
|
-
{user?.login ?? polyglot.t("anonymous")}
|
|
1236
|
-
</span>
|
|
1237
|
-
<Svg className="gt-ico-arrdown" icon={ArrowDown} />
|
|
1238
|
-
</div>
|
|
1239
|
-
</div>
|
|
1240
|
-
</div>
|
|
1241
|
-
);
|
|
1242
|
-
};
|
|
1243
|
-
|
|
1244
|
-
return (
|
|
1245
|
-
<I18nContext.Provider
|
|
1246
|
-
value={{ language, polyglot, dateFnsLocaleMap: DATE_FNS_LOCALE_MAP }}
|
|
1247
|
-
>
|
|
1248
|
-
<div
|
|
1249
|
-
className={`gt-container${isInputFocused ? " gt-input-focused" : ""} ${className}`}
|
|
1250
|
-
{...restProps}
|
|
1251
|
-
>
|
|
1252
|
-
{alert && <div className="gt-error">{alert}</div>}
|
|
1253
|
-
{initialized
|
|
1254
|
-
? issueCreated
|
|
1255
|
-
? [renderMeta(), renderHeader(), renderCommentList()]
|
|
1256
|
-
: renderIssueNotInitialized()
|
|
1257
|
-
: renderInitializing()}
|
|
1258
875
|
</div>
|
|
1259
876
|
</I18nContext.Provider>
|
|
1260
877
|
);
|
|
1261
878
|
};
|
|
1262
879
|
|
|
880
|
+
export type { GitalkProps };
|
|
881
|
+
|
|
1263
882
|
export default Gitalk;
|