@waline/client 2.5.0 → 2.6.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/LICENSE +339 -0
- 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 +40 -0
- package/dist/legacy.js +1 -1
- package/dist/legacy.js.map +1 -1
- package/dist/pageview.cjs.js +1 -1
- package/dist/pageview.cjs.js.map +1 -1
- 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 +41 -1
- package/dist/shim.esm.d.ts +41 -1
- 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 +41 -1
- 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 +41 -1
- package/dist/waline.esm.d.ts +41 -1
- 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 +31 -33
- package/src/comment.ts +7 -2
- package/src/components/CommentBox.vue +57 -63
- package/src/components/CommentCard.vue +1 -1
- package/src/components/ImageWall.vue +166 -0
- package/src/composables/like.ts +1 -1
- package/src/config/default.ts +91 -1
- package/src/init.ts +2 -10
- package/src/pageview.ts +7 -2
- package/src/styles/emoji.scss +112 -94
- package/src/styles/gif.scss +58 -139
- package/src/styles/index.scss +3 -0
- package/src/styles/panel.scss +0 -6
- package/src/typings/base.ts +40 -0
- package/src/typings/waline.ts +8 -0
- package/src/utils/config.ts +5 -2
- package/src/utils/index.ts +0 -2
- package/src/utils/fetchGif.ts +0 -62
- package/src/utils/throttle.ts +0 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waline/client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "client for waline comment system",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"valine",
|
|
@@ -56,15 +56,6 @@
|
|
|
56
56
|
"dist",
|
|
57
57
|
"src"
|
|
58
58
|
],
|
|
59
|
-
"scripts": {
|
|
60
|
-
"build": "pnpm rollup && pnpm style",
|
|
61
|
-
"clean": "rimraf ./dist",
|
|
62
|
-
"dev": "vite -c config/vite.config.js",
|
|
63
|
-
"lint": "eslint --ext .ts,.vue .",
|
|
64
|
-
"prepublish": "pnpm clean && pnpm build",
|
|
65
|
-
"rollup": "rollup -c ./config/rollup.config.js",
|
|
66
|
-
"style": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed"
|
|
67
|
-
},
|
|
68
59
|
"browserslist": {
|
|
69
60
|
"production": [
|
|
70
61
|
">0.5%",
|
|
@@ -80,33 +71,40 @@
|
|
|
80
71
|
]
|
|
81
72
|
},
|
|
82
73
|
"dependencies": {
|
|
83
|
-
"@vueuse/core": "^8.
|
|
74
|
+
"@vueuse/core": "^8.6.0",
|
|
84
75
|
"autosize": "^5.0.1",
|
|
85
|
-
"marked": "^4.0.
|
|
86
|
-
"vue": "^3.2.
|
|
76
|
+
"marked": "^4.0.17",
|
|
77
|
+
"vue": "^3.2.37"
|
|
87
78
|
},
|
|
88
79
|
"devDependencies": {
|
|
89
|
-
"@babel/core": "
|
|
90
|
-
"@babel/preset-env": "
|
|
91
|
-
"@rollup/plugin-babel": "
|
|
92
|
-
"@rollup/plugin-commonjs": "
|
|
93
|
-
"@rollup/plugin-node-resolve": "
|
|
94
|
-
"@rollup/plugin-replace": "
|
|
95
|
-
"@types/autosize": "
|
|
96
|
-
"@types/marked": "
|
|
97
|
-
"@types/node": "
|
|
98
|
-
"@vitejs/plugin-vue": "
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"rollup": "
|
|
103
|
-
"rollup-plugin-
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"typescript": "^4.6.4",
|
|
107
|
-
"vite": "^2.9.9"
|
|
80
|
+
"@babel/core": "7.18.5",
|
|
81
|
+
"@babel/preset-env": "7.18.2",
|
|
82
|
+
"@rollup/plugin-babel": "5.3.1",
|
|
83
|
+
"@rollup/plugin-commonjs": "22.0.0",
|
|
84
|
+
"@rollup/plugin-node-resolve": "13.3.0",
|
|
85
|
+
"@rollup/plugin-replace": "4.0.0",
|
|
86
|
+
"@types/autosize": "4.0.1",
|
|
87
|
+
"@types/marked": "4.0.3",
|
|
88
|
+
"@types/node": "17.0.43",
|
|
89
|
+
"@vitejs/plugin-vue": "2.3.3",
|
|
90
|
+
"rimraf": "3.0.2",
|
|
91
|
+
"rollup": "2.75.6",
|
|
92
|
+
"rollup-plugin-dts": "4.2.2",
|
|
93
|
+
"rollup-plugin-terser": "7.0.2",
|
|
94
|
+
"rollup-plugin-ts": "3.0.2",
|
|
95
|
+
"typescript": "4.7.3",
|
|
96
|
+
"vite": "2.9.12"
|
|
108
97
|
},
|
|
109
98
|
"engines": {
|
|
110
99
|
"node": ">=14"
|
|
100
|
+
},
|
|
101
|
+
"scripts": {
|
|
102
|
+
"build": "pnpm rollup && pnpm style",
|
|
103
|
+
"clean": "rimraf ./dist",
|
|
104
|
+
"dev": "vite -c config/vite.config.js",
|
|
105
|
+
"lint": "eslint --ext .ts,.vue .",
|
|
106
|
+
"prepublish": "pnpm clean && pnpm build",
|
|
107
|
+
"rollup": "rollup -c ./config/rollup.config.js",
|
|
108
|
+
"style": "sass ./src/styles/index.scss ./dist/waline.css --style=compressed"
|
|
111
109
|
}
|
|
112
|
-
}
|
|
110
|
+
}
|
package/src/comment.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { useUserInfo } from './composables';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
decodePath,
|
|
4
|
+
errorHandler,
|
|
5
|
+
fetchCommentCount,
|
|
6
|
+
getServerURL,
|
|
7
|
+
} from './utils';
|
|
3
8
|
import type { WalineAbort } from './typings';
|
|
4
9
|
|
|
5
10
|
export interface WalineCommentCountOptions {
|
|
@@ -54,7 +59,7 @@ WalineCommentCountOptions): WalineAbort => {
|
|
|
54
59
|
|
|
55
60
|
if (elements.length)
|
|
56
61
|
void fetchCommentCount({
|
|
57
|
-
serverURL,
|
|
62
|
+
serverURL: getServerURL(serverURL),
|
|
58
63
|
paths: Array.from(elements).map((element) =>
|
|
59
64
|
decodePath(element.dataset.path || element.getAttribute('id') || path)
|
|
60
65
|
),
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
<input
|
|
36
36
|
:ref="
|
|
37
37
|
(element) => {
|
|
38
|
-
if (element) inputRefs[kind] = element;
|
|
38
|
+
if (element) inputRefs[kind] = element as HTMLInputElement;
|
|
39
39
|
}
|
|
40
40
|
"
|
|
41
41
|
:id="`wl-${kind}`"
|
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
</button>
|
|
90
90
|
|
|
91
91
|
<button
|
|
92
|
+
v-if="config.search"
|
|
92
93
|
ref="gifButtonRef"
|
|
93
94
|
class="wl-action"
|
|
94
95
|
:class="{ actived: showGif }"
|
|
@@ -174,30 +175,14 @@
|
|
|
174
175
|
@input="onGifSearch"
|
|
175
176
|
/>
|
|
176
177
|
|
|
177
|
-
<
|
|
178
|
-
class="wl-gif-waterfall"
|
|
178
|
+
<ImageWall
|
|
179
179
|
:items="gifData.list"
|
|
180
|
-
:ssr-columns="2"
|
|
181
180
|
:column-width="200"
|
|
182
181
|
:gap="6"
|
|
183
|
-
@
|
|
182
|
+
@insert="insert($event)"
|
|
183
|
+
@scroll="onImageWallScroll"
|
|
184
184
|
>
|
|
185
|
-
|
|
186
|
-
<img
|
|
187
|
-
@click="insert(``)"
|
|
188
|
-
:src="item.media[0].tinygif.url"
|
|
189
|
-
:title="item.title"
|
|
190
|
-
loading="lazy"
|
|
191
|
-
:style="{
|
|
192
|
-
width: '200px',
|
|
193
|
-
height:
|
|
194
|
-
(200 * item.media[0].tinygif.dims[1]) /
|
|
195
|
-
item.media[0].tinygif.dims[0] +
|
|
196
|
-
'px',
|
|
197
|
-
}"
|
|
198
|
-
/>
|
|
199
|
-
</template>
|
|
200
|
-
</masonry-wall>
|
|
185
|
+
</ImageWall>
|
|
201
186
|
|
|
202
187
|
<div v-if="gifData.loading" class="wl-loading">
|
|
203
188
|
<LoadingIcon :size="30" />
|
|
@@ -261,6 +246,7 @@
|
|
|
261
246
|
</template>
|
|
262
247
|
|
|
263
248
|
<script lang="ts">
|
|
249
|
+
import { useDebounceFn } from '@vueuse/core';
|
|
264
250
|
import autosize from 'autosize';
|
|
265
251
|
import {
|
|
266
252
|
computed,
|
|
@@ -268,6 +254,7 @@ import {
|
|
|
268
254
|
inject,
|
|
269
255
|
onMounted,
|
|
270
256
|
onUnmounted,
|
|
257
|
+
reactive,
|
|
271
258
|
ref,
|
|
272
259
|
watch,
|
|
273
260
|
} from 'vue';
|
|
@@ -281,21 +268,24 @@ import {
|
|
|
281
268
|
LoadingIcon,
|
|
282
269
|
GifIcon,
|
|
283
270
|
} from './Icons';
|
|
271
|
+
import ImageWall from './ImageWall.vue';
|
|
284
272
|
import { useEditor, useUserMeta, useUserInfo } from '../composables';
|
|
285
273
|
import {
|
|
274
|
+
getEmojis,
|
|
286
275
|
getImagefromDataTransfer,
|
|
287
|
-
parseMarkdown,
|
|
288
276
|
getWordNumber,
|
|
289
277
|
parseEmoji,
|
|
278
|
+
parseMarkdown,
|
|
290
279
|
postComment,
|
|
291
|
-
getEmojis,
|
|
292
|
-
fetchGif,
|
|
293
|
-
FetchGifResponse,
|
|
294
|
-
throttle,
|
|
295
280
|
} from '../utils';
|
|
296
281
|
|
|
297
282
|
import type { ComputedRef, DeepReadonly } from 'vue';
|
|
298
|
-
import type {
|
|
283
|
+
import type {
|
|
284
|
+
WalineCommentData,
|
|
285
|
+
WalineImageUploader,
|
|
286
|
+
WalineSearchOptions,
|
|
287
|
+
WalineSearchResult,
|
|
288
|
+
} from '../typings';
|
|
299
289
|
import type { WalineConfig, WalineEmojiConfig } from '../utils';
|
|
300
290
|
|
|
301
291
|
export default defineComponent({
|
|
@@ -305,6 +295,7 @@ export default defineComponent({
|
|
|
305
295
|
CloseIcon,
|
|
306
296
|
EmojiIcon,
|
|
307
297
|
ImageIcon,
|
|
298
|
+
ImageWall,
|
|
308
299
|
MarkdownIcon,
|
|
309
300
|
PreviewIcon,
|
|
310
301
|
LoadingIcon,
|
|
@@ -353,11 +344,11 @@ export default defineComponent({
|
|
|
353
344
|
const showPreview = ref(false);
|
|
354
345
|
const previewText = ref('');
|
|
355
346
|
const wordNumber = ref(0);
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
loading:
|
|
359
|
-
list:
|
|
360
|
-
}
|
|
347
|
+
|
|
348
|
+
const searchResults = reactive({
|
|
349
|
+
loading: true,
|
|
350
|
+
list: [] as WalineSearchResult[],
|
|
351
|
+
});
|
|
361
352
|
|
|
362
353
|
const wordLimit = ref(0);
|
|
363
354
|
const isWordNumberLegal = ref(false);
|
|
@@ -621,35 +612,35 @@ export default defineComponent({
|
|
|
621
612
|
showGif.value = false;
|
|
622
613
|
};
|
|
623
614
|
|
|
624
|
-
const
|
|
625
|
-
gifData.value.cursor = '';
|
|
626
|
-
gifData.value.list = [];
|
|
627
|
-
onGifMasonryScroll(event);
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
const onGifMasonryScroll = async (event: Event): Promise<void> => {
|
|
615
|
+
const onImageWallScroll = async (event: Event): Promise<void> => {
|
|
631
616
|
const { scrollTop, clientHeight, scrollHeight } =
|
|
632
617
|
event.target as HTMLDivElement;
|
|
633
618
|
const percent = (clientHeight + scrollTop) / scrollHeight;
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
619
|
+
const searchOptions = config.value.search as WalineSearchOptions;
|
|
620
|
+
const keyword = gifSearchInputRef.value?.value || '';
|
|
621
|
+
|
|
622
|
+
if (percent < 0.9 || searchResults.loading) return;
|
|
623
|
+
|
|
624
|
+
searchResults.loading = true;
|
|
625
|
+
|
|
626
|
+
searchResults.list.push(
|
|
627
|
+
...(searchOptions.more
|
|
628
|
+
? await searchOptions.more(keyword, searchResults.list.length)
|
|
629
|
+
: await searchOptions.search(keyword))
|
|
630
|
+
);
|
|
637
631
|
|
|
638
|
-
|
|
639
|
-
const data = await fetchGif({
|
|
640
|
-
keyword: gifSearchInputRef.value?.value || '',
|
|
641
|
-
pos: gifData.value.cursor,
|
|
642
|
-
}).finally(() => {
|
|
643
|
-
gifData.value.loading = false;
|
|
644
|
-
});
|
|
632
|
+
searchResults.loading = false;
|
|
645
633
|
|
|
646
|
-
gifData.value.cursor = data.next;
|
|
647
|
-
gifData.value.list = gifData.value.list.concat(data.results);
|
|
648
634
|
setTimeout(() => {
|
|
649
635
|
(event.target as HTMLDivElement).scrollTop = scrollTop;
|
|
650
636
|
}, 50);
|
|
651
637
|
};
|
|
652
638
|
|
|
639
|
+
const onGifSearch = useDebounceFn((event: Event) => {
|
|
640
|
+
searchResults.list = [];
|
|
641
|
+
onImageWallScroll(event);
|
|
642
|
+
}, 300);
|
|
643
|
+
|
|
653
644
|
// update wordNumber
|
|
654
645
|
watch(
|
|
655
646
|
[config, wordNumber],
|
|
@@ -675,18 +666,21 @@ export default defineComponent({
|
|
|
675
666
|
{ immediate: true }
|
|
676
667
|
);
|
|
677
668
|
|
|
678
|
-
watch(
|
|
679
|
-
if (!showGif)
|
|
680
|
-
|
|
681
|
-
|
|
669
|
+
watch(showGif, async (showGif) => {
|
|
670
|
+
if (!showGif) return;
|
|
671
|
+
|
|
672
|
+
const searchOptions = config.value.search as WalineSearchOptions;
|
|
673
|
+
|
|
674
|
+
// clear input
|
|
675
|
+
if (gifSearchInputRef.value) gifSearchInputRef.value.value = '';
|
|
676
|
+
|
|
677
|
+
searchResults.loading = true;
|
|
682
678
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
});
|
|
679
|
+
searchResults.list = searchOptions.default
|
|
680
|
+
? await searchOptions.default()
|
|
681
|
+
: await searchOptions.search('');
|
|
687
682
|
|
|
688
|
-
|
|
689
|
-
gifData.value.list = gifData.value.list.concat(data.results);
|
|
683
|
+
searchResults.loading = false;
|
|
690
684
|
});
|
|
691
685
|
|
|
692
686
|
onMounted(() => {
|
|
@@ -746,7 +740,7 @@ export default defineComponent({
|
|
|
746
740
|
onLogout,
|
|
747
741
|
onProfile,
|
|
748
742
|
submitComment,
|
|
749
|
-
|
|
743
|
+
onImageWallScroll,
|
|
750
744
|
onGifSearch,
|
|
751
745
|
|
|
752
746
|
isLogin,
|
|
@@ -768,6 +762,7 @@ export default defineComponent({
|
|
|
768
762
|
showEmoji,
|
|
769
763
|
|
|
770
764
|
// gif
|
|
765
|
+
gifData: searchResults,
|
|
771
766
|
showGif,
|
|
772
767
|
|
|
773
768
|
// image
|
|
@@ -786,7 +781,6 @@ export default defineComponent({
|
|
|
786
781
|
gifPopupRef,
|
|
787
782
|
imageUploadRef,
|
|
788
783
|
gifSearchInputRef,
|
|
789
|
-
gifData,
|
|
790
784
|
};
|
|
791
785
|
},
|
|
792
786
|
});
|
|
@@ -25,7 +25,7 @@
|
|
|
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 && comment.level >= 0"
|
|
28
|
+
v-if="comment.level !== undefined && comment.level >= 0"
|
|
29
29
|
:class="`wl-badge level${comment.level}`"
|
|
30
30
|
v-text="locale[`level${comment.level}`] || `Level ${comment.level}`"
|
|
31
31
|
/>
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<!-- forked from https://github.com/DerYeger/vue-masonry-wall/blob/master/src/masonry-wall.vue -->
|
|
2
|
+
|
|
3
|
+
<!-- MIT License
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2021 Fuxing Loh, Jan Müller
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE. -->
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<div ref="wall" class="wl-gallery" :style="{ gap: `${gap}px` }">
|
|
27
|
+
<div
|
|
28
|
+
v-for="(column, columnIndex) in columns"
|
|
29
|
+
:key="columnIndex"
|
|
30
|
+
class="wl-gallery-column"
|
|
31
|
+
:data-index="columnIndex"
|
|
32
|
+
:style="{ gap: `${gap}px` }"
|
|
33
|
+
>
|
|
34
|
+
<template v-for="itemIndex in column" :key="itemIndex">
|
|
35
|
+
<LoadingIcon
|
|
36
|
+
v-if="!state[items[itemIndex].src]"
|
|
37
|
+
:size="36"
|
|
38
|
+
style="margin: 20px auto"
|
|
39
|
+
/>
|
|
40
|
+
<img
|
|
41
|
+
class="wl-gallery-item"
|
|
42
|
+
:src="items[itemIndex].src"
|
|
43
|
+
:title="items[itemIndex].title"
|
|
44
|
+
loading="lazy"
|
|
45
|
+
@load="state[items[itemIndex].src] = true"
|
|
46
|
+
@click="$emit('insert', ``)"
|
|
47
|
+
/>
|
|
48
|
+
</template>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script lang="ts">
|
|
54
|
+
import {
|
|
55
|
+
defineComponent,
|
|
56
|
+
nextTick,
|
|
57
|
+
onBeforeUnmount,
|
|
58
|
+
onMounted,
|
|
59
|
+
ref,
|
|
60
|
+
watch,
|
|
61
|
+
} from 'vue';
|
|
62
|
+
|
|
63
|
+
import { LoadingIcon } from './Icons';
|
|
64
|
+
|
|
65
|
+
import type { PropType } from 'vue';
|
|
66
|
+
import type { WalineSearchResult } from '../typings';
|
|
67
|
+
|
|
68
|
+
type Column = number[];
|
|
69
|
+
|
|
70
|
+
export default defineComponent({
|
|
71
|
+
name: 'ImageWall',
|
|
72
|
+
|
|
73
|
+
components: {
|
|
74
|
+
LoadingIcon,
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
props: {
|
|
78
|
+
items: { type: Array as PropType<WalineSearchResult[]>, default: () => [] },
|
|
79
|
+
columnWidth: { type: Number, default: 300 },
|
|
80
|
+
gap: { type: Number, default: 0 },
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
emit: ['insert'],
|
|
84
|
+
|
|
85
|
+
setup(props) {
|
|
86
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
87
|
+
const wall = ref<HTMLDivElement | null>(null);
|
|
88
|
+
const state = ref<Record<string, boolean>>({});
|
|
89
|
+
const columns = ref<Column[]>([]);
|
|
90
|
+
|
|
91
|
+
const getColumnCount = (): number => {
|
|
92
|
+
const count = Math.floor(
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
94
|
+
(wall.value!.getBoundingClientRect().width + props.gap) /
|
|
95
|
+
(props.columnWidth + props.gap)
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return count > 0 ? count : 1;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const createColumns = (count: number): Column[] =>
|
|
102
|
+
new Array(count).fill(null).map(() => []);
|
|
103
|
+
|
|
104
|
+
const fillColumns = async (itemIndex: number): Promise<void> => {
|
|
105
|
+
if (itemIndex >= props.items.length) return;
|
|
106
|
+
|
|
107
|
+
await nextTick();
|
|
108
|
+
|
|
109
|
+
const columnDivs = Array.from(
|
|
110
|
+
wall.value?.children || []
|
|
111
|
+
) as HTMLDivElement[];
|
|
112
|
+
|
|
113
|
+
const target = columnDivs.reduce((prev, curr) =>
|
|
114
|
+
curr.getBoundingClientRect().height <
|
|
115
|
+
prev.getBoundingClientRect().height
|
|
116
|
+
? curr
|
|
117
|
+
: prev
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
columns.value[Number(target.dataset.index)].push(itemIndex);
|
|
121
|
+
|
|
122
|
+
await fillColumns(itemIndex + 1);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const redraw = async (force = false): Promise<void> => {
|
|
126
|
+
if (columns.value.length === getColumnCount() && !force) return;
|
|
127
|
+
|
|
128
|
+
columns.value = createColumns(getColumnCount());
|
|
129
|
+
|
|
130
|
+
const scrollY = window.scrollY;
|
|
131
|
+
|
|
132
|
+
await fillColumns(0);
|
|
133
|
+
|
|
134
|
+
window.scrollTo({ top: scrollY });
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
watch(
|
|
138
|
+
() => [props.items],
|
|
139
|
+
() => {
|
|
140
|
+
state.value = {};
|
|
141
|
+
redraw(true);
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
watch(
|
|
145
|
+
() => [props.columnWidth, props.gap],
|
|
146
|
+
() => redraw()
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
onMounted(() => {
|
|
150
|
+
redraw(true);
|
|
151
|
+
resizeObserver = new ResizeObserver(() => redraw());
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
153
|
+
resizeObserver.observe(wall.value!);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
157
|
+
onBeforeUnmount(() => resizeObserver!.unobserve(wall.value!));
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
columns,
|
|
161
|
+
state,
|
|
162
|
+
wall,
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
</script>
|
package/src/composables/like.ts
CHANGED
package/src/config/default.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WalineMeta } from '../typings';
|
|
1
|
+
import type { WalineMeta, WalineSearchOptions } from '../typings';
|
|
2
2
|
|
|
3
3
|
const availableMeta: WalineMeta[] = ['nick', 'mail', 'link'];
|
|
4
4
|
|
|
@@ -19,3 +19,93 @@ export const defaultTexRenderer = (blockMode: boolean): string =>
|
|
|
19
19
|
blockMode === true
|
|
20
20
|
? '<p class="wl-tex">Tex is not available in preview</p>'
|
|
21
21
|
: '<span class="wl-tex">Tex is not available in preview</span>';
|
|
22
|
+
|
|
23
|
+
export const getDefaultSearchOptions = (): WalineSearchOptions => {
|
|
24
|
+
interface FetchGifRequest {
|
|
25
|
+
keyword: string;
|
|
26
|
+
pos?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type GifFormat =
|
|
30
|
+
| 'gif'
|
|
31
|
+
| 'mediumgif'
|
|
32
|
+
| 'tinygif'
|
|
33
|
+
| 'nanogif'
|
|
34
|
+
| 'mp4'
|
|
35
|
+
| 'loopedmp4'
|
|
36
|
+
| 'tinymp4'
|
|
37
|
+
| 'nanomp4'
|
|
38
|
+
| 'webm'
|
|
39
|
+
| 'tinywebm'
|
|
40
|
+
| 'nanowebm';
|
|
41
|
+
|
|
42
|
+
interface MediaObject {
|
|
43
|
+
preview: string;
|
|
44
|
+
url: string;
|
|
45
|
+
dims: number[];
|
|
46
|
+
size: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface GifObject {
|
|
50
|
+
created: number;
|
|
51
|
+
hasaudio: boolean;
|
|
52
|
+
id: string;
|
|
53
|
+
media: Record<GifFormat, MediaObject>[];
|
|
54
|
+
tags: string[];
|
|
55
|
+
title: string;
|
|
56
|
+
itemurl: string;
|
|
57
|
+
hascaption: boolean;
|
|
58
|
+
url: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface FetchGifResponse {
|
|
62
|
+
next: string;
|
|
63
|
+
results: GifObject[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const state = {
|
|
67
|
+
next: '',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const fetchGif = ({
|
|
71
|
+
keyword,
|
|
72
|
+
pos,
|
|
73
|
+
}: FetchGifRequest): Promise<FetchGifResponse> => {
|
|
74
|
+
const baseUrl = `https://g.tenor.com/v1/search`;
|
|
75
|
+
const query = new URLSearchParams('media_filter=minimal');
|
|
76
|
+
query.set('key', 'PAY5JLFIH6V6');
|
|
77
|
+
query.set('limit', '20');
|
|
78
|
+
query.set('pos', pos || '');
|
|
79
|
+
query.set('q', keyword);
|
|
80
|
+
|
|
81
|
+
return fetch(`${baseUrl}?${query.toString()}`, {
|
|
82
|
+
headers: {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
.then((resp) => resp.json() as Promise<FetchGifResponse>)
|
|
88
|
+
.catch(() => ({ next: pos || '', results: [] }));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
search: (word = '') =>
|
|
93
|
+
fetchGif({ keyword: word }).then((resp) => {
|
|
94
|
+
state.next = resp.next;
|
|
95
|
+
|
|
96
|
+
return resp.results.map((item) => ({
|
|
97
|
+
title: item.title,
|
|
98
|
+
src: item.media[0].tinygif.url,
|
|
99
|
+
}));
|
|
100
|
+
}),
|
|
101
|
+
more: (word) =>
|
|
102
|
+
fetchGif({ keyword: word, pos: state.next }).then((resp) => {
|
|
103
|
+
state.next = resp.next;
|
|
104
|
+
|
|
105
|
+
return resp.results.map((item) => ({
|
|
106
|
+
title: item.title,
|
|
107
|
+
src: item.media[0].tinygif.url,
|
|
108
|
+
}));
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
111
|
+
};
|
package/src/init.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createApp, h, reactive, watchEffect } from 'vue';
|
|
2
|
-
import MasonryWall from '@yeger/vue-masonry-wall';
|
|
3
2
|
|
|
4
3
|
import Waline from './components/Waline.vue';
|
|
5
4
|
import { commentCount } from './comment';
|
|
@@ -81,15 +80,8 @@ export const init = ({
|
|
|
81
80
|
? createApp(() => h(Waline, { path: state.path, ...props }))
|
|
82
81
|
: null;
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
app.use(MasonryWall);
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
88
|
-
app.mount(root!);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
updateCommentCount();
|
|
92
|
-
updatePageviewCount();
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
84
|
+
if (app) app.mount(root!);
|
|
93
85
|
|
|
94
86
|
const stopComment = watchEffect(updateCommentCount);
|
|
95
87
|
const stopPageview = watchEffect(updatePageviewCount);
|
package/src/pageview.ts
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
errorHandler,
|
|
3
3
|
fetchPageviews,
|
|
4
4
|
getQuery,
|
|
5
|
+
getServerURL,
|
|
5
6
|
updatePageviews,
|
|
6
7
|
} from './utils';
|
|
7
8
|
|
|
@@ -83,7 +84,7 @@ export const pageviewCount = ({
|
|
|
83
84
|
|
|
84
85
|
const fetch = (elements: HTMLElement[]): Promise<void> =>
|
|
85
86
|
fetchPageviews({
|
|
86
|
-
serverURL,
|
|
87
|
+
serverURL: getServerURL(serverURL),
|
|
87
88
|
paths: elements.map((element) => getQuery(element) || path),
|
|
88
89
|
lang,
|
|
89
90
|
signal: controller.signal,
|
|
@@ -96,7 +97,11 @@ export const pageviewCount = ({
|
|
|
96
97
|
const normalElements = elements.filter((element) => !filter(element));
|
|
97
98
|
const elementsNeedstoBeFetched = elements.filter(filter);
|
|
98
99
|
|
|
99
|
-
void updatePageviews({
|
|
100
|
+
void updatePageviews({
|
|
101
|
+
serverURL: getServerURL(serverURL),
|
|
102
|
+
path,
|
|
103
|
+
lang,
|
|
104
|
+
}).then((count) =>
|
|
100
105
|
renderVisitorCount(
|
|
101
106
|
new Array<number>(normalElements.length).fill(count),
|
|
102
107
|
normalElements
|