@waline/client 2.11.1 → 2.11.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.
Files changed (84) hide show
  1. package/LICENSE +339 -0
  2. package/dist/api.cjs +2 -0
  3. package/dist/api.cjs.map +1 -0
  4. package/dist/api.d.cts +1 -0
  5. package/dist/api.d.mts +1 -0
  6. package/dist/api.d.ts +1 -0
  7. package/dist/api.mjs +2 -0
  8. package/dist/api.mjs.map +1 -0
  9. package/dist/comment.cjs +2 -0
  10. package/dist/comment.cjs.map +1 -0
  11. package/dist/comment.d.cts +39 -0
  12. package/dist/comment.d.mts +39 -0
  13. package/dist/comment.d.ts +39 -0
  14. package/dist/comment.js +2 -0
  15. package/dist/comment.js.map +1 -0
  16. package/dist/comment.mjs +2 -0
  17. package/dist/comment.mjs.map +1 -0
  18. package/dist/component.mjs +1 -1
  19. package/dist/component.mjs.map +1 -1
  20. package/dist/legacy.umd.d.ts +57 -30
  21. package/dist/legacy.umd.js +1 -1
  22. package/dist/legacy.umd.js.map +1 -1
  23. package/dist/pageview.cjs +1 -1
  24. package/dist/pageview.cjs.map +1 -1
  25. package/dist/pageview.js +1 -1
  26. package/dist/pageview.js.map +1 -1
  27. package/dist/pageview.mjs +1 -1
  28. package/dist/pageview.mjs.map +1 -1
  29. package/dist/shim.cjs +1 -1
  30. package/dist/shim.cjs.map +1 -1
  31. package/dist/shim.d.cts +58 -31
  32. package/dist/shim.d.mts +58 -31
  33. package/dist/shim.mjs +1 -1
  34. package/dist/shim.mjs.map +1 -1
  35. package/dist/waline-meta.css +1 -1
  36. package/dist/waline-meta.css.map +1 -1
  37. package/dist/waline.cjs +1 -1
  38. package/dist/waline.cjs.map +1 -1
  39. package/dist/waline.css +1 -1
  40. package/dist/waline.css.map +1 -1
  41. package/dist/waline.d.cts +58 -31
  42. package/dist/waline.d.mts +58 -31
  43. package/dist/waline.d.ts +58 -31
  44. package/dist/waline.js +1 -1
  45. package/dist/waline.js.map +1 -1
  46. package/dist/waline.mjs +1 -1
  47. package/dist/waline.mjs.map +1 -1
  48. package/package.json +34 -13
  49. package/src/api/articleCounter.ts +53 -0
  50. package/src/api/comment.ts +147 -0
  51. package/src/api/commentCount.ts +30 -0
  52. package/src/api/index.ts +6 -0
  53. package/src/api/login.ts +53 -0
  54. package/src/api/pageview.ts +41 -0
  55. package/src/api/recentComment.ts +29 -0
  56. package/src/api/utils.ts +23 -0
  57. package/src/comment.ts +2 -10
  58. package/src/compact/convert.ts +2 -2
  59. package/src/components/ArticleReaction.vue +50 -50
  60. package/src/components/CommentBox.vue +24 -49
  61. package/src/components/ImageWall.vue +1 -1
  62. package/src/components/Waline.vue +9 -9
  63. package/src/composables/index.ts +3 -1
  64. package/src/composables/recaptchaV3.ts +23 -0
  65. package/src/composables/userInfo.ts +1 -12
  66. package/src/composables/vote.ts +5 -2
  67. package/src/entrys/api.ts +1 -0
  68. package/src/entrys/comment.ts +2 -0
  69. package/src/init.ts +0 -11
  70. package/src/pageview.ts +2 -7
  71. package/src/styles/card.scss +1 -1
  72. package/src/styles/helpers/_svg.scss +12 -12
  73. package/src/styles/index.scss +1 -1
  74. package/src/styles/meta.scss +10 -14
  75. package/src/styles/reaction.scss +24 -18
  76. package/src/typings/base.ts +33 -7
  77. package/src/typings/locale.ts +37 -23
  78. package/src/utils/config.ts +9 -3
  79. package/src/utils/index.ts +0 -1
  80. package/src/widgets/recentComments.ts +2 -1
  81. package/src/components/RecaptchaV3/IReCaptchaOptions.ts +0 -6
  82. package/src/components/RecaptchaV3/README.md +0 -3
  83. package/src/components/RecaptchaV3/RecaptchaVuePlugin.ts +0 -86
  84. package/src/utils/fetch.ts +0 -310
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/client",
3
- "version": "2.11.1",
3
+ "version": "2.11.3",
4
4
  "description": "client for waline comment system",
5
5
  "keywords": [
6
6
  "valine",
@@ -25,6 +25,11 @@
25
25
  "require": "./dist/shim.cjs",
26
26
  "default": "./dist/shim.cjs"
27
27
  },
28
+ "./api": {
29
+ "types": "./dist/api.d.ts",
30
+ "require": "./dist/api.cjs",
31
+ "default": "./dist/api.mjs"
32
+ },
28
33
  "./component": {
29
34
  "default": "./dist/component.mjs"
30
35
  },
@@ -34,6 +39,12 @@
34
39
  "require": "./dist/waline.cjs",
35
40
  "default": "./dist/waline.js"
36
41
  },
42
+ "./comment": {
43
+ "types": "./dist/comment.d.ts",
44
+ "import": "./dist/comment.mjs",
45
+ "require": "./dist/comment.cjs",
46
+ "default": "./dist/comment.js"
47
+ },
37
48
  "./pageview": {
38
49
  "types": "./dist/pageview.d.ts",
39
50
  "import": "./dist/pageview.mjs",
@@ -42,9 +53,20 @@
42
53
  },
43
54
  "./waline.css": "./dist/waline.css",
44
55
  "./waline-meta.css": "./dist/waline-meta.css",
56
+ "./dist/api": {
57
+ "types": "./dist/api.d.ts",
58
+ "require": "./dist/api.cjs",
59
+ "default": "./dist/api.mjs"
60
+ },
45
61
  "./dist/component": {
46
62
  "default": "./dist/component.mjs"
47
63
  },
64
+ "./dist/comment": {
65
+ "types": "./dist/comment.d.ts",
66
+ "import": "./dist/comment.mjs",
67
+ "require": "./dist/comment.cjs",
68
+ "default": "./dist/comment.js"
69
+ },
48
70
  "./dist/pageview": {
49
71
  "types": "./dist/pageview.d.ts",
50
72
  "import": "./dist/pageview.mjs",
@@ -71,17 +93,6 @@
71
93
  "dist",
72
94
  "src"
73
95
  ],
74
- "scripts": {
75
- "build": "pnpm rollup && pnpm style",
76
- "clean": "rimraf ./dist",
77
- "dev": "vite -c config/vite.config.js",
78
- "lint": "eslint --ext .ts,.vue .",
79
- "prepublishOnly": "pnpm clean && pnpm build",
80
- "rollup": "rollup -c ./config/rollup.config.js",
81
- "style": "pnpm style:main && pnpm style:meta",
82
- "style:main": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed",
83
- "style:meta": "sass ./src/styles/meta.scss ./dist/waline-meta.css --style=compressed"
84
- },
85
96
  "browserslist": {
86
97
  "production": [
87
98
  ">0.5%",
@@ -124,5 +135,15 @@
124
135
  },
125
136
  "engines": {
126
137
  "node": ">=14"
138
+ },
139
+ "scripts": {
140
+ "build": "pnpm rollup && pnpm style",
141
+ "clean": "rimraf ./dist",
142
+ "dev": "vite -c config/vite.config.js",
143
+ "lint": "eslint --ext .ts,.vue .",
144
+ "rollup": "rollup -c ./config/rollup.config.js",
145
+ "style": "pnpm style:main && pnpm style:meta",
146
+ "style:main": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed",
147
+ "style:meta": "sass ./src/styles/meta.scss ./dist/waline-meta.css --style=compressed"
127
148
  }
128
- }
149
+ }
@@ -0,0 +1,53 @@
1
+ import { JSON_HEADERS, errorCheck } from './utils';
2
+
3
+ export interface FetchArticleCounterOptions {
4
+ serverURL: string;
5
+ lang: string;
6
+ paths: string[];
7
+ signal: AbortSignal;
8
+ type: string[];
9
+ }
10
+
11
+ export const fetchArticleCounter = ({
12
+ serverURL,
13
+ lang,
14
+ paths,
15
+ type,
16
+ signal,
17
+ }: FetchArticleCounterOptions): Promise<
18
+ Record<string, number>[] | Record<string, number> | number[] | number
19
+ > =>
20
+ fetch(
21
+ `${serverURL}/article?path=${encodeURIComponent(
22
+ paths.join(',')
23
+ )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,
24
+ { signal }
25
+ )
26
+ .then(
27
+ (resp) =>
28
+ resp.json() as Promise<Record<string, number>[] | number[] | number>
29
+ )
30
+ .then((data) => errorCheck(data, 'article count'));
31
+
32
+ export interface UpdateArticleCounterOptions {
33
+ serverURL: string;
34
+ lang: string;
35
+ path: string;
36
+ type: string;
37
+ action?: 'inc' | 'desc';
38
+ }
39
+
40
+ export const updateArticleCounter = ({
41
+ serverURL,
42
+ lang,
43
+ path,
44
+ type,
45
+ action,
46
+ }: UpdateArticleCounterOptions): Promise<number> =>
47
+ fetch(`${serverURL}/article?lang=${lang}`, {
48
+ method: 'POST',
49
+ headers: JSON_HEADERS,
50
+ body: JSON.stringify({ path, type, action }),
51
+ })
52
+ .then((resp) => resp.json() as Promise<number>)
53
+ .then((data) => errorCheck(data, 'article count'));
@@ -0,0 +1,147 @@
1
+ import { JSON_HEADERS, errorCheck } from './utils';
2
+ import type { WalineComment, WalineCommentData } from '../typings';
3
+
4
+ export interface FetchCommentOptions {
5
+ serverURL: string;
6
+ path: string;
7
+ page: number;
8
+ pageSize: number;
9
+ sortBy: string;
10
+ signal: AbortSignal;
11
+ token?: string;
12
+ lang: string;
13
+ }
14
+
15
+ export interface CommentData {
16
+ count: number;
17
+ data: WalineComment[];
18
+ totalPages: number;
19
+ }
20
+
21
+ export const fetchComment = ({
22
+ serverURL,
23
+ lang,
24
+ path,
25
+ page,
26
+ pageSize,
27
+ sortBy,
28
+ signal,
29
+ token,
30
+ }: FetchCommentOptions): Promise<CommentData> => {
31
+ const headers: Record<string, string> = {};
32
+
33
+ if (token) headers.Authorization = `Bearer ${token}`;
34
+
35
+ return fetch(
36
+ `${serverURL}/comment?path=${encodeURIComponent(
37
+ path
38
+ )}&pageSize=${pageSize}&page=${page}&lang=${lang}&sortBy=${sortBy}`,
39
+ { signal, headers }
40
+ )
41
+ .then((resp) => resp.json() as Promise<CommentData>)
42
+ .then((data) => errorCheck(data, 'comment data'));
43
+ };
44
+
45
+ export interface PostCommentOptions {
46
+ serverURL: string;
47
+ lang: string;
48
+ token?: string;
49
+ comment: WalineCommentData;
50
+ }
51
+
52
+ export interface PostCommentResponse {
53
+ data?: WalineComment;
54
+ errmsg?: string;
55
+ }
56
+
57
+ export const postComment = ({
58
+ serverURL,
59
+ lang,
60
+ token,
61
+ comment,
62
+ }: PostCommentOptions): Promise<PostCommentResponse> => {
63
+ const headers: Record<string, string> = {
64
+ // eslint-disable-next-line @typescript-eslint/naming-convention
65
+ 'Content-Type': 'application/json',
66
+ };
67
+
68
+ if (token) headers.Authorization = `Bearer ${token}`;
69
+
70
+ if (comment.eid) {
71
+ return fetch(`${serverURL}/comment/${comment.eid}?lang=${lang}`, {
72
+ method: 'PUT',
73
+ headers,
74
+ body: JSON.stringify(comment),
75
+ }).then((resp) => resp.json() as Promise<PostCommentResponse>);
76
+ }
77
+
78
+ return fetch(`${serverURL}/comment?lang=${lang}`, {
79
+ method: 'POST',
80
+ headers,
81
+ body: JSON.stringify(comment),
82
+ }).then((resp) => resp.json() as Promise<PostCommentResponse>);
83
+ };
84
+
85
+ export interface DeleteCommentOptions {
86
+ serverURL: string;
87
+ lang: string;
88
+ token: string;
89
+ objectId: string | number;
90
+ }
91
+
92
+ export const deleteComment = ({
93
+ serverURL,
94
+ lang,
95
+ token,
96
+ objectId,
97
+ }: DeleteCommentOptions): Promise<void> =>
98
+ fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {
99
+ method: 'DELETE',
100
+ headers: {
101
+ Authorization: `Bearer ${token}`,
102
+ },
103
+ }).then((resp) => resp.json() as Promise<void>);
104
+
105
+ export interface LikeCommentOptions {
106
+ serverURL: string;
107
+ lang: string;
108
+ objectId: number | string;
109
+ like: boolean;
110
+ }
111
+
112
+ export const likeComment = ({
113
+ serverURL,
114
+ lang,
115
+ objectId,
116
+ like,
117
+ }: LikeCommentOptions): Promise<void> =>
118
+ fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {
119
+ method: 'PUT',
120
+ headers: JSON_HEADERS,
121
+ body: JSON.stringify({ like }),
122
+ }).then((resp) => resp.json() as Promise<void>);
123
+
124
+ export interface UpdateCommentOptions {
125
+ serverURL: string;
126
+ lang: string;
127
+ token: string;
128
+ objectId: number | string;
129
+ status?: 'approved' | 'waiting' | 'spam';
130
+ sticky?: number;
131
+ }
132
+
133
+ export const updateComment = ({
134
+ serverURL,
135
+ lang,
136
+ token,
137
+ objectId,
138
+ ...data
139
+ }: UpdateCommentOptions): Promise<void> =>
140
+ fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {
141
+ method: 'PUT',
142
+ headers: {
143
+ ...JSON_HEADERS,
144
+ Authorization: `Bearer ${token}`,
145
+ },
146
+ body: JSON.stringify(data),
147
+ }).then((resp) => resp.json() as Promise<void>);
@@ -0,0 +1,30 @@
1
+ import { errorCheck } from './utils';
2
+
3
+ export interface FetchCommentCountOptions {
4
+ serverURL: string;
5
+ lang: string;
6
+ paths: string[];
7
+ signal: AbortSignal;
8
+ }
9
+
10
+ export const fetchCommentCount = ({
11
+ serverURL,
12
+ lang,
13
+ paths,
14
+ signal,
15
+ }: FetchCommentCountOptions): Promise<number[]> => {
16
+ const headers: Record<string, string> = {};
17
+
18
+ return (
19
+ fetch(
20
+ `${serverURL}/comment?type=count&url=${encodeURIComponent(
21
+ paths.join(',')
22
+ )}&lang=${lang}`,
23
+ { signal, headers }
24
+ )
25
+ .then((resp) => resp.json() as Promise<number | number[]>)
26
+ .then((data) => errorCheck(data, 'comment count'))
27
+ // TODO: Improve this API
28
+ .then((counts) => (Array.isArray(counts) ? counts : [counts]))
29
+ );
30
+ };
@@ -0,0 +1,6 @@
1
+ export * from './articleCounter';
2
+ export * from './comment';
3
+ export * from './commentCount';
4
+ export * from './login';
5
+ export * from './pageview';
6
+ export * from './recentComment';
@@ -0,0 +1,53 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+
3
+ export interface LoginOptions {
4
+ lang: string;
5
+ serverURL: string;
6
+ }
7
+
8
+ export interface UserInfo {
9
+ // eslint-disable-next-line @typescript-eslint/naming-convention
10
+ display_name: string;
11
+ email: string;
12
+ url: string;
13
+ token: string;
14
+ avatar: string;
15
+ mailMd5: string;
16
+ objectId: string | number;
17
+ type: 'administrator' | 'guest';
18
+ }
19
+
20
+ export const login = ({
21
+ lang,
22
+ serverURL,
23
+ }: LoginOptions): Promise<UserInfo & { remember: boolean }> => {
24
+ const width = 450;
25
+ const height = 450;
26
+ const left = (window.innerWidth - width) / 2;
27
+ const top = (window.innerHeight - height) / 2;
28
+
29
+ const handler = window.open(
30
+ `${serverURL}/ui/login?lng=${encodeURIComponent(lang)}`,
31
+ '_blank',
32
+ `width=${width},height=${height},left=${left},top=${top},scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no`
33
+ );
34
+
35
+ handler?.postMessage({ type: 'TOKEN', data: null }, '*');
36
+
37
+ return new Promise((resolve) => {
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ const receiver = ({ data }: any): void => {
40
+ if (!data || typeof data !== 'object' || data.type !== 'userInfo') return;
41
+
42
+ if (data.data.token) {
43
+ handler?.close();
44
+
45
+ window.removeEventListener('message', receiver);
46
+
47
+ resolve(data.data as UserInfo & { remember: boolean });
48
+ }
49
+ };
50
+
51
+ window.addEventListener('message', receiver);
52
+ });
53
+ };
@@ -0,0 +1,41 @@
1
+ import { fetchArticleCounter, updateArticleCounter } from './articleCounter';
2
+
3
+ interface FetchPageviewOptions {
4
+ serverURL: string;
5
+ lang: string;
6
+ paths: string[];
7
+ signal: AbortSignal;
8
+ }
9
+
10
+ export const fetchPageviews = ({
11
+ serverURL,
12
+ lang,
13
+ paths,
14
+ signal,
15
+ }: FetchPageviewOptions): Promise<number[]> =>
16
+ fetchArticleCounter({
17
+ serverURL,
18
+ lang,
19
+ paths,
20
+ type: ['time'],
21
+ signal,
22
+ })
23
+ // TODO: Improve this API
24
+ .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<
25
+ number[]
26
+ >;
27
+
28
+ export interface UpdatePageviewOptions {
29
+ serverURL: string;
30
+ lang: string;
31
+ path: string;
32
+ action?: 'inc' | 'desc';
33
+ }
34
+
35
+ export const updatePageviews = (
36
+ options: UpdatePageviewOptions
37
+ ): Promise<number> =>
38
+ updateArticleCounter({
39
+ ...options,
40
+ type: 'time',
41
+ });
@@ -0,0 +1,29 @@
1
+ import { errorCheck } from './utils';
2
+ import type { WalineComment } from '../typings';
3
+
4
+ export interface FetchRecentCommentOptions {
5
+ serverURL: string;
6
+ lang: string;
7
+ count: number;
8
+ signal: AbortSignal;
9
+ token?: string;
10
+ }
11
+
12
+ export const fetchRecentComment = ({
13
+ serverURL,
14
+ lang,
15
+ count,
16
+ signal,
17
+ token,
18
+ }: FetchRecentCommentOptions): Promise<WalineComment[]> => {
19
+ const headers: Record<string, string> = {};
20
+
21
+ if (token) headers.Authorization = `Bearer ${token}`;
22
+
23
+ return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {
24
+ signal,
25
+ headers,
26
+ })
27
+ .then((resp) => resp.json() as Promise<WalineComment[]>)
28
+ .then((data) => errorCheck(data, 'recent comment'));
29
+ };
@@ -0,0 +1,23 @@
1
+ export interface FetchErrorData {
2
+ errno: number;
3
+ errmsg: string;
4
+ }
5
+
6
+ export const JSON_HEADERS: Record<string, string> = {
7
+ // eslint-disable-next-line @typescript-eslint/naming-convention
8
+ 'Content-Type': 'application/json',
9
+ };
10
+
11
+ export const errorCheck = <T = unknown>(
12
+ data: T | FetchErrorData,
13
+ name = ''
14
+ ): T => {
15
+ if (typeof data === 'object' && (data as FetchErrorData).errno)
16
+ throw new TypeError(
17
+ `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${
18
+ (data as FetchErrorData).errmsg
19
+ }`
20
+ );
21
+
22
+ return data as T;
23
+ };
package/src/comment.ts CHANGED
@@ -1,10 +1,5 @@
1
- import { useUserInfo } from './composables';
2
- import {
3
- decodePath,
4
- errorHandler,
5
- fetchCommentCount,
6
- getServerURL,
7
- } from './utils';
1
+ import { fetchCommentCount } from './api';
2
+ import { decodePath, errorHandler, getServerURL } from './utils';
8
3
  import type { WalineAbort } from './typings';
9
4
 
10
5
  export interface WalineCommentCountOptions {
@@ -55,8 +50,6 @@ WalineCommentCountOptions): WalineAbort => {
55
50
  // comment count
56
51
  const elements = document.querySelectorAll<HTMLElement>(selector);
57
52
 
58
- const userInfo = useUserInfo();
59
-
60
53
  if (elements.length)
61
54
  void fetchCommentCount({
62
55
  serverURL: getServerURL(serverURL),
@@ -65,7 +58,6 @@ WalineCommentCountOptions): WalineAbort => {
65
58
  ),
66
59
  lang,
67
60
  signal: controller.signal,
68
- token: userInfo.value?.token,
69
61
  })
70
62
  .then((counts) => {
71
63
  elements.forEach((element, index) => {
@@ -1,11 +1,11 @@
1
1
  import { warning } from './logger';
2
2
  import { resolveOldEmojiMap } from './valine';
3
-
4
- import type { DeprecatedValineOptions } from './valine';
5
3
  import {
6
4
  DROPPED_OPTIONS_WHICH_CAN_NOT_BE_POLYFILLED,
7
5
  DROPPED_OPTIONS_WHICH_CAN_STILL_BE_POLYFILLED,
8
6
  } from './dropped';
7
+
8
+ import type { DeprecatedValineOptions } from './valine';
9
9
  import type { DeprecatedWalineOptions } from './v1';
10
10
  import type { WalineInitOptions } from '../typings';
11
11
 
@@ -1,18 +1,18 @@
1
1
  <template>
2
- <div v-if="reaction && reaction.length" class="wl-reaction">
3
- <h4>{{ locale.reactionTitle }}</h4>
2
+ <div v-if="reaction.length" class="wl-reaction">
3
+ <h4 v-text="locale.reactionTitle" />
4
4
  <ul>
5
5
  <li
6
6
  v-for="(item, index) in reaction"
7
7
  :key="index"
8
- :class="item.active ? 'active' : ''"
9
- @click="onVote(index)"
8
+ :class="{ active: item.active }"
9
+ @click="vote(index)"
10
10
  >
11
- <div class="wl-reaction__img">
11
+ <div class="wl-reaction-img">
12
12
  <img :src="item.icon" :alt="item.desc" />
13
- <div class="wl-reaction__votes">{{ item.vote }}</div>
13
+ <div class="wl-reaction-votes">{{ item.vote }}</div>
14
14
  </div>
15
- <div class="wl-reaction__text">{{ item.desc }}</div>
15
+ <div class="wl-reaction-text">{{ item.desc }}</div>
16
16
  </li>
17
17
  </ul>
18
18
  </div>
@@ -28,12 +28,10 @@ import {
28
28
  onUnmounted,
29
29
  ref,
30
30
  } from 'vue';
31
- import {
32
- fetchArticleCounter,
33
- updateArticleCounter,
34
- WalineConfig,
35
- } from '../utils';
36
- import { useVoteStorage } from '../composables/vote';
31
+ import { fetchArticleCounter, updateArticleCounter } from '../api';
32
+ import { VOTE_IDENTIFIER, VOTE_INDEX, useVoteStorage } from '../composables';
33
+ import type { WalineConfig } from '../utils';
34
+ import type { WalineLocale } from '../typings';
37
35
 
38
36
  interface ReactionItem {
39
37
  icon: string;
@@ -46,59 +44,55 @@ export default defineComponent({
46
44
  setup() {
47
45
  const votes = ref<ReactionItem['vote'][]>([]);
48
46
  const voteStorage = useVoteStorage();
49
- const config = inject<ComputedRef<WalineConfig>>(
50
- 'config'
51
- ) as ComputedRef<WalineConfig>;
47
+ const config = inject<ComputedRef<WalineConfig>>('config')!;
52
48
  const locale = computed(() => config.value.locale);
53
49
  const reaction = computed((): ReactionItem[] => {
54
- const { path } = config.value;
55
-
56
- if (!Array.isArray(config.value.reaction)) {
57
- return [];
58
- }
50
+ const { reaction, path } = config.value;
59
51
 
60
- return config.value.reaction.map((icon, index) => ({
52
+ return reaction.map((icon, index) => ({
61
53
  icon,
62
54
  vote: votes.value[index] || 0,
63
- desc: locale.value[`reaction${index}` as `reaction0`],
55
+ desc: locale.value[`reaction${index}` as keyof WalineLocale],
64
56
  active: Boolean(
65
- voteStorage.value.find(({ u, i }) => u === path && i === index)
57
+ voteStorage.value.find(
58
+ ({ [VOTE_IDENTIFIER]: voteIdentifier, [VOTE_INDEX]: voteIndex }) =>
59
+ voteIdentifier === path && voteIndex === index
60
+ )
66
61
  ),
67
62
  }));
68
63
  });
69
64
 
70
- const controller = new AbortController();
65
+ let abort: () => void;
71
66
 
72
- const fetchCounter = async (): Promise<void> => {
67
+ const fetchCounter = (): void => {
73
68
  const { serverURL, lang, path, reaction } = config.value;
74
69
 
75
- if (!Array.isArray(reaction)) {
76
- return;
77
- }
70
+ if (reaction.length) {
71
+ const controller = new AbortController();
78
72
 
79
- const resp = await fetchArticleCounter({
80
- serverURL,
81
- lang,
82
- paths: [path],
83
- type: reaction.map((_, k) => `reaction${k}`),
84
- signal: controller.signal,
85
- });
73
+ fetchArticleCounter({
74
+ serverURL,
75
+ lang,
76
+ paths: [path],
77
+ type: reaction.map((_, k) => `reaction${k}`),
78
+ signal: controller.signal,
79
+ }).then((resp) => {
80
+ if (Array.isArray(resp) || typeof resp === 'number') return;
81
+ votes.value = reaction.map((_, k) => resp[`reaction${k}`]);
82
+ });
86
83
 
87
- if (Array.isArray(resp) || typeof resp === 'number') {
88
- return;
84
+ abort = controller.abort.bind(controller);
89
85
  }
90
-
91
- votes.value = reaction.map((_, k) => resp[`reaction${k}`]);
92
86
  };
93
87
 
94
- const onVote = async (index: number): Promise<void> => {
88
+ const vote = async (index: number): Promise<void> => {
95
89
  const { serverURL, lang, path } = config.value;
96
- const hasVoted = voteStorage.value.find(({ u }) => u === path);
97
- const hasVotedTheReaction = hasVoted && hasVoted.i === index;
90
+ const hasVoted = voteStorage.value.find(
91
+ ({ [VOTE_IDENTIFIER]: voteIdentifier }) => voteIdentifier === path
92
+ );
93
+ const hasVotedTheReaction = hasVoted && hasVoted[VOTE_INDEX] === index;
98
94
 
99
- if (hasVotedTheReaction) {
100
- return;
101
- }
95
+ if (hasVotedTheReaction) return;
102
96
 
103
97
  await updateArticleCounter({
104
98
  serverURL,
@@ -109,7 +103,10 @@ export default defineComponent({
109
103
 
110
104
  votes.value[index] = (votes.value[index] || 0) + 1;
111
105
  if (hasVoted) {
112
- votes.value[hasVoted.i] = Math.max(votes.value[hasVoted.i] - 1, 0);
106
+ votes.value[hasVoted[VOTE_INDEX]] = Math.max(
107
+ votes.value[hasVoted[VOTE_INDEX]] - 1,
108
+ 0
109
+ );
113
110
  updateArticleCounter({
114
111
  serverURL,
115
112
  lang,
@@ -121,7 +118,10 @@ export default defineComponent({
121
118
  hasVoted.i = index;
122
119
  voteStorage.value = Array.from(voteStorage.value);
123
120
  } else {
124
- voteStorage.value = [...voteStorage.value, { u: path, i: index }];
121
+ voteStorage.value = [
122
+ ...voteStorage.value,
123
+ { [VOTE_IDENTIFIER]: path, [VOTE_INDEX]: index },
124
+ ];
125
125
  }
126
126
 
127
127
  if (voteStorage.value.length > 50)
@@ -129,12 +129,12 @@ export default defineComponent({
129
129
  };
130
130
 
131
131
  onMounted(() => fetchCounter());
132
- onUnmounted(() => controller.abort());
132
+ onUnmounted(() => abort?.());
133
133
 
134
134
  return {
135
135
  reaction,
136
136
  locale,
137
- onVote,
137
+ vote,
138
138
  };
139
139
  },
140
140
  });