@waline/client 2.5.1 → 2.6.2

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.
Files changed (59) hide show
  1. package/dist/component.esm.js +1 -1
  2. package/dist/component.esm.js.map +1 -1
  3. package/dist/component.js +1 -1
  4. package/dist/component.js.map +1 -1
  5. package/dist/legacy.d.ts +41 -0
  6. package/dist/legacy.js +1 -1
  7. package/dist/legacy.js.map +1 -1
  8. package/dist/pageview.cjs.js +1 -1
  9. package/dist/pageview.cjs.js.map +1 -1
  10. package/dist/pageview.esm.js +1 -1
  11. package/dist/pageview.esm.js.map +1 -1
  12. package/dist/pageview.js +1 -1
  13. package/dist/pageview.js.map +1 -1
  14. package/dist/shim.d.ts +42 -1
  15. package/dist/shim.esm.d.ts +42 -1
  16. package/dist/shim.esm.js +1 -1
  17. package/dist/shim.esm.js.map +1 -1
  18. package/dist/shim.js +1 -1
  19. package/dist/shim.js.map +1 -1
  20. package/dist/waline.cjs.d.ts +42 -1
  21. package/dist/waline.cjs.js +1 -1
  22. package/dist/waline.cjs.js.map +1 -1
  23. package/dist/waline.css +1 -1
  24. package/dist/waline.css.map +1 -1
  25. package/dist/waline.d.ts +42 -1
  26. package/dist/waline.esm.d.ts +42 -1
  27. package/dist/waline.esm.js +1 -1
  28. package/dist/waline.esm.js.map +1 -1
  29. package/dist/waline.js +1 -1
  30. package/dist/waline.js.map +1 -1
  31. package/package.json +31 -32
  32. package/src/comment.ts +7 -2
  33. package/src/components/CommentBox.vue +88 -71
  34. package/src/components/CommentCard.vue +19 -17
  35. package/src/components/ImageWall.vue +34 -22
  36. package/src/composables/userInfo.ts +1 -1
  37. package/src/config/default.ts +93 -1
  38. package/src/config/i18n/en.ts +1 -0
  39. package/src/config/i18n/generate.ts +1 -0
  40. package/src/config/i18n/jp.ts +1 -0
  41. package/src/config/i18n/pt-BR.ts +1 -0
  42. package/src/config/i18n/ru.ts +1 -0
  43. package/src/config/i18n/vi-VN.ts +1 -0
  44. package/src/config/i18n/zh-CN.ts +1 -0
  45. package/src/config/i18n/zh-TW.ts +3 -2
  46. package/src/init.ts +0 -4
  47. package/src/pageview.ts +7 -2
  48. package/src/styles/emoji.scss +23 -0
  49. package/src/styles/gif.scss +6 -5
  50. package/src/typings/base.ts +40 -0
  51. package/src/typings/locale.ts +1 -0
  52. package/src/typings/waline.ts +8 -0
  53. package/src/utils/config.ts +5 -2
  54. package/src/utils/fetch.ts +3 -0
  55. package/src/utils/index.ts +0 -2
  56. package/src/utils/markedMathExtension.ts +1 -0
  57. package/LICENSE +0 -339
  58. package/src/utils/fetchGif.ts +0 -63
  59. package/src/utils/throttle.ts +0 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/client",
3
- "version": "2.5.1",
3
+ "version": "2.6.2",
4
4
  "description": "client for waline comment system",
5
5
  "keywords": [
6
6
  "valine",
@@ -56,6 +56,15 @@
56
56
  "dist",
57
57
  "src"
58
58
  ],
59
+ "scripts": {
60
+ "build": "pnpm rollup && pnpm style",
61
+ "clean": "rimraf ./dist",
62
+ "dev": "vite -c config/vite.config.js",
63
+ "lint": "eslint --ext .ts,.vue .",
64
+ "prepublish": "pnpm clean && pnpm build",
65
+ "rollup": "rollup -c ./config/rollup.config.js",
66
+ "style": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed"
67
+ },
59
68
  "browserslist": {
60
69
  "production": [
61
70
  ">0.5%",
@@ -71,41 +80,31 @@
71
80
  ]
72
81
  },
73
82
  "dependencies": {
74
- "@vueuse/core": "^8.5.0",
83
+ "@vueuse/core": "^8.9.4",
75
84
  "autosize": "^5.0.1",
76
- "marked": "^4.0.16",
77
- "vue": "^3.2.35"
85
+ "marked": "^4.0.18",
86
+ "vue": "^3.2.37"
78
87
  },
79
88
  "devDependencies": {
80
- "@babel/core": "^7.18.0",
81
- "@babel/preset-env": "^7.18.0",
82
- "@rollup/plugin-babel": "^5.3.1",
83
- "@rollup/plugin-commonjs": "^22.0.0",
84
- "@rollup/plugin-node-resolve": "^13.3.0",
85
- "@rollup/plugin-replace": "^4.0.0",
86
- "@types/autosize": "^4.0.1",
87
- "@types/marked": "^4.0.3",
88
- "@types/node": "^17.0.35",
89
- "@vitejs/plugin-vue": "^2.3.3",
90
- "@vue/compiler-sfc": "^3.2.35",
91
- "rimraf": "^3.0.2",
92
- "rollup": "^2.74.1",
93
- "rollup-plugin-dts": "^4.2.1",
94
- "rollup-plugin-terser": "^7.0.2",
95
- "rollup-plugin-ts": "^2.0.7",
96
- "typescript": "^4.6.4",
97
- "vite": "^2.9.9"
89
+ "@babel/core": "7.18.9",
90
+ "@babel/preset-env": "7.18.9",
91
+ "@rollup/plugin-babel": "5.3.1",
92
+ "@rollup/plugin-commonjs": "22.0.1",
93
+ "@rollup/plugin-node-resolve": "13.3.0",
94
+ "@rollup/plugin-replace": "4.0.0",
95
+ "@types/autosize": "4.0.1",
96
+ "@types/marked": "4.0.3",
97
+ "@types/node": "18.0.6",
98
+ "@vitejs/plugin-vue": "3.0.1",
99
+ "rimraf": "3.0.2",
100
+ "rollup": "2.77.0",
101
+ "rollup-plugin-dts": "4.2.2",
102
+ "rollup-plugin-terser": "7.0.2",
103
+ "rollup-plugin-ts": "3.0.2",
104
+ "typescript": "4.7.4",
105
+ "vite": "3.0.2"
98
106
  },
99
107
  "engines": {
100
108
  "node": ">=14"
101
- },
102
- "scripts": {
103
- "build": "pnpm rollup && pnpm style",
104
- "clean": "rimraf ./dist",
105
- "dev": "vite -c config/vite.config.js",
106
- "lint": "eslint --ext .ts,.vue .",
107
- "prepublish": "pnpm clean && pnpm build",
108
- "rollup": "rollup -c ./config/rollup.config.js",
109
- "style": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed"
110
109
  }
111
- }
110
+ }
package/src/comment.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import { useUserInfo } from './composables';
2
- import { decodePath, errorHandler, fetchCommentCount } from './utils';
2
+ import {
3
+ decodePath,
4
+ errorHandler,
5
+ fetchCommentCount,
6
+ getServerURL,
7
+ } from './utils';
3
8
  import type { WalineAbort } from './typings';
4
9
 
5
10
  export interface WalineCommentCountOptions {
@@ -54,7 +59,7 @@ WalineCommentCountOptions): WalineAbort => {
54
59
 
55
60
  if (elements.length)
56
61
  void fetchCommentCount({
57
- serverURL,
62
+ serverURL: getServerURL(serverURL),
58
63
  paths: Array.from(elements).map((element) =>
59
64
  decodePath(element.dataset.path || element.getAttribute('id') || path)
60
65
  ),
@@ -6,12 +6,21 @@
6
6
  <CloseIcon :size="14" />
7
7
  </button>
8
8
 
9
- <img :src="userInfo.avatar" alt="avatar" />
9
+ <a
10
+ href="#"
11
+ class="wl-login-nick"
12
+ aria-label="Profile"
13
+ :title="locale.profile"
14
+ @click="onProfile"
15
+ >
16
+ <img :src="userInfo.avatar" alt="avatar" />
17
+ </a>
10
18
  </div>
11
19
  <a
12
20
  href="#"
13
21
  class="wl-login-nick"
14
22
  aria-label="Profile"
23
+ :title="locale.profile"
15
24
  @click="onProfile"
16
25
  v-text="userInfo.display_name"
17
26
  />
@@ -22,7 +31,7 @@
22
31
  v-if="config.login !== 'force' && config.meta.length && !isLogin"
23
32
  :class="['wl-header', `item${config.meta.length}`]"
24
33
  >
25
- <div v-for="kind in config.meta" class="wl-header-item" :key="kind">
34
+ <div v-for="kind in config.meta" :key="kind" class="wl-header-item">
26
35
  <label
27
36
  :for="kind"
28
37
  v-text="
@@ -33,34 +42,35 @@
33
42
  "
34
43
  />
35
44
  <input
45
+ :id="`wl-${kind}`"
36
46
  :ref="
37
47
  (element) => {
38
48
  if (element) inputRefs[kind] = element as HTMLInputElement;
39
49
  }
40
50
  "
41
- :id="`wl-${kind}`"
51
+ v-model="userMeta[kind]"
42
52
  :class="['wl-input', `wl-${kind}`]"
43
53
  :name="kind"
44
54
  :type="kind === 'mail' ? 'email' : 'text'"
45
- v-model="userMeta[kind]"
46
55
  />
47
56
  </div>
48
57
  </div>
49
58
 
50
59
  <textarea
51
- class="wl-editor"
52
- ref="editorRef"
53
60
  id="wl-edit"
54
- :placeholder="replyUser ? `@${replyUser}` : locale.placeholder"
61
+ ref="editorRef"
55
62
  v-model="editor"
63
+ class="wl-editor"
64
+ :placeholder="replyUser ? `@${replyUser}` : locale.placeholder"
56
65
  @keydown="onKeyDown"
57
66
  @drop="onDrop"
58
67
  @paste="onPaste"
59
68
  />
60
69
 
61
- <div class="wl-preview" v-show="showPreview">
70
+ <div v-show="showPreview" class="wl-preview">
62
71
  <hr />
63
72
  <h4>{{ locale.preview }}:</h4>
73
+ <!-- eslint-disable-next-line vue/no-v-html -->
64
74
  <div class="wl-content" v-html="previewText" />
65
75
  </div>
66
76
 
@@ -89,6 +99,7 @@
89
99
  </button>
90
100
 
91
101
  <button
102
+ v-if="config.search"
92
103
  ref="gifButtonRef"
93
104
  class="wl-action"
94
105
  :class="{ actived: showGif }"
@@ -99,9 +110,9 @@
99
110
  </button>
100
111
 
101
112
  <input
113
+ id="wl-image-upload"
102
114
  ref="imageUploadRef"
103
115
  class="upload"
104
- id="wl-image-upload"
105
116
  type="file"
106
117
  accept=".png,.jpg,.jpeg,.webp,.bmp,.gif"
107
118
  @change="onChange"
@@ -168,9 +179,9 @@
168
179
  :class="{ display: showGif }"
169
180
  >
170
181
  <input
182
+ ref="gifSearchInputRef"
171
183
  type="text"
172
184
  :placeholder="locale.gifSearchPlaceholder"
173
- ref="gifSearchInputRef"
174
185
  @input="onGifSearch"
175
186
  />
176
187
 
@@ -192,10 +203,13 @@
192
203
  class="wl-emoji-popup"
193
204
  :class="{ display: showEmoji }"
194
205
  >
195
- <template v-for="(config, index) in emoji.tabs" :key="config.name">
206
+ <template
207
+ v-for="(emojiItem, index) in emoji.tabs"
208
+ :key="emojiItem.name"
209
+ >
196
210
  <div v-if="index === emojiTabIndex" class="wl-tab-wrapper">
197
211
  <button
198
- v-for="key in config.items"
212
+ v-for="key in emojiItem.items"
199
213
  :key="key"
200
214
  :title="key"
201
215
  @click="insert(`:${key}:`)"
@@ -213,17 +227,17 @@
213
227
  </template>
214
228
  <div v-if="emoji.tabs.length > 1" class="wl-tabs">
215
229
  <button
216
- v-for="(config, index) in emoji.tabs"
217
- :key="config.name"
230
+ v-for="(emojiItem, index) in emoji.tabs"
231
+ :key="emojiItem.name"
218
232
  class="wl-tab"
219
233
  :class="{ active: emojiTabIndex === index }"
220
234
  @click="emojiTabIndex = index"
221
235
  >
222
236
  <img
223
237
  class="wl-emoji"
224
- :src="config.icon"
225
- :alt="config.name"
226
- :title="config.name"
238
+ :src="emojiItem.icon"
239
+ :alt="emojiItem.name"
240
+ :title="emojiItem.name"
227
241
  loading="lazy"
228
242
  referrerPolicy="no-referrer"
229
243
  />
@@ -245,6 +259,7 @@
245
259
  </template>
246
260
 
247
261
  <script lang="ts">
262
+ import { useDebounceFn } from '@vueuse/core';
248
263
  import autosize from 'autosize';
249
264
  import {
250
265
  computed,
@@ -252,11 +267,11 @@ import {
252
267
  inject,
253
268
  onMounted,
254
269
  onUnmounted,
270
+ reactive,
255
271
  ref,
256
272
  watch,
257
273
  } from 'vue';
258
274
 
259
- import ImageWall from './ImageWall.vue';
260
275
  import {
261
276
  CloseIcon,
262
277
  EmojiIcon,
@@ -266,25 +281,25 @@ import {
266
281
  LoadingIcon,
267
282
  GifIcon,
268
283
  } from './Icons';
284
+ import ImageWall from './ImageWall.vue';
269
285
  import { useEditor, useUserMeta, useUserInfo } from '../composables';
270
286
  import {
271
- fetchGif,
272
287
  getEmojis,
273
288
  getImagefromDataTransfer,
274
289
  getWordNumber,
275
290
  parseEmoji,
276
291
  parseMarkdown,
277
292
  postComment,
278
- throttle,
279
293
  } from '../utils';
280
294
 
281
295
  import type { ComputedRef, DeepReadonly } from 'vue';
282
- import type { WalineCommentData, WalineImageUploader } from '../typings';
283
296
  import type {
284
- FetchGifResponse,
285
- WalineConfig,
286
- WalineEmojiConfig,
287
- } from '../utils';
297
+ WalineCommentData,
298
+ WalineImageUploader,
299
+ WalineSearchOptions,
300
+ WalineSearchResult,
301
+ } from '../typings';
302
+ import type { WalineConfig, WalineEmojiConfig } from '../utils';
288
303
 
289
304
  export default defineComponent({
290
305
  name: 'CommentBox',
@@ -342,11 +357,11 @@ export default defineComponent({
342
357
  const showPreview = ref(false);
343
358
  const previewText = ref('');
344
359
  const wordNumber = ref(0);
345
- const gifData = ref<{
346
- cursor: string;
347
- loading: boolean;
348
- list: FetchGifResponse['results'];
349
- }>({ cursor: '', loading: true, list: [] });
360
+
361
+ const searchResults = reactive({
362
+ loading: true,
363
+ list: [] as WalineSearchResult[],
364
+ });
350
365
 
351
366
  const wordLimit = ref(0);
352
367
  const isWordNumberLegal = ref(false);
@@ -452,6 +467,7 @@ export default defineComponent({
452
467
  // check nick
453
468
  if (requiredMeta.indexOf('nick') > -1 && !comment.nick) {
454
469
  inputRefs.value.nick?.focus();
470
+
455
471
  return alert(locale.value.nickError);
456
472
  }
457
473
 
@@ -462,12 +478,14 @@ export default defineComponent({
462
478
  !/^\w(?:[\w._-]*\w)?@(?:\w(?:[\w-]*\w)?\.)*\w+$/.exec(comment.mail))
463
479
  ) {
464
480
  inputRefs.value.mail?.focus();
481
+
465
482
  return alert(locale.value.mailError);
466
483
  }
467
484
 
468
485
  // check comment
469
486
  if (!comment.comment) {
470
487
  editorRef.value?.focus();
488
+
471
489
  return;
472
490
  }
473
491
 
@@ -503,7 +521,6 @@ export default defineComponent({
503
521
 
504
522
  if (resp.errmsg) return alert(resp.errmsg);
505
523
 
506
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
507
524
  emit('submit', resp.data!);
508
525
 
509
526
  editor.value = '';
@@ -576,24 +593,7 @@ export default defineComponent({
576
593
  `width=${width},height=${height},left=${left},top=${top},scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no`
577
594
  );
578
595
 
579
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
580
596
  handler?.postMessage({ type: 'TOKEN', data: userInfo.value!.token }, '*');
581
-
582
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
583
- const receiver = ({ data }: any): void => {
584
- if (!data || data.type !== 'profile') return;
585
-
586
- userInfo.value = { ...userInfo.value, ...data };
587
-
588
- [localStorage, sessionStorage]
589
- .filter((store) => store.getItem('WALINE_USER'))
590
- .forEach((store) =>
591
- store.setItem('WALINE_USER', JSON.stringify(userInfo))
592
- );
593
- window.removeEventListener('message', receiver);
594
- };
595
-
596
- window.addEventListener('message', receiver);
597
597
  };
598
598
 
599
599
  const popupHandler = (event: MouseEvent): void => {
@@ -614,31 +614,30 @@ export default defineComponent({
614
614
  const { scrollTop, clientHeight, scrollHeight } =
615
615
  event.target as HTMLDivElement;
616
616
  const percent = (clientHeight + scrollTop) / scrollHeight;
617
+ const searchOptions = config.value.search as WalineSearchOptions;
618
+ const keyword = gifSearchInputRef.value?.value || '';
617
619
 
618
- if (percent < 0.9 || gifData.value.loading) return;
620
+ if (percent < 0.9 || searchResults.loading) return;
619
621
 
620
- gifData.value.loading = true;
622
+ searchResults.loading = true;
621
623
 
622
- const data = await fetchGif({
623
- keyword: gifSearchInputRef.value?.value || '',
624
- pos: gifData.value.cursor,
625
- }).finally(() => {
626
- gifData.value.loading = false;
627
- });
624
+ searchResults.list.push(
625
+ ...(searchOptions.more
626
+ ? await searchOptions.more(keyword, searchResults.list.length)
627
+ : await searchOptions.search(keyword))
628
+ );
628
629
 
629
- gifData.value.cursor = data.next;
630
- gifData.value.list = gifData.value.list.concat(data.results);
630
+ searchResults.loading = false;
631
631
 
632
632
  setTimeout(() => {
633
633
  (event.target as HTMLDivElement).scrollTop = scrollTop;
634
634
  }, 50);
635
635
  };
636
636
 
637
- const onGifSearch = throttle(async (event: Event) => {
638
- gifData.value.cursor = '';
639
- gifData.value.list = [];
637
+ const onGifSearch = useDebounceFn((event: Event) => {
638
+ searchResults.list = [];
640
639
  onImageWallScroll(event);
641
- });
640
+ }, 300);
642
641
 
643
642
  // update wordNumber
644
643
  watch(
@@ -668,17 +667,36 @@ export default defineComponent({
668
667
  watch(showGif, async (showGif) => {
669
668
  if (!showGif) return;
670
669
 
671
- gifData.value.loading = true;
672
- const data = await fetchGif({ keyword: '' }).finally(() => {
673
- gifData.value.loading = false;
674
- });
670
+ const searchOptions = config.value.search as WalineSearchOptions;
671
+
672
+ // clear input
673
+ if (gifSearchInputRef.value) gifSearchInputRef.value.value = '';
674
+
675
+ searchResults.loading = true;
676
+
677
+ searchResults.list = searchOptions.default
678
+ ? await searchOptions.default()
679
+ : await searchOptions.search('');
675
680
 
676
- gifData.value.cursor = data.next;
677
- gifData.value.list = gifData.value.list.concat(data.results);
681
+ searchResults.loading = false;
678
682
  });
679
683
 
684
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
685
+ const onMessageRecive = ({ data }: any): void => {
686
+ if (!data || data.type !== 'profile') return;
687
+
688
+ userInfo.value = { ...userInfo.value, ...data.data };
689
+
690
+ [localStorage, sessionStorage]
691
+ .filter((store) => store.getItem('WALINE_USER'))
692
+ .forEach((store) =>
693
+ store.setItem('WALINE_USER', JSON.stringify(userInfo))
694
+ );
695
+ };
696
+
680
697
  onMounted(() => {
681
698
  document.body.addEventListener('click', popupHandler);
699
+ window.addEventListener('message', onMessageRecive);
682
700
 
683
701
  // watch editor
684
702
  watch(
@@ -694,9 +712,7 @@ export default defineComponent({
694
712
  });
695
713
  wordNumber.value = getWordNumber(value);
696
714
 
697
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
698
715
  if (value) autosize(editorRef.value!);
699
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
700
716
  else autosize.destroy(editorRef.value!);
701
717
  },
702
718
  { immediate: true }
@@ -717,6 +733,7 @@ export default defineComponent({
717
733
 
718
734
  onUnmounted(() => {
719
735
  document.body.removeEventListener('click', popupHandler);
736
+ window.removeEventListener('message', onMessageRecive);
720
737
  });
721
738
 
722
739
  return {
@@ -756,7 +773,7 @@ export default defineComponent({
756
773
  showEmoji,
757
774
 
758
775
  // gif
759
- gifData,
776
+ gifData: searchResults,
760
777
  showGif,
761
778
 
762
779
  // image
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="wl-item" :id="comment.objectId">
2
+ <div :id="comment.objectId" class="wl-item">
3
3
  <div class="wl-user" aria-hidden="true">
4
4
  <img v-if="comment.avatar" :src="comment.avatar" />
5
5
  <VerifiedIcon v-if="comment.type" />
@@ -33,8 +33,8 @@
33
33
 
34
34
  <div class="wl-comment-actions">
35
35
  <button
36
- class="wl-delete"
37
36
  v-if="isAdmin || isOwner"
37
+ class="wl-delete"
38
38
  @click="$emit('delete', comment)"
39
39
  >
40
40
  <DeleteIcon />
@@ -42,8 +42,8 @@
42
42
 
43
43
  <button
44
44
  class="wl-like"
45
- @click="$emit('like', comment)"
46
45
  :title="like ? locale.cancelLike : locale.like"
46
+ @click="$emit('like', comment)"
47
47
  >
48
48
  <LikeIcon :active="like" />
49
49
  <span v-if="'like' in comment" v-text="comment.like" />
@@ -64,6 +64,7 @@
64
64
  <span v-if="comment.browser" v-text="comment.browser" />
65
65
  <span v-if="comment.os" v-text="comment.os" />
66
66
  </div>
67
+ <!-- eslint-disable-next-line vue/no-v-html -->
67
68
  <div class="wl-content" v-html="comment.comment" />
68
69
 
69
70
  <div v-if="isAdmin" class="wl-admin-actions">
@@ -79,8 +80,8 @@
79
80
  </span>
80
81
 
81
82
  <button
82
- class="wl-btn wl-sticky"
83
83
  v-if="isAdmin && !comment.rid"
84
+ class="wl-btn wl-sticky"
84
85
  @click="$emit('sticky', comment)"
85
86
  >
86
87
  {{ comment.sticky ? 'unsticky' : 'sticky' }}
@@ -89,9 +90,9 @@
89
90
 
90
91
  <div v-if="isReplyingCurrent" class="wl-reply-wrapper">
91
92
  <CommentBox
92
- :replyId="comment.objectId"
93
- :replyUser="comment.nick"
94
- :rootId="rootId"
93
+ :reply-id="comment.objectId"
94
+ :reply-user="comment.nick"
95
+ :root-id="rootId"
95
96
  @submit="$emit('submit', $event)"
96
97
  @cancel-reply="$emit('reply', null)"
97
98
  />
@@ -102,7 +103,7 @@
102
103
  :key="child.objectId"
103
104
  :comment="child"
104
105
  :reply="reply"
105
- :rootId="rootId"
106
+ :root-id="rootId"
106
107
  @reply="$emit('reply', $event)"
107
108
  @submit="$emit('submit', $event)"
108
109
  @like="$emit('like', $event)"
@@ -129,6 +130,14 @@ import type { WalineComment, WalineCommentStatus } from '../typings';
129
130
  const commentStatus: WalineCommentStatus[] = ['approved', 'waiting', 'spam'];
130
131
 
131
132
  export default defineComponent({
133
+ components: {
134
+ CommentBox,
135
+ DeleteIcon,
136
+ LikeIcon,
137
+ ReplyIcon,
138
+ VerifiedIcon,
139
+ },
140
+
132
141
  props: {
133
142
  comment: {
134
143
  type: Object as PropType<WalineComment>,
@@ -140,17 +149,10 @@ export default defineComponent({
140
149
  },
141
150
  reply: {
142
151
  type: Object as PropType<WalineComment | null>,
152
+ default: null,
143
153
  },
144
154
  },
145
155
 
146
- components: {
147
- CommentBox,
148
- DeleteIcon,
149
- LikeIcon,
150
- ReplyIcon,
151
- VerifiedIcon,
152
- },
153
-
154
156
  emits: ['submit', 'reply', 'like', 'delete', 'status', 'sticky'],
155
157
 
156
158
  setup(props) {
@@ -163,7 +165,7 @@ export default defineComponent({
163
165
  const locale = computed(() => config.value.locale);
164
166
 
165
167
  const link = computed(() => {
166
- let { link } = props.comment;
168
+ const { link } = props.comment;
167
169
 
168
170
  return link ? (isLinkHttp(link) ? link : `https://${link}`) : '';
169
171
  });