@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.
- package/dist/api.cjs +1 -1
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +22 -20
- package/dist/api.d.mts +22 -20
- package/dist/api.d.ts +22 -20
- package/dist/api.mjs +1 -1
- package/dist/api.mjs.map +1 -1
- package/dist/comment.cjs +1 -1
- package/dist/comment.cjs.map +1 -1
- package/dist/comment.d.cts +1 -1
- package/dist/comment.d.mts +1 -1
- package/dist/comment.d.ts +1 -1
- package/dist/comment.js +1 -68
- package/dist/comment.js.map +1 -1
- package/dist/comment.mjs +1 -1
- package/dist/comment.mjs.map +1 -1
- package/dist/component.mjs +1 -1
- package/dist/component.mjs.map +1 -1
- package/dist/legacy.umd.d.ts +15 -6
- package/dist/legacy.umd.js +1 -1
- package/dist/legacy.umd.js.map +1 -1
- package/dist/pageview.cjs +1 -1
- package/dist/pageview.cjs.map +1 -1
- package/dist/pageview.d.cts +1 -1
- package/dist/pageview.d.mts +1 -1
- package/dist/pageview.d.ts +1 -1
- package/dist/pageview.js +1 -121
- package/dist/pageview.js.map +1 -1
- package/dist/pageview.mjs +1 -1
- package/dist/pageview.mjs.map +1 -1
- package/dist/shim.cjs +1 -1
- package/dist/shim.cjs.map +1 -1
- package/dist/shim.d.cts +20 -11
- package/dist/shim.d.mts +20 -11
- package/dist/shim.mjs +1 -1
- package/dist/shim.mjs.map +1 -1
- package/dist/waline.cjs +1 -1
- package/dist/waline.cjs.map +1 -1
- package/dist/waline.css +1 -1
- package/dist/waline.css.map +1 -1
- package/dist/waline.d.cts +20 -11
- package/dist/waline.d.mts +20 -11
- package/dist/waline.d.ts +20 -11
- package/dist/waline.js +1 -6787
- package/dist/waline.js.map +1 -1
- package/dist/waline.mjs +1 -1
- package/dist/waline.mjs.map +1 -1
- package/package.json +28 -27
- package/src/api/comment.ts +25 -24
- package/src/comment.ts +3 -4
- package/src/components/ArticleReaction.vue +120 -117
- package/src/components/CommentBox.vue +451 -488
- package/src/components/CommentCard.vue +109 -98
- package/src/components/ImageWall.vue +132 -131
- package/src/components/WalineComment.vue +683 -0
- package/src/composables/index.ts +1 -2
- package/src/composables/reaction.ts +16 -0
- package/src/composables/recaptchaV3.ts +4 -6
- package/src/config/default.ts +6 -1
- package/src/config/i18n/index.ts +1 -0
- package/src/{entrys → entries}/api.ts +0 -0
- package/src/{entrys → entries}/comment.ts +0 -0
- package/src/entries/components.ts +2 -0
- package/src/{entrys → entries}/full.ts +0 -0
- package/src/{entrys → entries}/init.ts +0 -0
- package/src/{entrys → entries}/legacy.ts +0 -0
- package/src/{entrys → entries}/pageview.ts +0 -0
- package/src/init.ts +1 -1
- package/src/pageview.ts +2 -2
- package/src/styles/reaction.scss +27 -16
- package/src/typings/base.ts +5 -0
- package/src/typings/waline.ts +15 -6
- package/src/utils/config.ts +28 -6
- package/src/utils/image.ts +1 -1
- package/src/widgets/recentComments.ts +2 -2
- package/src/widgets/userList.ts +2 -2
- package/LICENSE +0 -339
- package/src/components/Waline.vue +0 -509
- package/src/composables/timeAgo.ts +0 -15
- package/src/composables/vote.ts +0 -20
- package/src/entrys/components.ts +0 -2
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
class="wl-login-info"
|
|
6
6
|
>
|
|
7
7
|
<div class="wl-avatar">
|
|
8
|
-
<button
|
|
8
|
+
<button
|
|
9
|
+
type="submit"
|
|
10
|
+
class="wl-logout-btn"
|
|
11
|
+
:title="locale.logout"
|
|
12
|
+
@click="onLogout"
|
|
13
|
+
>
|
|
9
14
|
<CloseIcon :size="14" />
|
|
10
15
|
</button>
|
|
11
16
|
|
|
@@ -19,6 +24,7 @@
|
|
|
19
24
|
<img :src="userInfo.avatar" alt="avatar" />
|
|
20
25
|
</a>
|
|
21
26
|
</div>
|
|
27
|
+
|
|
22
28
|
<a
|
|
23
29
|
href="#"
|
|
24
30
|
class="wl-login-nick"
|
|
@@ -32,7 +38,8 @@
|
|
|
32
38
|
<div class="wl-panel">
|
|
33
39
|
<div
|
|
34
40
|
v-if="config.login !== 'force' && config.meta.length && !isLogin"
|
|
35
|
-
|
|
41
|
+
class="wl-header"
|
|
42
|
+
:class="`item${config.meta.length}`"
|
|
36
43
|
>
|
|
37
44
|
<div v-for="kind in config.meta" :key="kind" class="wl-header-item">
|
|
38
45
|
<label
|
|
@@ -44,6 +51,7 @@
|
|
|
44
51
|
: `(${locale.optional})`)
|
|
45
52
|
"
|
|
46
53
|
/>
|
|
54
|
+
|
|
47
55
|
<input
|
|
48
56
|
:id="`wl-${kind}`"
|
|
49
57
|
:ref="
|
|
@@ -52,7 +60,8 @@
|
|
|
52
60
|
}
|
|
53
61
|
"
|
|
54
62
|
v-model="userMeta[kind]"
|
|
55
|
-
|
|
63
|
+
class="wl-input"
|
|
64
|
+
:class="`wl-${kind}`"
|
|
56
65
|
:name="kind"
|
|
57
66
|
:type="kind === 'mail' ? 'email' : 'text'"
|
|
58
67
|
/>
|
|
@@ -72,6 +81,7 @@
|
|
|
72
81
|
|
|
73
82
|
<div v-show="showPreview" class="wl-preview">
|
|
74
83
|
<hr />
|
|
84
|
+
|
|
75
85
|
<h4>{{ locale.preview }}:</h4>
|
|
76
86
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
77
87
|
<div class="wl-content" v-html="previewText" />
|
|
@@ -85,7 +95,7 @@
|
|
|
85
95
|
aria-label="Markdown is supported"
|
|
86
96
|
class="wl-action"
|
|
87
97
|
target="_blank"
|
|
88
|
-
rel="noreferrer"
|
|
98
|
+
rel="noopener noreferrer"
|
|
89
99
|
>
|
|
90
100
|
<MarkdownIcon />
|
|
91
101
|
</a>
|
|
@@ -93,6 +103,7 @@
|
|
|
93
103
|
<button
|
|
94
104
|
v-show="emoji.tabs.length"
|
|
95
105
|
ref="emojiButtonRef"
|
|
106
|
+
type="button"
|
|
96
107
|
class="wl-action"
|
|
97
108
|
:class="{ active: showEmoji }"
|
|
98
109
|
:title="locale.emoji"
|
|
@@ -104,6 +115,7 @@
|
|
|
104
115
|
<button
|
|
105
116
|
v-if="config.search"
|
|
106
117
|
ref="gifButtonRef"
|
|
118
|
+
type="button"
|
|
107
119
|
class="wl-action"
|
|
108
120
|
:class="{ active: showGif }"
|
|
109
121
|
:title="locale.gif"
|
|
@@ -131,6 +143,7 @@
|
|
|
131
143
|
</label>
|
|
132
144
|
|
|
133
145
|
<button
|
|
146
|
+
type="button"
|
|
134
147
|
class="wl-action"
|
|
135
148
|
:class="{ active: showPreview }"
|
|
136
149
|
:title="locale.preview"
|
|
@@ -157,6 +170,7 @@
|
|
|
157
170
|
|
|
158
171
|
<button
|
|
159
172
|
v-if="config.login !== 'disable' && !isLogin"
|
|
173
|
+
type="button"
|
|
160
174
|
class="wl-btn"
|
|
161
175
|
@click="onLogin"
|
|
162
176
|
v-text="locale.login"
|
|
@@ -164,12 +178,14 @@
|
|
|
164
178
|
|
|
165
179
|
<button
|
|
166
180
|
v-if="config.login !== 'force' || isLogin"
|
|
167
|
-
|
|
181
|
+
type="submit"
|
|
182
|
+
class="primary wl-btn"
|
|
168
183
|
title="Cmd|Ctrl + Enter"
|
|
169
184
|
:disabled="isSubmitting"
|
|
170
185
|
@click="submitComment"
|
|
171
186
|
>
|
|
172
187
|
<LoadingIcon v-if="isSubmitting" :size="16" />
|
|
188
|
+
|
|
173
189
|
<template v-else>
|
|
174
190
|
{{ locale.submit }}
|
|
175
191
|
</template>
|
|
@@ -189,7 +205,7 @@
|
|
|
189
205
|
/>
|
|
190
206
|
|
|
191
207
|
<ImageWall
|
|
192
|
-
:items="
|
|
208
|
+
:items="searchResults.list"
|
|
193
209
|
:column-width="200"
|
|
194
210
|
:gap="6"
|
|
195
211
|
@insert="insert($event)"
|
|
@@ -197,10 +213,11 @@
|
|
|
197
213
|
>
|
|
198
214
|
</ImageWall>
|
|
199
215
|
|
|
200
|
-
<div v-if="
|
|
216
|
+
<div v-if="searchResults.loading" class="wl-loading">
|
|
201
217
|
<LoadingIcon :size="30" />
|
|
202
218
|
</div>
|
|
203
219
|
</div>
|
|
220
|
+
|
|
204
221
|
<div
|
|
205
222
|
ref="emojiPopupRef"
|
|
206
223
|
class="wl-emoji-popup"
|
|
@@ -214,6 +231,7 @@
|
|
|
214
231
|
<button
|
|
215
232
|
v-for="key in emojiItem.items"
|
|
216
233
|
:key="key"
|
|
234
|
+
type="button"
|
|
217
235
|
:title="key"
|
|
218
236
|
@click="insert(`:${key}:`)"
|
|
219
237
|
>
|
|
@@ -228,10 +246,12 @@
|
|
|
228
246
|
</button>
|
|
229
247
|
</div>
|
|
230
248
|
</template>
|
|
249
|
+
|
|
231
250
|
<div v-if="emoji.tabs.length > 1" class="wl-tabs">
|
|
232
251
|
<button
|
|
233
252
|
v-for="(emojiItem, index) in emoji.tabs"
|
|
234
253
|
:key="emojiItem.name"
|
|
254
|
+
type="button"
|
|
235
255
|
class="wl-tab"
|
|
236
256
|
:class="{ active: emojiTabIndex === index }"
|
|
237
257
|
@click="emojiTabIndex = index"
|
|
@@ -252,26 +272,24 @@
|
|
|
252
272
|
|
|
253
273
|
<button
|
|
254
274
|
v-if="replyId || edit?.objectId"
|
|
275
|
+
type="button"
|
|
255
276
|
class="wl-close"
|
|
256
277
|
:title="locale.cancelReply"
|
|
257
|
-
@click="$emit(replyId ? '
|
|
278
|
+
@click="$emit(replyId ? 'cancelReply' : 'cancelEdit')"
|
|
258
279
|
>
|
|
259
280
|
<CloseIcon :size="24" />
|
|
260
281
|
</button>
|
|
261
282
|
</div>
|
|
262
283
|
</template>
|
|
263
284
|
|
|
264
|
-
<script lang="ts">
|
|
285
|
+
<script setup lang="ts">
|
|
265
286
|
import { useDebounceFn } from '@vueuse/core';
|
|
266
|
-
import { useReCaptcha } from '../composables';
|
|
267
287
|
import autosize from 'autosize';
|
|
268
288
|
import {
|
|
269
289
|
computed,
|
|
270
|
-
defineComponent,
|
|
271
290
|
inject,
|
|
272
291
|
onMounted,
|
|
273
292
|
onUnmounted,
|
|
274
|
-
PropType,
|
|
275
293
|
reactive,
|
|
276
294
|
ref,
|
|
277
295
|
watch,
|
|
@@ -280,22 +298,27 @@ import {
|
|
|
280
298
|
import {
|
|
281
299
|
CloseIcon,
|
|
282
300
|
EmojiIcon,
|
|
301
|
+
GifIcon,
|
|
283
302
|
ImageIcon,
|
|
303
|
+
LoadingIcon,
|
|
284
304
|
MarkdownIcon,
|
|
285
305
|
PreviewIcon,
|
|
286
|
-
LoadingIcon,
|
|
287
|
-
GifIcon,
|
|
288
306
|
} from './Icons';
|
|
289
307
|
import ImageWall from './ImageWall.vue';
|
|
290
|
-
import { addComment, login, updateComment } from '../api';
|
|
291
|
-
import {
|
|
308
|
+
import { addComment, login, updateComment, UserInfo } from '../api/index.js';
|
|
309
|
+
import {
|
|
310
|
+
useEditor,
|
|
311
|
+
useReCaptcha,
|
|
312
|
+
useUserInfo,
|
|
313
|
+
useUserMeta,
|
|
314
|
+
} from '../composables/index.js';
|
|
292
315
|
import {
|
|
293
316
|
getEmojis,
|
|
294
|
-
|
|
317
|
+
getImageFromDataTransfer,
|
|
295
318
|
getWordNumber,
|
|
296
319
|
parseEmoji,
|
|
297
320
|
parseMarkdown,
|
|
298
|
-
} from '../utils';
|
|
321
|
+
} from '../utils/index.js';
|
|
299
322
|
|
|
300
323
|
import type { ComputedRef, DeepReadonly } from 'vue';
|
|
301
324
|
import type {
|
|
@@ -304,510 +327,450 @@ import type {
|
|
|
304
327
|
WalineImageUploader,
|
|
305
328
|
WalineSearchOptions,
|
|
306
329
|
WalineSearchResult,
|
|
307
|
-
} from '../typings';
|
|
308
|
-
import type { WalineConfig, WalineEmojiConfig } from '../utils';
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
replyUser:
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
const wordLimit = ref(0);
|
|
375
|
-
const isWordNumberLegal = ref(false);
|
|
376
|
-
|
|
377
|
-
const content = ref('');
|
|
378
|
-
|
|
379
|
-
const isSubmitting = ref(false);
|
|
380
|
-
|
|
381
|
-
const locale = computed(() => config.value.locale);
|
|
382
|
-
|
|
383
|
-
const isLogin = computed(() => Boolean(userInfo.value?.token));
|
|
384
|
-
|
|
385
|
-
const canUploadImage = computed(() => config.value.imageUploader !== false);
|
|
386
|
-
|
|
387
|
-
const insert = (content: string): void => {
|
|
388
|
-
const textArea = editorRef.value!;
|
|
389
|
-
const startPosition = textArea.selectionStart;
|
|
390
|
-
const endPosition = textArea.selectionEnd || 0;
|
|
391
|
-
const scrollTop = textArea.scrollTop;
|
|
392
|
-
|
|
393
|
-
editor.value =
|
|
394
|
-
textArea.value.substring(0, startPosition) +
|
|
395
|
-
content +
|
|
396
|
-
textArea.value.substring(endPosition, textArea.value.length);
|
|
397
|
-
textArea.focus();
|
|
398
|
-
textArea.selectionStart = startPosition + content.length;
|
|
399
|
-
textArea.selectionEnd = startPosition + content.length;
|
|
400
|
-
textArea.scrollTop = scrollTop;
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
const onKeyDown = (event: KeyboardEvent): void => {
|
|
404
|
-
const key = event.key;
|
|
405
|
-
|
|
406
|
-
// Shortcut key
|
|
407
|
-
if ((event.ctrlKey || event.metaKey) && key === 'Enter') submitComment();
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const uploadImage = (file: File): Promise<void> => {
|
|
411
|
-
const uploadText = `![${config.value.locale.uploading} ${file.name}]()`;
|
|
412
|
-
|
|
413
|
-
insert(uploadText);
|
|
414
|
-
|
|
415
|
-
return Promise.resolve()
|
|
416
|
-
.then(() => (config.value.imageUploader as WalineImageUploader)(file))
|
|
417
|
-
.then((url) => {
|
|
418
|
-
editor.value = editor.value.replace(
|
|
419
|
-
uploadText,
|
|
420
|
-
`\r\n`
|
|
421
|
-
);
|
|
422
|
-
})
|
|
423
|
-
.catch((e) => {
|
|
424
|
-
alert(e.message);
|
|
425
|
-
editor.value = editor.value.replace(uploadText, '');
|
|
426
|
-
});
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
const onDrop = (event: DragEvent): void => {
|
|
430
|
-
if (event.dataTransfer?.items) {
|
|
431
|
-
const file = getImagefromDataTransfer(event.dataTransfer.items);
|
|
432
|
-
|
|
433
|
-
if (file && canUploadImage.value) {
|
|
434
|
-
uploadImage(file);
|
|
435
|
-
event.preventDefault();
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
const onPaste = (event: ClipboardEvent): void => {
|
|
441
|
-
if (event.clipboardData) {
|
|
442
|
-
const file = getImagefromDataTransfer(event.clipboardData.items);
|
|
443
|
-
|
|
444
|
-
if (file && canUploadImage.value) uploadImage(file);
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
const onChange = (): void => {
|
|
449
|
-
const inputElement = imageUploadRef.value!;
|
|
450
|
-
|
|
451
|
-
if (inputElement.files && canUploadImage.value)
|
|
452
|
-
uploadImage(inputElement.files[0]).then(() => {
|
|
453
|
-
// clear input so a same image can be uploaded later
|
|
454
|
-
inputElement.value = '';
|
|
455
|
-
});
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const submitComment = async (): Promise<void> => {
|
|
459
|
-
const { serverURL, lang, login, wordLimit, requiredMeta } = config.value;
|
|
460
|
-
|
|
461
|
-
let token = '';
|
|
462
|
-
|
|
463
|
-
if (config.value.recaptchaV3Key)
|
|
464
|
-
token = await useReCaptcha(config.value.recaptchaV3Key).execute(
|
|
465
|
-
'social'
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
const comment: WalineCommentData = {
|
|
469
|
-
comment: content.value,
|
|
470
|
-
nick: userMeta.value.nick,
|
|
471
|
-
mail: userMeta.value.mail,
|
|
472
|
-
link: userMeta.value.link,
|
|
473
|
-
ua: navigator.userAgent,
|
|
474
|
-
url: config.value.path,
|
|
475
|
-
recaptchaV3: token,
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
if (userInfo.value?.token) {
|
|
479
|
-
// login user
|
|
480
|
-
|
|
481
|
-
comment.nick = userInfo.value.display_name;
|
|
482
|
-
comment.mail = userInfo.value.email;
|
|
483
|
-
comment.link = userInfo.value.url;
|
|
484
|
-
} else {
|
|
485
|
-
if (login === 'force') return;
|
|
486
|
-
|
|
487
|
-
// check nick
|
|
488
|
-
if (requiredMeta.indexOf('nick') > -1 && !comment.nick) {
|
|
489
|
-
inputRefs.value.nick?.focus();
|
|
490
|
-
|
|
491
|
-
return alert(locale.value.nickError);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
// check mail
|
|
495
|
-
if (
|
|
496
|
-
(requiredMeta.indexOf('mail') > -1 && !comment.mail) ||
|
|
497
|
-
(comment.mail &&
|
|
498
|
-
!/^\w(?:[\w._-]*\w)?@(?:\w(?:[\w-]*\w)?\.)*\w+$/.exec(comment.mail))
|
|
499
|
-
) {
|
|
500
|
-
inputRefs.value.mail?.focus();
|
|
330
|
+
} from '../typings/index.js';
|
|
331
|
+
import type { WalineConfig, WalineEmojiConfig } from '../utils/index.js';
|
|
332
|
+
|
|
333
|
+
const props = withDefaults(
|
|
334
|
+
defineProps<{
|
|
335
|
+
/**
|
|
336
|
+
* Current comment to be edited
|
|
337
|
+
*/
|
|
338
|
+
edit?: WalineComment | null;
|
|
339
|
+
/**
|
|
340
|
+
* Root comment id
|
|
341
|
+
*/
|
|
342
|
+
rootId?: string;
|
|
343
|
+
/**
|
|
344
|
+
* Comment id to be replied
|
|
345
|
+
*/
|
|
346
|
+
replyId?: string;
|
|
347
|
+
/**
|
|
348
|
+
* User name to be replied
|
|
349
|
+
*/
|
|
350
|
+
replyUser?: string;
|
|
351
|
+
}>(),
|
|
352
|
+
{
|
|
353
|
+
edit: null,
|
|
354
|
+
rootId: '',
|
|
355
|
+
replyId: '',
|
|
356
|
+
replyUser: '',
|
|
357
|
+
}
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
const emit = defineEmits<{
|
|
361
|
+
(event: 'log'): void;
|
|
362
|
+
(event: 'cancelEdit'): void;
|
|
363
|
+
(event: 'cancelReply'): void;
|
|
364
|
+
(event: 'submit', comment: WalineComment): void;
|
|
365
|
+
}>();
|
|
366
|
+
|
|
367
|
+
defineExpose();
|
|
368
|
+
|
|
369
|
+
const config = inject<ComputedRef<WalineConfig>>('config')!;
|
|
370
|
+
|
|
371
|
+
const editor = useEditor();
|
|
372
|
+
const userMeta = useUserMeta();
|
|
373
|
+
const userInfo = useUserInfo();
|
|
374
|
+
|
|
375
|
+
const inputRefs = ref<Record<string, HTMLInputElement>>({});
|
|
376
|
+
const editorRef = ref<HTMLTextAreaElement | null>(null);
|
|
377
|
+
const imageUploadRef = ref<HTMLInputElement | null>(null);
|
|
378
|
+
const emojiButtonRef = ref<HTMLDivElement | null>(null);
|
|
379
|
+
const emojiPopupRef = ref<HTMLDivElement | null>(null);
|
|
380
|
+
const gifButtonRef = ref<HTMLDivElement | null>(null);
|
|
381
|
+
const gifPopupRef = ref<HTMLDivElement | null>(null);
|
|
382
|
+
const gifSearchInputRef = ref<HTMLInputElement | null>(null);
|
|
383
|
+
|
|
384
|
+
const emoji = ref<DeepReadonly<WalineEmojiConfig>>({ tabs: [], map: {} });
|
|
385
|
+
const emojiTabIndex = ref(0);
|
|
386
|
+
const showEmoji = ref(false);
|
|
387
|
+
const showGif = ref(false);
|
|
388
|
+
const showPreview = ref(false);
|
|
389
|
+
const previewText = ref('');
|
|
390
|
+
const wordNumber = ref(0);
|
|
391
|
+
|
|
392
|
+
const searchResults = reactive({
|
|
393
|
+
loading: true,
|
|
394
|
+
list: [] as WalineSearchResult,
|
|
395
|
+
});
|
|
501
396
|
|
|
502
|
-
|
|
503
|
-
|
|
397
|
+
const wordLimit = ref(0);
|
|
398
|
+
const isWordNumberLegal = ref(false);
|
|
504
399
|
|
|
505
|
-
|
|
506
|
-
if (!comment.comment) {
|
|
507
|
-
editorRef.value?.focus();
|
|
400
|
+
const content = ref('');
|
|
508
401
|
|
|
509
|
-
|
|
510
|
-
}
|
|
402
|
+
const isSubmitting = ref(false);
|
|
511
403
|
|
|
512
|
-
|
|
513
|
-
}
|
|
404
|
+
const locale = computed(() => config.value.locale);
|
|
514
405
|
|
|
515
|
-
|
|
516
|
-
return alert(
|
|
517
|
-
locale.value.wordHint
|
|
518
|
-
.replace('$0', (wordLimit as [number, number])[0].toString())
|
|
519
|
-
.replace('$1', (wordLimit as [number, number])[1].toString())
|
|
520
|
-
.replace('$2', wordNumber.value.toString())
|
|
521
|
-
);
|
|
406
|
+
const isLogin = computed(() => Boolean(userInfo.value?.token));
|
|
522
407
|
|
|
523
|
-
|
|
408
|
+
const canUploadImage = computed(() => config.value.imageUploader !== false);
|
|
524
409
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
410
|
+
const insert = (content: string): void => {
|
|
411
|
+
const textArea = editorRef.value!;
|
|
412
|
+
const startPosition = textArea.selectionStart;
|
|
413
|
+
const endPosition = textArea.selectionEnd || 0;
|
|
414
|
+
const scrollTop = textArea.scrollTop;
|
|
530
415
|
|
|
531
|
-
|
|
416
|
+
editor.value =
|
|
417
|
+
textArea.value.substring(0, startPosition) +
|
|
418
|
+
content +
|
|
419
|
+
textArea.value.substring(endPosition, textArea.value.length);
|
|
420
|
+
textArea.focus();
|
|
421
|
+
textArea.selectionStart = startPosition + content.length;
|
|
422
|
+
textArea.selectionEnd = startPosition + content.length;
|
|
423
|
+
textArea.scrollTop = scrollTop;
|
|
424
|
+
};
|
|
532
425
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
lang,
|
|
536
|
-
token: userInfo.value?.token,
|
|
537
|
-
comment,
|
|
538
|
-
};
|
|
426
|
+
const onKeyDown = (event: KeyboardEvent): void => {
|
|
427
|
+
const key = event.key;
|
|
539
428
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
)
|
|
544
|
-
.then((resp) => {
|
|
545
|
-
isSubmitting.value = false;
|
|
429
|
+
// Shortcut key
|
|
430
|
+
if ((event.ctrlKey || event.metaKey) && key === 'Enter') void submitComment();
|
|
431
|
+
};
|
|
546
432
|
|
|
547
|
-
|
|
433
|
+
const uploadImage = (file: File): Promise<void> => {
|
|
434
|
+
const uploadText = `![${config.value.locale.uploading} ${file.name}]()`;
|
|
548
435
|
|
|
549
|
-
|
|
436
|
+
insert(uploadText);
|
|
550
437
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
438
|
+
return Promise.resolve()
|
|
439
|
+
.then(() => (config.value.imageUploader as WalineImageUploader)(file))
|
|
440
|
+
.then((url) => {
|
|
441
|
+
editor.value = editor.value.replace(
|
|
442
|
+
uploadText,
|
|
443
|
+
`\r\n`
|
|
444
|
+
);
|
|
445
|
+
})
|
|
446
|
+
.catch((err: Error) => {
|
|
447
|
+
alert(err.message);
|
|
448
|
+
editor.value = editor.value.replace(uploadText, '');
|
|
449
|
+
});
|
|
450
|
+
};
|
|
560
451
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
452
|
+
const onDrop = (event: DragEvent): void => {
|
|
453
|
+
if (event.dataTransfer?.items) {
|
|
454
|
+
const file = getImageFromDataTransfer(event.dataTransfer.items);
|
|
564
455
|
|
|
565
|
-
|
|
456
|
+
if (file && canUploadImage.value) {
|
|
457
|
+
void uploadImage(file);
|
|
566
458
|
event.preventDefault();
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
serverURL,
|
|
571
|
-
lang,
|
|
572
|
-
}).then((data) => {
|
|
573
|
-
userInfo.value = data;
|
|
574
|
-
(data.remember ? localStorage : sessionStorage).setItem(
|
|
575
|
-
'WALINE_USER',
|
|
576
|
-
JSON.stringify(data)
|
|
577
|
-
);
|
|
578
|
-
});
|
|
579
|
-
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
580
462
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
sessionStorage.setItem('WALINE_USER', 'null');
|
|
585
|
-
};
|
|
463
|
+
const onPaste = (event: ClipboardEvent): void => {
|
|
464
|
+
if (event.clipboardData) {
|
|
465
|
+
const file = getImageFromDataTransfer(event.clipboardData.items);
|
|
586
466
|
|
|
587
|
-
|
|
588
|
-
|
|
467
|
+
if (file && canUploadImage.value) void uploadImage(file);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
589
470
|
|
|
590
|
-
|
|
471
|
+
const onChange = (): void => {
|
|
472
|
+
const inputElement = imageUploadRef.value!;
|
|
591
473
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
if (wordNumber < limit[0] && limit[0] !== 0) {
|
|
661
|
-
wordLimit.value = limit[0];
|
|
662
|
-
isWordNumberLegal.value = false;
|
|
663
|
-
} else if (wordNumber > limit[1]) {
|
|
664
|
-
wordLimit.value = limit[1];
|
|
665
|
-
isWordNumberLegal.value = false;
|
|
666
|
-
} else {
|
|
667
|
-
wordLimit.value = limit[1];
|
|
668
|
-
isWordNumberLegal.value = true;
|
|
669
|
-
}
|
|
670
|
-
} else {
|
|
671
|
-
wordLimit.value = 0;
|
|
672
|
-
isWordNumberLegal.value = true;
|
|
673
|
-
}
|
|
674
|
-
},
|
|
675
|
-
{ immediate: true }
|
|
474
|
+
if (inputElement.files && canUploadImage.value)
|
|
475
|
+
void uploadImage(inputElement.files[0]).then(() => {
|
|
476
|
+
// clear input so a same image can be uploaded later
|
|
477
|
+
inputElement.value = '';
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const submitComment = async (): Promise<void> => {
|
|
482
|
+
const { serverURL, lang, login, wordLimit, requiredMeta } = config.value;
|
|
483
|
+
|
|
484
|
+
let token = '';
|
|
485
|
+
|
|
486
|
+
if (config.value.recaptchaV3Key)
|
|
487
|
+
token = await useReCaptcha(config.value.recaptchaV3Key).execute('social');
|
|
488
|
+
|
|
489
|
+
const comment: WalineCommentData = {
|
|
490
|
+
comment: content.value,
|
|
491
|
+
nick: userMeta.value.nick,
|
|
492
|
+
mail: userMeta.value.mail,
|
|
493
|
+
link: userMeta.value.link,
|
|
494
|
+
ua: navigator.userAgent,
|
|
495
|
+
url: config.value.path,
|
|
496
|
+
recaptchaV3: token,
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
if (userInfo.value?.token) {
|
|
500
|
+
// login user
|
|
501
|
+
|
|
502
|
+
comment.nick = userInfo.value.display_name;
|
|
503
|
+
comment.mail = userInfo.value.email;
|
|
504
|
+
comment.link = userInfo.value.url;
|
|
505
|
+
} else {
|
|
506
|
+
if (login === 'force') return;
|
|
507
|
+
|
|
508
|
+
// check nick
|
|
509
|
+
if (requiredMeta.indexOf('nick') > -1 && !comment.nick) {
|
|
510
|
+
inputRefs.value.nick?.focus();
|
|
511
|
+
|
|
512
|
+
return alert(locale.value.nickError);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// check mail
|
|
516
|
+
if (
|
|
517
|
+
(requiredMeta.indexOf('mail') > -1 && !comment.mail) ||
|
|
518
|
+
(comment.mail &&
|
|
519
|
+
!/^\w(?:[\w._-]*\w)?@(?:\w(?:[\w-]*\w)?\.)*\w+$/.exec(comment.mail))
|
|
520
|
+
) {
|
|
521
|
+
inputRefs.value.mail?.focus();
|
|
522
|
+
|
|
523
|
+
return alert(locale.value.mailError);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// check comment
|
|
527
|
+
if (!comment.comment) {
|
|
528
|
+
editorRef.value?.focus();
|
|
529
|
+
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (!comment.nick) comment.nick = locale.value.anonymous;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (!isWordNumberLegal.value)
|
|
537
|
+
return alert(
|
|
538
|
+
locale.value.wordHint
|
|
539
|
+
.replace('$0', (wordLimit as [number, number])[0].toString())
|
|
540
|
+
.replace('$1', (wordLimit as [number, number])[1].toString())
|
|
541
|
+
.replace('$2', wordNumber.value.toString())
|
|
676
542
|
);
|
|
677
543
|
|
|
678
|
-
|
|
679
|
-
const onMessageReceive = ({ data }: any): void => {
|
|
680
|
-
if (!data || data.type !== 'profile') return;
|
|
544
|
+
comment.comment = parseEmoji(comment.comment, emoji.value.map);
|
|
681
545
|
|
|
682
|
-
|
|
546
|
+
if (props.replyId && props.rootId) {
|
|
547
|
+
comment.pid = props.replyId;
|
|
548
|
+
comment.rid = props.rootId;
|
|
549
|
+
comment.at = props.replyUser;
|
|
550
|
+
}
|
|
683
551
|
|
|
684
|
-
|
|
685
|
-
.filter((store) => store.getItem('WALINE_USER'))
|
|
686
|
-
.forEach((store) =>
|
|
687
|
-
store.setItem('WALINE_USER', JSON.stringify(userInfo))
|
|
688
|
-
);
|
|
689
|
-
};
|
|
552
|
+
isSubmitting.value = true;
|
|
690
553
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
// watch gif
|
|
699
|
-
watch(showGif, async (showGif) => {
|
|
700
|
-
if (!showGif) return;
|
|
554
|
+
const options = {
|
|
555
|
+
serverURL,
|
|
556
|
+
lang,
|
|
557
|
+
token: userInfo.value?.token,
|
|
558
|
+
comment,
|
|
559
|
+
};
|
|
701
560
|
|
|
702
|
-
|
|
561
|
+
void (
|
|
562
|
+
props.edit
|
|
563
|
+
? updateComment({ objectId: props.edit.objectId, ...options })
|
|
564
|
+
: addComment(options)
|
|
565
|
+
)
|
|
566
|
+
.then((resp) => {
|
|
567
|
+
isSubmitting.value = false;
|
|
703
568
|
|
|
704
|
-
|
|
705
|
-
if (gifSearchInputRef.value) gifSearchInputRef.value.value = '';
|
|
569
|
+
if (resp.errmsg) return alert(resp.errmsg);
|
|
706
570
|
|
|
707
|
-
|
|
571
|
+
emit('submit', resp.data!);
|
|
708
572
|
|
|
709
|
-
|
|
710
|
-
? await searchOptions.default()
|
|
711
|
-
: await searchOptions.search('');
|
|
573
|
+
editor.value = '';
|
|
712
574
|
|
|
713
|
-
|
|
714
|
-
});
|
|
575
|
+
previewText.value = '';
|
|
715
576
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
content.value = value;
|
|
723
|
-
previewText.value = parseMarkdown(value, {
|
|
724
|
-
emojiMap: emoji.value.map,
|
|
725
|
-
highlighter,
|
|
726
|
-
texRenderer,
|
|
727
|
-
});
|
|
728
|
-
wordNumber.value = getWordNumber(value);
|
|
729
|
-
|
|
730
|
-
if (value) autosize(editorRef.value!);
|
|
731
|
-
else autosize.destroy(editorRef.value!);
|
|
732
|
-
},
|
|
733
|
-
{ immediate: true }
|
|
734
|
-
);
|
|
577
|
+
if (props.replyId) emit('cancelReply');
|
|
578
|
+
if (props.edit?.objectId) emit('cancelEdit');
|
|
579
|
+
})
|
|
580
|
+
.catch((err: TypeError) => {
|
|
581
|
+
isSubmitting.value = false;
|
|
735
582
|
|
|
736
|
-
|
|
737
|
-
watch(
|
|
738
|
-
() => config.value.emoji,
|
|
739
|
-
(emojiConfig) =>
|
|
740
|
-
getEmojis(Array.isArray(emojiConfig) ? emojiConfig : []).then(
|
|
741
|
-
(config) => {
|
|
742
|
-
emoji.value = config;
|
|
743
|
-
}
|
|
744
|
-
),
|
|
745
|
-
{ immediate: true }
|
|
746
|
-
);
|
|
583
|
+
alert(err.message);
|
|
747
584
|
});
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const onLogin = (event: Event): void => {
|
|
588
|
+
event.preventDefault();
|
|
589
|
+
const { lang, serverURL } = config.value;
|
|
590
|
+
|
|
591
|
+
void login({
|
|
592
|
+
serverURL,
|
|
593
|
+
lang,
|
|
594
|
+
}).then((data) => {
|
|
595
|
+
userInfo.value = data;
|
|
596
|
+
(data.remember ? localStorage : sessionStorage).setItem(
|
|
597
|
+
'WALINE_USER',
|
|
598
|
+
JSON.stringify(data)
|
|
599
|
+
);
|
|
600
|
+
emit('log');
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
const onLogout = (): void => {
|
|
605
|
+
userInfo.value = {};
|
|
606
|
+
localStorage.setItem('WALINE_USER', 'null');
|
|
607
|
+
sessionStorage.setItem('WALINE_USER', 'null');
|
|
608
|
+
emit('log');
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
const onProfile = (event: Event): void => {
|
|
612
|
+
event.preventDefault();
|
|
613
|
+
|
|
614
|
+
const { lang, serverURL } = config.value;
|
|
615
|
+
|
|
616
|
+
const width = 800;
|
|
617
|
+
const height = 800;
|
|
618
|
+
const left = (window.innerWidth - width) / 2;
|
|
619
|
+
const top = (window.innerHeight - height) / 2;
|
|
620
|
+
const query = new URLSearchParams({
|
|
621
|
+
lng: lang,
|
|
622
|
+
token: userInfo.value.token,
|
|
623
|
+
});
|
|
624
|
+
const handler = window.open(
|
|
625
|
+
`${serverURL}/ui/profile?${query.toString()}`,
|
|
626
|
+
'_blank',
|
|
627
|
+
`width=${width},height=${height},left=${left},top=${top},scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no`
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
handler?.postMessage({ type: 'TOKEN', data: userInfo.value.token }, '*');
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
const popupHandler = (event: MouseEvent): void => {
|
|
634
|
+
if (
|
|
635
|
+
!emojiButtonRef.value?.contains(event.target as Node) &&
|
|
636
|
+
!emojiPopupRef.value?.contains(event.target as Node)
|
|
637
|
+
)
|
|
638
|
+
showEmoji.value = false;
|
|
639
|
+
|
|
640
|
+
if (
|
|
641
|
+
!gifButtonRef.value?.contains(event.target as Node) &&
|
|
642
|
+
!gifPopupRef.value?.contains(event.target as Node)
|
|
643
|
+
)
|
|
644
|
+
showGif.value = false;
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
const onImageWallScroll = async (event: Event): Promise<void> => {
|
|
648
|
+
const { scrollTop, clientHeight, scrollHeight } =
|
|
649
|
+
event.target as HTMLDivElement;
|
|
650
|
+
const percent = (clientHeight + scrollTop) / scrollHeight;
|
|
651
|
+
const searchOptions = config.value.search as WalineSearchOptions;
|
|
652
|
+
const keyword = gifSearchInputRef.value?.value || '';
|
|
653
|
+
|
|
654
|
+
if (percent < 0.9 || searchResults.loading) return;
|
|
655
|
+
|
|
656
|
+
searchResults.loading = true;
|
|
657
|
+
|
|
658
|
+
searchResults.list = [
|
|
659
|
+
...searchResults.list,
|
|
660
|
+
...(searchOptions.more && searchResults.list.length
|
|
661
|
+
? await searchOptions.more(keyword, searchResults.list.length)
|
|
662
|
+
: await searchOptions.search(keyword)),
|
|
663
|
+
];
|
|
664
|
+
|
|
665
|
+
searchResults.loading = false;
|
|
666
|
+
|
|
667
|
+
setTimeout(() => {
|
|
668
|
+
(event.target as HTMLDivElement).scrollTop = scrollTop;
|
|
669
|
+
}, 50);
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const onGifSearch = useDebounceFn((event: Event) => {
|
|
673
|
+
searchResults.list = [];
|
|
674
|
+
void onImageWallScroll(event);
|
|
675
|
+
}, 300);
|
|
676
|
+
|
|
677
|
+
// update wordNumber
|
|
678
|
+
watch(
|
|
679
|
+
[config, wordNumber],
|
|
680
|
+
([config, wordNumber]) => {
|
|
681
|
+
const { wordLimit: limit } = config;
|
|
682
|
+
|
|
683
|
+
if (limit) {
|
|
684
|
+
if (wordNumber < limit[0] && limit[0] !== 0) {
|
|
685
|
+
wordLimit.value = limit[0];
|
|
686
|
+
isWordNumberLegal.value = false;
|
|
687
|
+
} else if (wordNumber > limit[1]) {
|
|
688
|
+
wordLimit.value = limit[1];
|
|
689
|
+
isWordNumberLegal.value = false;
|
|
690
|
+
} else {
|
|
691
|
+
wordLimit.value = limit[1];
|
|
692
|
+
isWordNumberLegal.value = true;
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
wordLimit.value = 0;
|
|
696
|
+
isWordNumberLegal.value = true;
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
{ immediate: true }
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
const onMessageReceive = ({
|
|
703
|
+
data,
|
|
704
|
+
}: {
|
|
705
|
+
data: { type: 'profile'; data: UserInfo };
|
|
706
|
+
}): void => {
|
|
707
|
+
if (!data || data.type !== 'profile') return;
|
|
708
|
+
|
|
709
|
+
userInfo.value = { ...userInfo.value, ...data.data };
|
|
710
|
+
|
|
711
|
+
[localStorage, sessionStorage]
|
|
712
|
+
.filter((store) => store.getItem('WALINE_USER'))
|
|
713
|
+
.forEach((store) => store.setItem('WALINE_USER', JSON.stringify(userInfo)));
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
onMounted(() => {
|
|
717
|
+
document.body.addEventListener('click', popupHandler);
|
|
718
|
+
window.addEventListener('message', onMessageReceive);
|
|
719
|
+
if (props.edit?.objectId) {
|
|
720
|
+
editor.value = props.edit.orig;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// watch gif
|
|
724
|
+
watch(showGif, async (showGif) => {
|
|
725
|
+
if (!showGif) return;
|
|
726
|
+
|
|
727
|
+
const searchOptions = config.value.search as WalineSearchOptions;
|
|
728
|
+
|
|
729
|
+
// clear input
|
|
730
|
+
if (gifSearchInputRef.value) gifSearchInputRef.value.value = '';
|
|
731
|
+
|
|
732
|
+
searchResults.loading = true;
|
|
733
|
+
|
|
734
|
+
searchResults.list = searchOptions.default
|
|
735
|
+
? await searchOptions.default()
|
|
736
|
+
: await searchOptions.search('');
|
|
737
|
+
|
|
738
|
+
searchResults.loading = false;
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// watch editor
|
|
742
|
+
watch(
|
|
743
|
+
() => editor.value,
|
|
744
|
+
(value) => {
|
|
745
|
+
const { highlighter, texRenderer } = config.value;
|
|
746
|
+
|
|
747
|
+
content.value = value;
|
|
748
|
+
previewText.value = parseMarkdown(value, {
|
|
749
|
+
emojiMap: emoji.value.map,
|
|
750
|
+
highlighter,
|
|
751
|
+
texRenderer,
|
|
752
|
+
});
|
|
753
|
+
wordNumber.value = getWordNumber(value);
|
|
748
754
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
}
|
|
755
|
+
if (value) autosize(editorRef.value!);
|
|
756
|
+
else autosize.destroy(editorRef.value!);
|
|
757
|
+
},
|
|
758
|
+
{ immediate: true }
|
|
759
|
+
);
|
|
760
|
+
|
|
761
|
+
// watch emoji value change
|
|
762
|
+
watch(
|
|
763
|
+
() => config.value.emoji,
|
|
764
|
+
(emojiConfig) =>
|
|
765
|
+
getEmojis(emojiConfig).then((config) => {
|
|
766
|
+
emoji.value = config;
|
|
767
|
+
}),
|
|
768
|
+
{ immediate: true }
|
|
769
|
+
);
|
|
770
|
+
});
|
|
753
771
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
locale,
|
|
758
|
-
|
|
759
|
-
// events
|
|
760
|
-
insert,
|
|
761
|
-
onChange,
|
|
762
|
-
onDrop,
|
|
763
|
-
onKeyDown,
|
|
764
|
-
onPaste,
|
|
765
|
-
onLogin,
|
|
766
|
-
onLogout,
|
|
767
|
-
onProfile,
|
|
768
|
-
submitComment,
|
|
769
|
-
onImageWallScroll,
|
|
770
|
-
onGifSearch,
|
|
771
|
-
|
|
772
|
-
isLogin,
|
|
773
|
-
userInfo,
|
|
774
|
-
isSubmitting,
|
|
775
|
-
|
|
776
|
-
// word
|
|
777
|
-
wordNumber,
|
|
778
|
-
wordLimit,
|
|
779
|
-
isWordNumberLegal,
|
|
780
|
-
|
|
781
|
-
// inputs
|
|
782
|
-
editor,
|
|
783
|
-
userMeta,
|
|
784
|
-
|
|
785
|
-
// emoji
|
|
786
|
-
emoji,
|
|
787
|
-
emojiTabIndex,
|
|
788
|
-
showEmoji,
|
|
789
|
-
|
|
790
|
-
// gif
|
|
791
|
-
gifData: searchResults,
|
|
792
|
-
showGif,
|
|
793
|
-
|
|
794
|
-
// image
|
|
795
|
-
canUploadImage,
|
|
796
|
-
|
|
797
|
-
// preview
|
|
798
|
-
previewText,
|
|
799
|
-
showPreview,
|
|
800
|
-
|
|
801
|
-
// ref
|
|
802
|
-
inputRefs,
|
|
803
|
-
editorRef,
|
|
804
|
-
emojiButtonRef,
|
|
805
|
-
emojiPopupRef,
|
|
806
|
-
gifButtonRef,
|
|
807
|
-
gifPopupRef,
|
|
808
|
-
imageUploadRef,
|
|
809
|
-
gifSearchInputRef,
|
|
810
|
-
};
|
|
811
|
-
},
|
|
772
|
+
onUnmounted(() => {
|
|
773
|
+
document.body.removeEventListener('click', popupHandler);
|
|
774
|
+
window.removeEventListener('message', onMessageReceive);
|
|
812
775
|
});
|
|
813
776
|
</script>
|