@waline/client 2.3.1 → 2.4.1
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/dist/component.esm.js +1 -1
- package/dist/component.esm.js.map +1 -1
- package/dist/component.js +1 -1
- package/dist/component.js.map +1 -1
- package/dist/legacy.d.ts +3 -8
- package/dist/legacy.js +1 -1
- package/dist/legacy.js.map +1 -1
- package/dist/pageview.cjs.d.ts +47 -0
- package/dist/pageview.cjs.js +1 -1
- package/dist/pageview.cjs.js.map +1 -1
- package/dist/pageview.esm.d.ts +47 -0
- package/dist/pageview.esm.js +1 -1
- package/dist/pageview.esm.js.map +1 -1
- package/dist/pageview.js +1 -1
- package/dist/pageview.js.map +1 -1
- package/dist/shim.d.ts +45 -10
- package/dist/shim.esm.d.ts +45 -10
- package/dist/shim.esm.js +1 -1
- package/dist/shim.esm.js.map +1 -1
- package/dist/shim.js +1 -1
- package/dist/shim.js.map +1 -1
- package/dist/waline.cjs.d.ts +45 -10
- package/dist/waline.cjs.js +1 -1
- package/dist/waline.cjs.js.map +1 -1
- package/dist/waline.css +1 -1
- package/dist/waline.css.map +1 -1
- package/dist/waline.d.ts +45 -10
- package/dist/waline.esm.d.ts +45 -10
- package/dist/waline.esm.js +1 -1
- package/dist/waline.esm.js.map +1 -1
- package/dist/waline.js +1 -1
- package/dist/waline.js.map +1 -1
- package/package.json +20 -5
- package/src/components/CommentBox.vue +1 -3
- package/src/components/CommentCard.vue +80 -14
- package/src/components/Icons.ts +28 -2
- package/src/components/Waline.vue +220 -98
- package/src/composables/index.ts +1 -0
- package/src/composables/like.ts +14 -0
- package/src/composables/userInfo.ts +9 -1
- package/src/config/i18n/en.ts +2 -0
- package/src/config/i18n/generate.ts +2 -0
- package/src/config/i18n/jp.ts +2 -0
- package/src/config/i18n/pt-BR.ts +2 -0
- package/src/config/i18n/ru.ts +2 -0
- package/src/config/i18n/vi-VN.ts +2 -0
- package/src/config/i18n/zh-CN.ts +2 -0
- package/src/config/i18n/zh-TW.ts +2 -0
- package/src/styles/card.scss +55 -27
- package/src/styles/nomalize.scss +0 -1
- package/src/styles/panel.scss +0 -1
- package/src/typings/comment.ts +56 -1
- package/src/typings/locale.ts +3 -8
- package/src/utils/config.ts +1 -1
- package/src/utils/fetch.ts +77 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "client for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"valine",
|
|
@@ -71,19 +71,34 @@
|
|
|
71
71
|
]
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@vueuse/core": "^8.4.
|
|
74
|
+
"@vueuse/core": "^8.4.2",
|
|
75
75
|
"autosize": "^5.0.1",
|
|
76
76
|
"marked": "^4.0.15",
|
|
77
77
|
"vue": "^3.2.33"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
|
+
"@babel/core": "^7.17.10",
|
|
81
|
+
"@babel/preset-env": "^7.17.10",
|
|
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",
|
|
80
86
|
"@types/autosize": "^4.0.1",
|
|
81
87
|
"@types/marked": "^4.0.3",
|
|
82
|
-
"@
|
|
83
|
-
"
|
|
88
|
+
"@types/node": "^17.0.31",
|
|
89
|
+
"@vitejs/plugin-vue": "^2.3.2",
|
|
90
|
+
"@vue/compiler-sfc": "^3.2.33",
|
|
91
|
+
"rimraf": "^3.0.2",
|
|
92
|
+
"rollup": "^2.72.1",
|
|
93
|
+
"rollup-plugin-dts": "^4.2.1",
|
|
94
|
+
"rollup-plugin-terser": "^7.0.2",
|
|
95
|
+
"rollup-plugin-ts": "^2.0.7",
|
|
96
|
+
"rollup-plugin-vue": "^6.0.0",
|
|
97
|
+
"typescript": "^4.6.4",
|
|
98
|
+
"vite": "^2.9.8"
|
|
84
99
|
},
|
|
85
100
|
"engines": {
|
|
86
|
-
"node": ">=
|
|
101
|
+
"node": ">=14"
|
|
87
102
|
},
|
|
88
103
|
"scripts": {
|
|
89
104
|
"build": "pnpm rollup && pnpm style",
|
|
@@ -405,9 +405,7 @@ export default defineComponent({
|
|
|
405
405
|
if (
|
|
406
406
|
(requiredMeta.indexOf('mail') > -1 && !comment.mail) ||
|
|
407
407
|
(comment.mail &&
|
|
408
|
-
!/^\w(?:[\w._-]*\w)?@(?:\w(?:[\w-]*\w)?\.)
|
|
409
|
-
comment.mail
|
|
410
|
-
))
|
|
408
|
+
!/^\w(?:[\w._-]*\w)?@(?:\w(?:[\w-]*\w)?\.)*\w+$/.exec(comment.mail))
|
|
411
409
|
) {
|
|
412
410
|
inputRefs.value.mail?.focus();
|
|
413
411
|
return alert(locale.value.mailError);
|
|
@@ -25,21 +25,39 @@
|
|
|
25
25
|
<span v-if="comment.label" class="wl-badge" v-text="comment.label" />
|
|
26
26
|
<span v-if="comment.sticky" class="wl-badge" v-text="locale.sticky" />
|
|
27
27
|
<span
|
|
28
|
-
v-if="comment.level >= 0"
|
|
28
|
+
v-if="comment.level && comment.level >= 0"
|
|
29
29
|
:class="`wl-badge level${comment.level}`"
|
|
30
30
|
v-text="locale[`level${comment.level}`] || `Level ${comment.level}`"
|
|
31
31
|
/>
|
|
32
|
-
|
|
33
32
|
<span class="wl-time" v-text="time" />
|
|
34
33
|
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
<div class="wl-comment-actions">
|
|
35
|
+
<button
|
|
36
|
+
class="wl-delete"
|
|
37
|
+
v-if="isAdmin || isOwner"
|
|
38
|
+
@click="$emit('delete', comment)"
|
|
39
|
+
>
|
|
40
|
+
<DeleteIcon />
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
<button
|
|
44
|
+
class="wl-like"
|
|
45
|
+
@click="$emit('like', comment)"
|
|
46
|
+
:title="like ? locale.cancelLike : locale.like"
|
|
47
|
+
>
|
|
48
|
+
<LikeIcon :active="like" />
|
|
49
|
+
<span v-if="'like' in comment" v-text="comment.like" />
|
|
50
|
+
</button>
|
|
51
|
+
|
|
52
|
+
<button
|
|
53
|
+
class="wl-reply"
|
|
54
|
+
:class="{ active: isReplyingCurrent }"
|
|
55
|
+
:title="isReplyingCurrent ? locale.cancelReply : locale.reply"
|
|
56
|
+
@click="$emit('reply', isReplyingCurrent ? null : comment)"
|
|
57
|
+
>
|
|
58
|
+
<ReplyIcon />
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
43
61
|
</div>
|
|
44
62
|
<div class="wl-meta" aria-hidden="true">
|
|
45
63
|
<span v-if="comment.addr" v-text="comment.addr" />
|
|
@@ -48,6 +66,27 @@
|
|
|
48
66
|
</div>
|
|
49
67
|
<div class="wl-content" v-html="comment.comment" />
|
|
50
68
|
|
|
69
|
+
<div v-if="isAdmin" class="wl-admin-actions">
|
|
70
|
+
<span class="wl-comment-status">
|
|
71
|
+
<button
|
|
72
|
+
v-for="status in commentStatus"
|
|
73
|
+
:key="status"
|
|
74
|
+
:class="`wl-btn wl-${status}`"
|
|
75
|
+
:disabled="comment.status === status"
|
|
76
|
+
@click="$emit('status', { status, comment })"
|
|
77
|
+
v-text="status"
|
|
78
|
+
/>
|
|
79
|
+
</span>
|
|
80
|
+
|
|
81
|
+
<button
|
|
82
|
+
class="wl-btn wl-sticky"
|
|
83
|
+
v-if="isAdmin && !comment.rid"
|
|
84
|
+
@click="$emit('sticky', comment)"
|
|
85
|
+
>
|
|
86
|
+
{{ comment.sticky ? 'unsticky' : 'sticky' }}
|
|
87
|
+
</button>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
51
90
|
<div v-if="isReplyingCurrent" class="wl-reply-wrapper">
|
|
52
91
|
<CommentBox
|
|
53
92
|
:replyId="comment.objectId"
|
|
@@ -66,6 +105,10 @@
|
|
|
66
105
|
:rootId="rootId"
|
|
67
106
|
@reply="$emit('reply', $event)"
|
|
68
107
|
@submit="$emit('submit', $event)"
|
|
108
|
+
@like="$emit('like', $event)"
|
|
109
|
+
@delete="$emit('delete', $event)"
|
|
110
|
+
@status="$emit('status', $event)"
|
|
111
|
+
@sticky="$emit('sticky', $event)"
|
|
69
112
|
/>
|
|
70
113
|
</div>
|
|
71
114
|
</div>
|
|
@@ -75,13 +118,15 @@
|
|
|
75
118
|
<script lang="ts">
|
|
76
119
|
import { computed, defineComponent, inject } from 'vue';
|
|
77
120
|
import CommentBox from './CommentBox.vue';
|
|
78
|
-
import { ReplyIcon, VerifiedIcon } from './Icons';
|
|
121
|
+
import { DeleteIcon, LikeIcon, ReplyIcon, VerifiedIcon } from './Icons';
|
|
79
122
|
import { isLinkHttp } from '../utils';
|
|
80
|
-
import { useTimeAgo } from '../composables';
|
|
123
|
+
import { useTimeAgo, useLikeStorage, useUserInfo } from '../composables';
|
|
81
124
|
|
|
82
125
|
import type { ComputedRef, PropType } from 'vue';
|
|
83
126
|
import type { WalineConfig } from '../utils';
|
|
84
|
-
import type { WalineComment } from '../typings';
|
|
127
|
+
import type { WalineComment, WalineCommentStatus } from '../typings';
|
|
128
|
+
|
|
129
|
+
const commentStatus: WalineCommentStatus[] = ['approved', 'waiting', 'spam'];
|
|
85
130
|
|
|
86
131
|
export default defineComponent({
|
|
87
132
|
props: {
|
|
@@ -100,16 +145,21 @@ export default defineComponent({
|
|
|
100
145
|
|
|
101
146
|
components: {
|
|
102
147
|
CommentBox,
|
|
148
|
+
DeleteIcon,
|
|
149
|
+
LikeIcon,
|
|
103
150
|
ReplyIcon,
|
|
104
151
|
VerifiedIcon,
|
|
105
152
|
},
|
|
106
153
|
|
|
107
|
-
emits: ['submit', 'reply'],
|
|
154
|
+
emits: ['submit', 'reply', 'like', 'delete', 'status', 'sticky'],
|
|
108
155
|
|
|
109
156
|
setup(props) {
|
|
110
157
|
const config = inject<ComputedRef<WalineConfig>>(
|
|
111
158
|
'config'
|
|
112
159
|
) as ComputedRef<WalineConfig>;
|
|
160
|
+
const likes = useLikeStorage();
|
|
161
|
+
const userInfo = useUserInfo();
|
|
162
|
+
|
|
113
163
|
const locale = computed(() => config.value.locale);
|
|
114
164
|
|
|
115
165
|
const link = computed(() => {
|
|
@@ -118,8 +168,18 @@ export default defineComponent({
|
|
|
118
168
|
return link ? (isLinkHttp(link) ? link : `https://${link}`) : '';
|
|
119
169
|
});
|
|
120
170
|
|
|
171
|
+
const like = computed(() => likes.value.includes(props.comment.objectId));
|
|
172
|
+
|
|
121
173
|
const time = useTimeAgo(props.comment.insertedAt, locale.value);
|
|
122
174
|
|
|
175
|
+
const isAdmin = computed(() => userInfo.value.type === 'administrator');
|
|
176
|
+
|
|
177
|
+
const isOwner = computed(
|
|
178
|
+
() =>
|
|
179
|
+
props.comment.user_id &&
|
|
180
|
+
userInfo.value.objectId === props.comment.user_id
|
|
181
|
+
);
|
|
182
|
+
|
|
123
183
|
const isReplyingCurrent = computed(
|
|
124
184
|
() => props.comment.objectId === props.reply?.objectId
|
|
125
185
|
);
|
|
@@ -130,7 +190,13 @@ export default defineComponent({
|
|
|
130
190
|
|
|
131
191
|
isReplyingCurrent,
|
|
132
192
|
link,
|
|
193
|
+
like,
|
|
133
194
|
time,
|
|
195
|
+
|
|
196
|
+
isAdmin,
|
|
197
|
+
isOwner,
|
|
198
|
+
|
|
199
|
+
commentStatus,
|
|
134
200
|
};
|
|
135
201
|
},
|
|
136
202
|
});
|
package/src/components/Icons.ts
CHANGED
|
@@ -22,6 +22,16 @@ export const CloseIcon: FunctionalComponent<{ size: number }> = ({ size }) =>
|
|
|
22
22
|
]
|
|
23
23
|
);
|
|
24
24
|
|
|
25
|
+
export const DeleteIcon: FunctionalComponent = () =>
|
|
26
|
+
h(
|
|
27
|
+
'svg',
|
|
28
|
+
{ viewBox: '0 0 1024 1024', width: '24', height: '24' },
|
|
29
|
+
h('path', {
|
|
30
|
+
d: 'm341.013 394.667 27.755 393.45h271.83l27.733-393.45h64.106l-28.01 397.952a64 64 0 0 1-63.83 59.498H368.768a64 64 0 0 1-63.83-59.52l-28.053-397.93h64.128zm139.307 19.818v298.667h-64V414.485h64zm117.013 0v298.667h-64V414.485h64zM181.333 288h640v64h-640v-64zm453.483-106.667v64h-256v-64h256z',
|
|
31
|
+
fill: 'red',
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
|
|
25
35
|
export const EmojiIcon: FunctionalComponent = () =>
|
|
26
36
|
h(
|
|
27
37
|
'svg',
|
|
@@ -44,6 +54,22 @@ export const ImageIcon: FunctionalComponent = () =>
|
|
|
44
54
|
}),
|
|
45
55
|
]);
|
|
46
56
|
|
|
57
|
+
export const LikeIcon: FunctionalComponent<{ active: boolean }> = ({
|
|
58
|
+
active = false,
|
|
59
|
+
}: {
|
|
60
|
+
active?: boolean;
|
|
61
|
+
}) =>
|
|
62
|
+
h('svg', { viewBox: '0 0 1024 1024', width: '24', height: '24' }, [
|
|
63
|
+
h('path', {
|
|
64
|
+
d: `M850.654 323.804c-11.042-25.625-26.862-48.532-46.885-68.225-20.022-19.61-43.258-34.936-69.213-45.73-26.78-11.124-55.124-16.727-84.375-16.727-40.622 0-80.256 11.123-114.698 32.135A214.79 214.79 0 0 0 512 241.819a214.79 214.79 0 0 0-23.483-16.562c-34.442-21.012-74.076-32.135-114.698-32.135-29.25 0-57.595 5.603-84.375 16.727-25.872 10.711-49.19 26.12-69.213 45.73-20.105 19.693-35.843 42.6-46.885 68.225-11.453 26.615-17.303 54.877-17.303 83.963 0 27.439 5.603 56.03 16.727 85.117 9.31 24.307 22.659 49.52 39.715 74.981 27.027 40.293 64.188 82.316 110.33 124.915 76.465 70.615 152.189 119.394 155.402 121.371l19.528 12.525c8.652 5.52 19.776 5.52 28.427 0l19.529-12.525c3.213-2.06 78.854-50.756 155.401-121.371 46.143-42.6 83.304-84.622 110.33-124.915 17.057-25.46 30.487-50.674 39.716-74.981 11.124-29.087 16.727-57.678 16.727-85.117.082-29.086-5.768-57.348-17.221-83.963z${
|
|
65
|
+
active
|
|
66
|
+
? ''
|
|
67
|
+
: 'M512 761.5S218.665 573.55 218.665 407.767c0-83.963 69.461-152.023 155.154-152.023 60.233 0 112.473 33.618 138.181 82.727 25.708-49.109 77.948-82.727 138.18-82.727 85.694 0 155.155 68.06 155.155 152.023C805.335 573.551 512 761.5 512 761.5z'
|
|
68
|
+
}`,
|
|
69
|
+
fill: active ? 'red' : 'currentColor',
|
|
70
|
+
}),
|
|
71
|
+
]);
|
|
72
|
+
|
|
47
73
|
export const PreviewIcon: FunctionalComponent = () =>
|
|
48
74
|
h('svg', { viewBox: '0 0 1024 1024', width: '24', height: '24' }, [
|
|
49
75
|
h('path', {
|
|
@@ -69,9 +95,9 @@ export const MarkdownIcon: FunctionalComponent = () =>
|
|
|
69
95
|
export const ReplyIcon: FunctionalComponent = () =>
|
|
70
96
|
h(
|
|
71
97
|
'svg',
|
|
72
|
-
{ viewBox: '0 0 1024 1024', width: '
|
|
98
|
+
{ viewBox: '0 0 1024 1024', width: '24', height: '24' },
|
|
73
99
|
h('path', {
|
|
74
|
-
d: '
|
|
100
|
+
d: 'M810.667 213.333a64 64 0 0 1 64 64V704a64 64 0 0 1-64 64H478.336l-146.645 96.107a21.333 21.333 0 0 1-33.024-17.856V768h-85.334a64 64 0 0 1-64-64V277.333a64 64 0 0 1 64-64h597.334zm0 64H213.333V704h149.334v63.296L459.243 704h351.424V277.333zm-271.36 213.334v64h-176.64v-64h176.64zm122.026-128v64H362.667v-64h298.666z',
|
|
75
101
|
fill: 'currentColor',
|
|
76
102
|
})
|
|
77
103
|
);
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
:reply="reply"
|
|
16
16
|
@reply="onReply"
|
|
17
17
|
@submit="onSubmit"
|
|
18
|
+
@status="onStatusChange"
|
|
19
|
+
@delete="onDelete"
|
|
20
|
+
@sticky="onSticky"
|
|
21
|
+
@like="onLike"
|
|
18
22
|
/>
|
|
19
23
|
</div>
|
|
20
24
|
|
|
@@ -66,134 +70,152 @@ import { computed, defineComponent, onMounted, provide, ref, watch } from 'vue';
|
|
|
66
70
|
import CommentBox from './CommentBox.vue';
|
|
67
71
|
import CommentCard from './CommentCard.vue';
|
|
68
72
|
import { LoadingIcon } from './Icons';
|
|
69
|
-
import { useUserInfo } from '../composables';
|
|
73
|
+
import { useUserInfo, useLikeStorage } from '../composables';
|
|
70
74
|
import { defaultLocales } from '../config';
|
|
71
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
deleteComment,
|
|
77
|
+
fetchCommentList,
|
|
78
|
+
likeComment,
|
|
79
|
+
getConfig,
|
|
80
|
+
getDarkStyle,
|
|
81
|
+
updateComment,
|
|
82
|
+
} from '../utils';
|
|
72
83
|
|
|
73
84
|
import type { PropType } from 'vue';
|
|
74
85
|
import type {
|
|
75
86
|
WalineComment,
|
|
87
|
+
WalineCommentStatus,
|
|
76
88
|
WalineEmojiInfo,
|
|
77
89
|
WalineHighlighter,
|
|
78
90
|
WalineTexRenderer,
|
|
79
91
|
WalineImageUploader,
|
|
80
92
|
WalineLocale,
|
|
81
93
|
WalineProps,
|
|
94
|
+
WalineMeta,
|
|
82
95
|
} from '../typings';
|
|
83
96
|
|
|
84
97
|
declare const SHOULD_VALIDATE: boolean;
|
|
85
98
|
declare const VERSION: string;
|
|
86
99
|
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
const props = [
|
|
101
|
+
'serverURL',
|
|
102
|
+
'path',
|
|
103
|
+
'meta',
|
|
104
|
+
'requiredMeta',
|
|
105
|
+
'dark',
|
|
106
|
+
'lang',
|
|
107
|
+
'locale',
|
|
108
|
+
'pageSize',
|
|
109
|
+
'wordLimit',
|
|
110
|
+
'emoji',
|
|
111
|
+
'login',
|
|
112
|
+
'highlighter',
|
|
113
|
+
'texRenderer',
|
|
114
|
+
'imageUploader',
|
|
115
|
+
'copyright',
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const propsWithValidate = {
|
|
119
|
+
serverURL: {
|
|
120
|
+
type: String,
|
|
121
|
+
required: true,
|
|
122
|
+
},
|
|
89
123
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
LoadingIcon,
|
|
124
|
+
path: {
|
|
125
|
+
type: String,
|
|
126
|
+
required: true,
|
|
94
127
|
},
|
|
95
128
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
type: String,
|
|
104
|
-
required: true,
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
meta: {
|
|
108
|
-
type: Array,
|
|
109
|
-
...(SHOULD_VALIDATE
|
|
110
|
-
? {
|
|
111
|
-
validator: (value: unknown): boolean =>
|
|
112
|
-
Array.isArray(value) &&
|
|
113
|
-
value.every((item) => ['nick', 'mail', 'link'].includes(item)),
|
|
114
|
-
}
|
|
115
|
-
: {}),
|
|
116
|
-
},
|
|
117
|
-
|
|
118
|
-
requiredMeta: {
|
|
119
|
-
type: Array,
|
|
120
|
-
...(SHOULD_VALIDATE
|
|
121
|
-
? {
|
|
122
|
-
validator: (value: unknown): boolean =>
|
|
123
|
-
Array.isArray(value) &&
|
|
124
|
-
value.every((item) => ['nick', 'mail', 'link'].includes(item)),
|
|
125
|
-
}
|
|
126
|
-
: {}),
|
|
127
|
-
},
|
|
129
|
+
meta: {
|
|
130
|
+
type: Array as PropType<WalineMeta[]>,
|
|
131
|
+
default: (): WalineMeta[] => ['nick', 'mail', 'link'],
|
|
132
|
+
validator: (value: unknown): boolean =>
|
|
133
|
+
Array.isArray(value) &&
|
|
134
|
+
value.every((item) => ['nick', 'mail', 'link'].includes(item)),
|
|
135
|
+
},
|
|
128
136
|
|
|
129
|
-
|
|
137
|
+
requiredMeta: {
|
|
138
|
+
type: Array,
|
|
139
|
+
default: (): WalineMeta[] => [],
|
|
140
|
+
validator: (value: unknown): boolean =>
|
|
141
|
+
Array.isArray(value) &&
|
|
142
|
+
value.every((item) => ['nick', 'mail', 'link'].includes(item)),
|
|
143
|
+
},
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
locale: Object as PropType<Partial<WalineLocale>>,
|
|
142
|
-
|
|
143
|
-
pageSize: Number,
|
|
144
|
-
|
|
145
|
-
wordLimit: {
|
|
146
|
-
type: [Number, Array] as PropType<number | [number, number]>,
|
|
147
|
-
// default: 0,
|
|
148
|
-
...(SHOULD_VALIDATE
|
|
149
|
-
? {
|
|
150
|
-
validator: (value: unknown): boolean =>
|
|
151
|
-
typeof value === 'number' ||
|
|
152
|
-
(Array.isArray(value) &&
|
|
153
|
-
value.length === 2 &&
|
|
154
|
-
value.every((item) => typeof item === 'number')),
|
|
155
|
-
}
|
|
156
|
-
: {}),
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
emoji: {
|
|
160
|
-
type: [Array, Boolean] as PropType<(string | WalineEmojiInfo)[] | false>,
|
|
161
|
-
...(SHOULD_VALIDATE
|
|
162
|
-
? {
|
|
163
|
-
validator: (value: unknown): boolean =>
|
|
164
|
-
value === false ||
|
|
165
|
-
(Array.isArray(value) &&
|
|
166
|
-
value.every(
|
|
167
|
-
(item) =>
|
|
168
|
-
typeof item === 'string' ||
|
|
169
|
-
(typeof item === 'object' &&
|
|
170
|
-
typeof item.name === 'string' &&
|
|
171
|
-
typeof item.folder === 'string' &&
|
|
172
|
-
typeof item.icon === 'string' &&
|
|
173
|
-
Array.isArray(item.items) &&
|
|
174
|
-
(item.items as unknown[]).every(
|
|
175
|
-
(icon) => typeof icon === 'string'
|
|
176
|
-
))
|
|
177
|
-
)),
|
|
178
|
-
}
|
|
179
|
-
: {}),
|
|
180
|
-
},
|
|
145
|
+
dark: [String, Boolean],
|
|
146
|
+
|
|
147
|
+
lang: {
|
|
148
|
+
type: String,
|
|
149
|
+
default: 'zh-CN',
|
|
150
|
+
validator: (value: unknown): boolean =>
|
|
151
|
+
Object.keys(defaultLocales).includes(value as string),
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
locale: Object as PropType<Partial<WalineLocale>>,
|
|
181
155
|
|
|
182
|
-
|
|
156
|
+
pageSize: { type: Number, default: 10 },
|
|
183
157
|
|
|
184
|
-
|
|
158
|
+
wordLimit: {
|
|
159
|
+
type: [Number, Array] as PropType<number | [number, number]>,
|
|
160
|
+
validator: (value: unknown): boolean =>
|
|
161
|
+
typeof value === 'number' ||
|
|
162
|
+
(Array.isArray(value) &&
|
|
163
|
+
value.length === 2 &&
|
|
164
|
+
value.every((item) => typeof item === 'number')),
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
emoji: {
|
|
168
|
+
type: [Array, Boolean] as PropType<(string | WalineEmojiInfo)[] | false>,
|
|
169
|
+
validator: (value: unknown): boolean =>
|
|
170
|
+
value === false ||
|
|
171
|
+
(Array.isArray(value) &&
|
|
172
|
+
value.every(
|
|
173
|
+
(item) =>
|
|
174
|
+
typeof item === 'string' ||
|
|
175
|
+
(typeof item === 'object' &&
|
|
176
|
+
typeof item.name === 'string' &&
|
|
177
|
+
typeof item.folder === 'string' &&
|
|
178
|
+
typeof item.icon === 'string' &&
|
|
179
|
+
Array.isArray(item.items) &&
|
|
180
|
+
(item.items as unknown[]).every(
|
|
181
|
+
(icon) => typeof icon === 'string'
|
|
182
|
+
))
|
|
183
|
+
)),
|
|
184
|
+
},
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
login: String as PropType<'enable' | 'disable' | 'force'>,
|
|
187
187
|
|
|
188
|
-
|
|
188
|
+
highlighter: Function as PropType<WalineHighlighter>,
|
|
189
|
+
|
|
190
|
+
imageUploader: {
|
|
191
|
+
type: [Function, Boolean] as PropType<WalineImageUploader | false>,
|
|
192
|
+
default: undefined,
|
|
193
|
+
},
|
|
189
194
|
|
|
190
|
-
|
|
195
|
+
texRenderer: {
|
|
196
|
+
type: [Function, Boolean] as PropType<WalineTexRenderer | false>,
|
|
197
|
+
default: undefined,
|
|
191
198
|
},
|
|
192
199
|
|
|
200
|
+
copyright: { type: Boolean, default: true },
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default defineComponent({
|
|
204
|
+
name: 'WalineRoot',
|
|
205
|
+
|
|
206
|
+
components: {
|
|
207
|
+
CommentBox,
|
|
208
|
+
CommentCard,
|
|
209
|
+
LoadingIcon,
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
props: SHOULD_VALIDATE ? propsWithValidate : props,
|
|
213
|
+
|
|
193
214
|
setup(props) {
|
|
194
|
-
const config = computed(() => getConfig(props as WalineProps));
|
|
215
|
+
const config = computed(() => getConfig(props as unknown as WalineProps));
|
|
195
216
|
|
|
196
217
|
const userInfo = useUserInfo();
|
|
218
|
+
const likeStorage = useLikeStorage();
|
|
197
219
|
|
|
198
220
|
const status = ref<'loading' | 'success' | 'error'>('loading');
|
|
199
221
|
|
|
@@ -272,9 +294,105 @@ export default defineComponent({
|
|
|
272
294
|
} else data.value.unshift(comment);
|
|
273
295
|
};
|
|
274
296
|
|
|
297
|
+
const onStatusChange = async ({
|
|
298
|
+
comment,
|
|
299
|
+
status,
|
|
300
|
+
}: {
|
|
301
|
+
comment: WalineComment;
|
|
302
|
+
status: WalineCommentStatus;
|
|
303
|
+
}): Promise<void> => {
|
|
304
|
+
if (comment.status === status) return;
|
|
305
|
+
|
|
306
|
+
const { serverURL, lang } = config.value;
|
|
307
|
+
|
|
308
|
+
await updateComment({
|
|
309
|
+
serverURL,
|
|
310
|
+
lang,
|
|
311
|
+
token: userInfo.value?.token,
|
|
312
|
+
objectId: comment.objectId,
|
|
313
|
+
status,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
comment.status = status;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const onSticky = async (comment: WalineComment): Promise<void> => {
|
|
320
|
+
if (comment.rid) return;
|
|
321
|
+
|
|
322
|
+
const { serverURL, lang } = config.value;
|
|
323
|
+
|
|
324
|
+
await updateComment({
|
|
325
|
+
serverURL,
|
|
326
|
+
lang,
|
|
327
|
+
token: userInfo.value?.token,
|
|
328
|
+
objectId: comment.objectId,
|
|
329
|
+
sticky: comment.sticky ? 0 : 1,
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
comment.sticky = !comment.sticky;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const onDelete = async ({ objectId }: WalineComment): Promise<void> => {
|
|
336
|
+
if (!confirm('Are you sure you want to delete this comment?')) return;
|
|
337
|
+
|
|
338
|
+
const { serverURL, lang } = config.value;
|
|
339
|
+
|
|
340
|
+
await deleteComment({
|
|
341
|
+
serverURL,
|
|
342
|
+
lang,
|
|
343
|
+
token: userInfo.value?.token,
|
|
344
|
+
objectId: objectId,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// delete comment from data
|
|
348
|
+
data.value.some((item, index) => {
|
|
349
|
+
if (item.objectId === objectId) {
|
|
350
|
+
data.value = data.value.filter((_item, i) => i !== index);
|
|
351
|
+
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return item.children.some((child, childIndex) => {
|
|
356
|
+
if (child.objectId === objectId) {
|
|
357
|
+
data.value[index].children = item.children.filter(
|
|
358
|
+
(_item, i) => i !== childIndex
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return false;
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const onLike = async (comment: WalineComment): Promise<void> => {
|
|
370
|
+
const { serverURL, lang } = config.value;
|
|
371
|
+
const { objectId } = comment;
|
|
372
|
+
const hasLiked = likeStorage.value.includes(objectId);
|
|
373
|
+
|
|
374
|
+
await likeComment({
|
|
375
|
+
serverURL,
|
|
376
|
+
lang,
|
|
377
|
+
objectId,
|
|
378
|
+
like: !hasLiked,
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
if (hasLiked)
|
|
382
|
+
likeStorage.value = likeStorage.value.filter((id) => id !== objectId);
|
|
383
|
+
else {
|
|
384
|
+
likeStorage.value = [...likeStorage.value, objectId];
|
|
385
|
+
|
|
386
|
+
if (likeStorage.value.length > 50)
|
|
387
|
+
likeStorage.value = likeStorage.value.slice(-50);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
comment.like = (comment.like || 0) + (hasLiked ? -1 : 1);
|
|
391
|
+
};
|
|
392
|
+
|
|
275
393
|
provide('config', config);
|
|
276
394
|
|
|
277
|
-
watch(() => props.path, refresh);
|
|
395
|
+
watch(() => (props as unknown as WalineProps).path, refresh);
|
|
278
396
|
|
|
279
397
|
onMounted(() => refresh());
|
|
280
398
|
|
|
@@ -294,6 +412,10 @@ export default defineComponent({
|
|
|
294
412
|
refresh,
|
|
295
413
|
onReply,
|
|
296
414
|
onSubmit,
|
|
415
|
+
onStatusChange,
|
|
416
|
+
onDelete,
|
|
417
|
+
onSticky,
|
|
418
|
+
onLike,
|
|
297
419
|
|
|
298
420
|
version: VERSION,
|
|
299
421
|
};
|
package/src/composables/index.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useStorage } from '@vueuse/core';
|
|
2
|
+
|
|
3
|
+
import type { Ref } from 'vue';
|
|
4
|
+
|
|
5
|
+
const LIKE_KEY = 'WALIKE_LIKE';
|
|
6
|
+
|
|
7
|
+
export type LikeID = number | string;
|
|
8
|
+
|
|
9
|
+
export type LikeRef = Ref<LikeID[]>;
|
|
10
|
+
|
|
11
|
+
let likeStorage: LikeRef | null = null;
|
|
12
|
+
|
|
13
|
+
export const useLikeStorage = (): LikeRef =>
|
|
14
|
+
likeStorage || (likeStorage = useStorage<LikeID[]>(LIKE_KEY, []));
|