@waline/client 2.14.1 → 2.14.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 (81) hide show
  1. package/dist/api.cjs +1 -1
  2. package/dist/api.cjs.map +1 -1
  3. package/dist/api.d.cts +22 -20
  4. package/dist/api.d.mts +22 -20
  5. package/dist/api.d.ts +22 -20
  6. package/dist/api.mjs +1 -1
  7. package/dist/api.mjs.map +1 -1
  8. package/dist/comment.cjs +1 -1
  9. package/dist/comment.cjs.map +1 -1
  10. package/dist/comment.d.cts +1 -1
  11. package/dist/comment.d.mts +1 -1
  12. package/dist/comment.d.ts +1 -1
  13. package/dist/comment.js +1 -68
  14. package/dist/comment.js.map +1 -1
  15. package/dist/comment.mjs +1 -1
  16. package/dist/comment.mjs.map +1 -1
  17. package/dist/component.mjs +1 -1
  18. package/dist/component.mjs.map +1 -1
  19. package/dist/legacy.umd.d.ts +15 -6
  20. package/dist/legacy.umd.js +1 -1
  21. package/dist/legacy.umd.js.map +1 -1
  22. package/dist/pageview.cjs +1 -1
  23. package/dist/pageview.cjs.map +1 -1
  24. package/dist/pageview.d.cts +1 -1
  25. package/dist/pageview.d.mts +1 -1
  26. package/dist/pageview.d.ts +1 -1
  27. package/dist/pageview.js +1 -121
  28. package/dist/pageview.js.map +1 -1
  29. package/dist/pageview.mjs +1 -1
  30. package/dist/pageview.mjs.map +1 -1
  31. package/dist/shim.cjs +1 -1
  32. package/dist/shim.cjs.map +1 -1
  33. package/dist/shim.d.cts +20 -11
  34. package/dist/shim.d.mts +20 -11
  35. package/dist/shim.mjs +1 -1
  36. package/dist/shim.mjs.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 +20 -11
  42. package/dist/waline.d.mts +20 -11
  43. package/dist/waline.d.ts +20 -11
  44. package/dist/waline.js +1 -6787
  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 +28 -27
  49. package/src/api/comment.ts +25 -24
  50. package/src/comment.ts +3 -4
  51. package/src/components/ArticleReaction.vue +120 -117
  52. package/src/components/CommentBox.vue +451 -488
  53. package/src/components/CommentCard.vue +109 -98
  54. package/src/components/ImageWall.vue +132 -131
  55. package/src/components/WalineComment.vue +683 -0
  56. package/src/composables/index.ts +1 -2
  57. package/src/composables/reaction.ts +16 -0
  58. package/src/composables/recaptchaV3.ts +4 -6
  59. package/src/config/default.ts +6 -1
  60. package/src/config/i18n/index.ts +1 -0
  61. package/src/{entrys → entries}/api.ts +0 -0
  62. package/src/{entrys → entries}/comment.ts +0 -0
  63. package/src/entries/components.ts +2 -0
  64. package/src/{entrys → entries}/full.ts +0 -0
  65. package/src/{entrys → entries}/init.ts +0 -0
  66. package/src/{entrys → entries}/legacy.ts +0 -0
  67. package/src/{entrys → entries}/pageview.ts +0 -0
  68. package/src/init.ts +1 -1
  69. package/src/pageview.ts +2 -2
  70. package/src/styles/reaction.scss +27 -16
  71. package/src/typings/base.ts +5 -0
  72. package/src/typings/waline.ts +15 -6
  73. package/src/utils/config.ts +28 -6
  74. package/src/utils/image.ts +1 -1
  75. package/src/widgets/recentComments.ts +2 -2
  76. package/src/widgets/userList.ts +2 -2
  77. package/LICENSE +0 -339
  78. package/src/components/Waline.vue +0 -509
  79. package/src/composables/timeAgo.ts +0 -15
  80. package/src/composables/vote.ts +0 -20
  81. package/src/entrys/components.ts +0 -2
@@ -1,150 +1,153 @@
1
1
  <template>
2
- <div v-if="reaction.length" class="wl-reaction">
2
+ <div v-if="reactionsInfo.length" class="wl-reaction">
3
3
  <h4 v-text="locale.reactionTitle" />
4
- <ul>
4
+
5
+ <ul class="wl-reaction-list">
5
6
  <li
6
- v-for="(item, index) in reaction"
7
+ v-for="({ active, icon, desc }, index) in reactionsInfo"
7
8
  :key="index"
8
- :class="{ active: item.active }"
9
+ class="wl-reaction-item"
10
+ :class="{ active }"
9
11
  @click="vote(index)"
10
12
  >
11
13
  <div class="wl-reaction-img">
12
- <img :src="item.icon" :alt="item.desc" />
13
- <div class="wl-reaction-votes">{{ item.vote }}</div>
14
+ <img :src="icon" :alt="desc" />
15
+
16
+ <LoadingIcon
17
+ v-if="votingIndex === index"
18
+ class="wl-reaction-loading"
19
+ />
20
+
21
+ <div
22
+ v-else
23
+ class="wl-reaction-votes"
24
+ v-text="voteNumbers[index] || 0"
25
+ />
14
26
  </div>
15
- <div class="wl-reaction-text">{{ item.desc }}</div>
27
+
28
+ <div class="wl-reaction-text" v-text="desc" />
16
29
  </li>
17
30
  </ul>
18
31
  </div>
19
32
  </template>
20
33
 
21
- <script lang="ts">
22
- import {
23
- defineComponent,
24
- inject,
25
- ComputedRef,
26
- computed,
27
- onMounted,
28
- onUnmounted,
29
- ref,
30
- watch,
31
- } from 'vue';
32
- import { getArticleCounter, updateArticleCounter } from '../api';
33
- import { VOTE_IDENTIFIER, VOTE_INDEX, useVoteStorage } from '../composables';
34
- import type { WalineConfig } from '../utils';
35
- import type { WalineLocale } from '../typings';
34
+ <script setup lang="ts">
35
+ import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue';
36
+
37
+ import { LoadingIcon } from './Icons.js';
38
+ import { getArticleCounter, updateArticleCounter } from '../api/index.js';
39
+ import { useReactionStorage } from '../composables/index.js';
40
+
41
+ import type { ComputedRef } from 'vue';
42
+ import type { WalineConfig } from '../utils/index.js';
43
+ import type { WalineReactionLocale } from '../typings';
44
+
45
+ defineExpose();
36
46
 
37
47
  interface ReactionItem {
38
48
  icon: string;
39
- vote: number;
40
49
  desc: string;
41
50
  active?: boolean;
42
51
  }
43
52
 
44
- export default defineComponent({
45
- setup() {
46
- const votes = ref<ReactionItem['vote'][]>([]);
47
- const voteStorage = useVoteStorage();
48
- const config = inject<ComputedRef<WalineConfig>>('config')!;
49
- const locale = computed(() => config.value.locale);
50
- const reaction = computed((): ReactionItem[] => {
51
- const { reaction, path } = config.value;
52
-
53
- return reaction.map((icon, index) => ({
54
- icon,
55
- vote: votes.value[index] || 0,
56
- desc: locale.value[`reaction${index}` as keyof WalineLocale],
57
- active: Boolean(
58
- voteStorage.value.find(
59
- ({ [VOTE_IDENTIFIER]: voteIdentifier, [VOTE_INDEX]: voteIndex }) =>
60
- voteIdentifier === path && voteIndex === index
61
- )
62
- ),
63
- }));
53
+ const reactionStorage = useReactionStorage();
54
+ const config = inject<ComputedRef<WalineConfig>>('config')!;
55
+
56
+ const votingIndex = ref(-1);
57
+ const voteNumbers = ref<number[]>([]);
58
+
59
+ const locale = computed(() => config.value.locale);
60
+ const isReactionEnabled = computed(() => config.value.reaction.length > 0);
61
+
62
+ const reactionsInfo = computed<ReactionItem[]>(() => {
63
+ const { reaction, path } = config.value;
64
+
65
+ return reaction.map((icon, index) => ({
66
+ icon,
67
+ desc: locale.value[`reaction${index}` as keyof WalineReactionLocale],
68
+ active: reactionStorage.value[path] === index,
69
+ }));
70
+ });
71
+
72
+ let abort: () => void;
73
+
74
+ const fetchReaction = async (): Promise<void> => {
75
+ if (isReactionEnabled.value) {
76
+ const { serverURL, lang, path, reaction } = config.value;
77
+ const controller = new AbortController();
78
+
79
+ abort = controller.abort.bind(controller);
80
+
81
+ const resp = await getArticleCounter({
82
+ serverURL,
83
+ lang,
84
+ paths: [path],
85
+ type: reaction.map((_reaction, index) => `reaction${index}`),
86
+ signal: controller.signal,
64
87
  });
65
88
 
66
- let abort: () => void;
67
-
68
- const fetchCounter = (): void => {
69
- const { serverURL, lang, path, reaction } = config.value;
70
-
71
- if (reaction.length) {
72
- const controller = new AbortController();
73
-
74
- getArticleCounter({
75
- serverURL,
76
- lang,
77
- paths: [path],
78
- type: reaction.map((_, k) => `reaction${k}`),
79
- signal: controller.signal,
80
- }).then((resp) => {
81
- if (Array.isArray(resp) || typeof resp === 'number') return;
82
- votes.value = reaction.map((_, k) => resp[`reaction${k}`]);
83
- });
84
-
85
- abort = controller.abort.bind(controller);
86
- }
87
- };
88
-
89
- const vote = async (index: number): Promise<void> => {
90
- const { serverURL, lang, path } = config.value;
91
- const hasVoted = voteStorage.value.find(
92
- ({ [VOTE_IDENTIFIER]: voteIdentifier }) => voteIdentifier === path
93
- );
94
- const hasVotedTheReaction = hasVoted && hasVoted[VOTE_INDEX] === index;
89
+ // TODO: Remove this compact code
90
+ if (Array.isArray(resp) || typeof resp === 'number') return;
91
+
92
+ voteNumbers.value = reaction.map(
93
+ (_reaction, index) => resp[`reaction${index}`]
94
+ );
95
+ }
96
+ };
97
+
98
+ const vote = async (index: number): Promise<void> => {
99
+ // we should ensure that only one vote request is sent at a time
100
+ if (votingIndex.value === -1) {
101
+ const { serverURL, lang, path } = config.value;
102
+ const currentVoteItemIndex = reactionStorage.value[path];
95
103
 
96
- if (hasVotedTheReaction) return;
104
+ // mark voting status
105
+ votingIndex.value = index;
97
106
 
107
+ // if user already vote current article, decrease the voted item number
108
+ if (currentVoteItemIndex !== undefined) {
98
109
  await updateArticleCounter({
99
110
  serverURL,
100
111
  lang,
101
112
  path,
102
- type: `reaction${index}`,
113
+ type: `reaction${currentVoteItemIndex}`,
114
+ action: 'desc',
103
115
  });
104
116
 
105
- votes.value[index] = (votes.value[index] || 0) + 1;
106
- if (hasVoted) {
107
- votes.value[hasVoted[VOTE_INDEX]] = Math.max(
108
- votes.value[hasVoted[VOTE_INDEX]] - 1,
109
- 0
110
- );
111
- updateArticleCounter({
112
- serverURL,
113
- lang,
114
- path,
115
- type: `reaction${hasVoted.i}`,
116
- action: 'desc',
117
- });
118
-
119
- hasVoted.i = index;
120
- voteStorage.value = Array.from(voteStorage.value);
121
- } else {
122
- voteStorage.value = [
123
- ...voteStorage.value,
124
- { [VOTE_IDENTIFIER]: path, [VOTE_INDEX]: index },
125
- ];
126
- }
127
-
128
- if (voteStorage.value.length > 50)
129
- voteStorage.value = voteStorage.value.slice(-50);
130
- };
131
-
132
- onMounted(() => {
133
- watch(
134
- () => [config.value.serverURL, config.value.path],
135
- () => {
136
- fetchCounter();
137
- },
138
- { immediate: true }
117
+ voteNumbers.value[currentVoteItemIndex] = Math.max(
118
+ voteNumbers.value[currentVoteItemIndex] - 1,
119
+ 0
139
120
  );
140
- });
141
- onUnmounted(() => abort?.());
142
-
143
- return {
144
- reaction,
145
- locale,
146
- vote,
147
- };
148
- },
121
+ }
122
+
123
+ // increase voting number if current reaction item is not been voted
124
+ if (currentVoteItemIndex !== index) {
125
+ await updateArticleCounter({
126
+ serverURL,
127
+ lang,
128
+ path,
129
+ type: `reaction${index}`,
130
+ });
131
+ voteNumbers.value[index] = (voteNumbers.value[index] || 0) + 1;
132
+ }
133
+
134
+ // update vote info in local storage
135
+ if (currentVoteItemIndex === index) delete reactionStorage.value[path];
136
+ else reactionStorage.value[path] = index;
137
+
138
+ // voting is completed
139
+ votingIndex.value = -1;
140
+ }
141
+ };
142
+
143
+ onMounted(() => {
144
+ watch(
145
+ () => [config.value.serverURL, config.value.path],
146
+ () => {
147
+ void fetchReaction();
148
+ },
149
+ { immediate: true }
150
+ );
149
151
  });
152
+ onUnmounted(() => abort?.());
150
153
  </script>