@waline/client 2.5.1 → 2.6.0

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 (44) hide show
  1. package/dist/component.esm.js +1 -1
  2. package/dist/component.esm.js.map +1 -1
  3. package/dist/component.js +1 -1
  4. package/dist/component.js.map +1 -1
  5. package/dist/legacy.d.ts +40 -0
  6. package/dist/legacy.js +1 -1
  7. package/dist/legacy.js.map +1 -1
  8. package/dist/pageview.cjs.js +1 -1
  9. package/dist/pageview.cjs.js.map +1 -1
  10. package/dist/pageview.esm.js +1 -1
  11. package/dist/pageview.esm.js.map +1 -1
  12. package/dist/pageview.js +1 -1
  13. package/dist/pageview.js.map +1 -1
  14. package/dist/shim.d.ts +41 -1
  15. package/dist/shim.esm.d.ts +41 -1
  16. package/dist/shim.esm.js +1 -1
  17. package/dist/shim.esm.js.map +1 -1
  18. package/dist/shim.js +1 -1
  19. package/dist/shim.js.map +1 -1
  20. package/dist/waline.cjs.d.ts +41 -1
  21. package/dist/waline.cjs.js +1 -1
  22. package/dist/waline.cjs.js.map +1 -1
  23. package/dist/waline.css +1 -1
  24. package/dist/waline.css.map +1 -1
  25. package/dist/waline.d.ts +41 -1
  26. package/dist/waline.esm.d.ts +41 -1
  27. package/dist/waline.esm.js +1 -1
  28. package/dist/waline.esm.js.map +1 -1
  29. package/dist/waline.js +1 -1
  30. package/dist/waline.js.map +1 -1
  31. package/package.json +20 -21
  32. package/src/comment.ts +7 -2
  33. package/src/components/CommentBox.vue +40 -34
  34. package/src/components/ImageWall.vue +32 -18
  35. package/src/config/default.ts +91 -1
  36. package/src/init.ts +0 -3
  37. package/src/pageview.ts +2 -1
  38. package/src/styles/emoji.scss +20 -0
  39. package/src/typings/base.ts +40 -0
  40. package/src/typings/waline.ts +8 -0
  41. package/src/utils/config.ts +5 -2
  42. package/src/utils/index.ts +0 -2
  43. package/src/utils/fetchGif.ts +0 -63
  44. package/src/utils/throttle.ts +0 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waline/client",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "client for waline comment system",
5
5
  "keywords": [
6
6
  "valine",
@@ -71,30 +71,29 @@
71
71
  ]
72
72
  },
73
73
  "dependencies": {
74
- "@vueuse/core": "^8.5.0",
74
+ "@vueuse/core": "^8.6.0",
75
75
  "autosize": "^5.0.1",
76
76
  "marked": "^4.0.16",
77
- "vue": "^3.2.35"
77
+ "vue": "^3.2.37"
78
78
  },
79
79
  "devDependencies": {
80
- "@babel/core": "^7.18.0",
81
- "@babel/preset-env": "^7.18.0",
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.35",
89
- "@vitejs/plugin-vue": "^2.3.3",
90
- "@vue/compiler-sfc": "^3.2.35",
91
- "rimraf": "^3.0.2",
92
- "rollup": "^2.74.1",
93
- "rollup-plugin-dts": "^4.2.1",
94
- "rollup-plugin-terser": "^7.0.2",
95
- "rollup-plugin-ts": "^2.0.7",
96
- "typescript": "^4.6.4",
97
- "vite": "^2.9.9"
80
+ "@babel/core": "7.18.2",
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.41",
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.10"
98
97
  },
99
98
  "engines": {
100
99
  "node": ">=14"
package/src/comment.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  import { useUserInfo } from './composables';
2
- import { decodePath, errorHandler, fetchCommentCount } from './utils';
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
  ),
@@ -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 }"
@@ -245,6 +246,7 @@
245
246
  </template>
246
247
 
247
248
  <script lang="ts">
249
+ import { useDebounceFn } from '@vueuse/core';
248
250
  import autosize from 'autosize';
249
251
  import {
250
252
  computed,
@@ -252,11 +254,11 @@ import {
252
254
  inject,
253
255
  onMounted,
254
256
  onUnmounted,
257
+ reactive,
255
258
  ref,
256
259
  watch,
257
260
  } from 'vue';
258
261
 
259
- import ImageWall from './ImageWall.vue';
260
262
  import {
261
263
  CloseIcon,
262
264
  EmojiIcon,
@@ -266,25 +268,25 @@ import {
266
268
  LoadingIcon,
267
269
  GifIcon,
268
270
  } from './Icons';
271
+ import ImageWall from './ImageWall.vue';
269
272
  import { useEditor, useUserMeta, useUserInfo } from '../composables';
270
273
  import {
271
- fetchGif,
272
274
  getEmojis,
273
275
  getImagefromDataTransfer,
274
276
  getWordNumber,
275
277
  parseEmoji,
276
278
  parseMarkdown,
277
279
  postComment,
278
- throttle,
279
280
  } from '../utils';
280
281
 
281
282
  import type { ComputedRef, DeepReadonly } from 'vue';
282
- import type { WalineCommentData, WalineImageUploader } from '../typings';
283
283
  import type {
284
- FetchGifResponse,
285
- WalineConfig,
286
- WalineEmojiConfig,
287
- } from '../utils';
284
+ WalineCommentData,
285
+ WalineImageUploader,
286
+ WalineSearchOptions,
287
+ WalineSearchResult,
288
+ } from '../typings';
289
+ import type { WalineConfig, WalineEmojiConfig } from '../utils';
288
290
 
289
291
  export default defineComponent({
290
292
  name: 'CommentBox',
@@ -342,11 +344,11 @@ export default defineComponent({
342
344
  const showPreview = ref(false);
343
345
  const previewText = ref('');
344
346
  const wordNumber = ref(0);
345
- const gifData = ref<{
346
- cursor: string;
347
- loading: boolean;
348
- list: FetchGifResponse['results'];
349
- }>({ cursor: '', loading: true, list: [] });
347
+
348
+ const searchResults = reactive({
349
+ loading: true,
350
+ list: [] as WalineSearchResult[],
351
+ });
350
352
 
351
353
  const wordLimit = ref(0);
352
354
  const isWordNumberLegal = ref(false);
@@ -614,31 +616,30 @@ export default defineComponent({
614
616
  const { scrollTop, clientHeight, scrollHeight } =
615
617
  event.target as HTMLDivElement;
616
618
  const percent = (clientHeight + scrollTop) / scrollHeight;
619
+ const searchOptions = config.value.search as WalineSearchOptions;
620
+ const keyword = gifSearchInputRef.value?.value || '';
617
621
 
618
- if (percent < 0.9 || gifData.value.loading) return;
622
+ if (percent < 0.9 || searchResults.loading) return;
619
623
 
620
- gifData.value.loading = true;
624
+ searchResults.loading = true;
621
625
 
622
- const data = await fetchGif({
623
- keyword: gifSearchInputRef.value?.value || '',
624
- pos: gifData.value.cursor,
625
- }).finally(() => {
626
- gifData.value.loading = false;
627
- });
626
+ searchResults.list.push(
627
+ ...(searchOptions.more
628
+ ? await searchOptions.more(keyword, searchResults.list.length)
629
+ : await searchOptions.search(keyword))
630
+ );
628
631
 
629
- gifData.value.cursor = data.next;
630
- gifData.value.list = gifData.value.list.concat(data.results);
632
+ searchResults.loading = false;
631
633
 
632
634
  setTimeout(() => {
633
635
  (event.target as HTMLDivElement).scrollTop = scrollTop;
634
636
  }, 50);
635
637
  };
636
638
 
637
- const onGifSearch = throttle(async (event: Event) => {
638
- gifData.value.cursor = '';
639
- gifData.value.list = [];
639
+ const onGifSearch = useDebounceFn((event: Event) => {
640
+ searchResults.list = [];
640
641
  onImageWallScroll(event);
641
- });
642
+ }, 300);
642
643
 
643
644
  // update wordNumber
644
645
  watch(
@@ -668,13 +669,18 @@ export default defineComponent({
668
669
  watch(showGif, async (showGif) => {
669
670
  if (!showGif) return;
670
671
 
671
- gifData.value.loading = true;
672
- const data = await fetchGif({ keyword: '' }).finally(() => {
673
- gifData.value.loading = false;
674
- });
672
+ const searchOptions = config.value.search as WalineSearchOptions;
673
+
674
+ // clear input
675
+ if (gifSearchInputRef.value) gifSearchInputRef.value.value = '';
676
+
677
+ searchResults.loading = true;
678
+
679
+ searchResults.list = searchOptions.default
680
+ ? await searchOptions.default()
681
+ : await searchOptions.search('');
675
682
 
676
- gifData.value.cursor = data.next;
677
- gifData.value.list = gifData.value.list.concat(data.results);
683
+ searchResults.loading = false;
678
684
  });
679
685
 
680
686
  onMounted(() => {
@@ -756,7 +762,7 @@ export default defineComponent({
756
762
  showEmoji,
757
763
 
758
764
  // gif
759
- gifData,
765
+ gifData: searchResults,
760
766
  showGif,
761
767
 
762
768
  // image
@@ -31,17 +31,21 @@ SOFTWARE. -->
31
31
  :data-index="columnIndex"
32
32
  :style="{ gap: `${gap}px` }"
33
33
  >
34
- <img
35
- v-for="itemIndex in column"
36
- :key="itemIndex"
37
- class="wl-gallery-item"
38
- :src="items[itemIndex].media[0].tinygif.url"
39
- :title="items[itemIndex].title"
40
- loading="lazy"
41
- @click="
42
- $emit('insert', `![](${items[itemIndex].media[0].tinygif.url})`)
43
- "
44
- />
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', `![](${items[itemIndex].src})`)"
47
+ />
48
+ </template>
45
49
  </div>
46
50
  </div>
47
51
  </template>
@@ -56,16 +60,24 @@ import {
56
60
  watch,
57
61
  } from 'vue';
58
62
 
63
+ import { LoadingIcon } from './Icons';
64
+
59
65
  import type { PropType } from 'vue';
66
+ import type { WalineSearchResult } from '../typings';
60
67
 
61
68
  type Column = number[];
62
69
 
63
70
  export default defineComponent({
71
+ name: 'ImageWall',
72
+
73
+ components: {
74
+ LoadingIcon,
75
+ },
76
+
64
77
  props: {
78
+ items: { type: Array as PropType<WalineSearchResult[]>, default: () => [] },
65
79
  columnWidth: { type: Number, default: 300 },
66
- items: { type: Array as PropType<unknown[]>, default: () => [] },
67
80
  gap: { type: Number, default: 0 },
68
- rtl: { type: Boolean, default: false },
69
81
  },
70
82
 
71
83
  emit: ['insert'],
@@ -73,7 +85,7 @@ export default defineComponent({
73
85
  setup(props) {
74
86
  let resizeObserver: ResizeObserver | null = null;
75
87
  const wall = ref<HTMLDivElement | null>(null);
76
-
88
+ const state = ref<Record<string, boolean>>({});
77
89
  const columns = ref<Column[]>([]);
78
90
 
79
91
  const getColumnCount = (): number => {
@@ -98,8 +110,6 @@ export default defineComponent({
98
110
  wall.value?.children || []
99
111
  ) as HTMLDivElement[];
100
112
 
101
- if (props.rtl) columnDivs.reverse();
102
-
103
113
  const target = columnDivs.reduce((prev, curr) =>
104
114
  curr.getBoundingClientRect().height <
105
115
  prev.getBoundingClientRect().height
@@ -125,8 +135,11 @@ export default defineComponent({
125
135
  };
126
136
 
127
137
  watch(
128
- () => [props.items, props.rtl],
129
- () => redraw(true)
138
+ () => [props.items],
139
+ () => {
140
+ state.value = {};
141
+ redraw(true);
142
+ }
130
143
  );
131
144
  watch(
132
145
  () => [props.columnWidth, props.gap],
@@ -145,6 +158,7 @@ export default defineComponent({
145
158
 
146
159
  return {
147
160
  columns,
161
+ state,
148
162
  wall,
149
163
  };
150
164
  },
@@ -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
@@ -83,9 +83,6 @@ export const init = ({
83
83
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
84
84
  if (app) app.mount(root!);
85
85
 
86
- updateCommentCount();
87
- updatePageviewCount();
88
-
89
86
  const stopComment = watchEffect(updateCommentCount);
90
87
  const stopPageview = watchEffect(updatePageviewCount);
91
88
 
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,
@@ -81,6 +81,8 @@
81
81
  position: relative;
82
82
  height: 2em;
83
83
  padding: 0 6px 1px;
84
+ overflow-x: scroll;
85
+ white-space: nowrap;
84
86
 
85
87
  &::before {
86
88
  content: ' ';
@@ -95,6 +97,24 @@
95
97
 
96
98
  background: var(--waline-border-color);
97
99
  }
100
+
101
+ &::-webkit-scrollbar {
102
+ width: 6px;
103
+ height: 6px;
104
+ }
105
+
106
+ &::-webkit-scrollbar-track-piece:horizontal {
107
+ -webkit-border-radius: 6px;
108
+ border-radius: 6px;
109
+ background: rgb(0 0 0 / 10%);
110
+ }
111
+
112
+ &::-webkit-scrollbar-thumb:horizontal {
113
+ height: 6px;
114
+ -webkit-border-radius: 6px;
115
+ border-radius: 6px;
116
+ background: var(--waline-theme-color);
117
+ }
98
118
  }
99
119
 
100
120
  .wl-tab {
@@ -39,6 +39,46 @@ export interface WalineEmojiInfo {
39
39
 
40
40
  export type WalineEmojiMaps = Record<string, string>;
41
41
 
42
+ export interface WalineSearchResult extends Record<string, unknown> {
43
+ /**
44
+ * Image link
45
+ */
46
+ src: string;
47
+
48
+ /**
49
+ * Image title, optional
50
+ */
51
+ title?: string;
52
+
53
+ /**
54
+ * Image preview link, optional
55
+ *
56
+ * @default src
57
+ */
58
+ preview?: string;
59
+ }
60
+
61
+ export interface WalineSearchOptions {
62
+ /**
63
+ * Search action
64
+ */
65
+ search: (word: string) => Promise<WalineSearchResult[]>;
66
+
67
+ /**
68
+ * Default search action
69
+ *
70
+ * @default () => search('')
71
+ */
72
+ default?: () => Promise<WalineSearchResult[]>;
73
+
74
+ /**
75
+ * Fetch more action
76
+ *
77
+ * @default (word) => search(word)
78
+ */
79
+ more?: (word: string, currectCount: number) => Promise<WalineSearchResult[]>;
80
+ }
81
+
42
82
  export type WalineMeta = 'nick' | 'mail' | 'link';
43
83
 
44
84
  export type WalineImageUploader = (image: File) => Promise<string>;
@@ -4,6 +4,7 @@ import type {
4
4
  WalineImageUploader,
5
5
  WalineMeta,
6
6
  WalineTexRenderer,
7
+ WalineSearchOptions,
7
8
  } from './base';
8
9
  import type { WalineLocale } from './locale';
9
10
 
@@ -148,6 +149,13 @@ export interface WalineProps {
148
149
  */
149
150
  emoji?: (string | WalineEmojiInfo)[] | false;
150
151
 
152
+ /**
153
+ * 设置搜索功能
154
+ *
155
+ * Customize Search feature
156
+ */
157
+ search?: WalineSearchOptions | false;
158
+
151
159
  /**
152
160
  * 代码高亮
153
161
  *
@@ -1,10 +1,11 @@
1
1
  import {
2
2
  defaultLang,
3
+ defaultLocales,
3
4
  defaultUploadImage,
4
5
  defaultHighlighter,
5
6
  defaultTexRenderer,
7
+ getDefaultSearchOptions,
6
8
  getMeta,
7
- defaultLocales,
8
9
  } from '../config';
9
10
 
10
11
  import { decodePath, isLinkHttp, removeEndingSplash } from './path';
@@ -27,7 +28,7 @@ export interface WalineConfig extends Required<Omit<WalineProps, 'wordLimit'>> {
27
28
  // emoji: Promise<EmojiConfig>;
28
29
  }
29
30
 
30
- const getServerURL = (serverURL: string): string => {
31
+ export const getServerURL = (serverURL: string): string => {
31
32
  const result = removeEndingSplash(serverURL);
32
33
 
33
34
  return isLinkHttp(result) ? result : `https://${result}`;
@@ -61,6 +62,7 @@ export const getConfig = ({
61
62
  texRenderer,
62
63
  copyright = true,
63
64
  login = 'enable',
65
+ search = getDefaultSearchOptions(),
64
66
  ...more
65
67
  }: WalineProps): WalineConfig => ({
66
68
  serverURL: getServerURL(serverURL),
@@ -81,5 +83,6 @@ export const getConfig = ({
81
83
  pageSize,
82
84
  login,
83
85
  copyright,
86
+ search,
84
87
  ...more,
85
88
  });
@@ -10,5 +10,3 @@ export * from './markdown';
10
10
  export * from './path';
11
11
  export * from './query';
12
12
  export * from './wordCount';
13
- export * from './fetchGif';
14
- export * from './throttle';
@@ -1,63 +0,0 @@
1
- export type GifFormat =
2
- | 'gif'
3
- | 'mediumgif'
4
- | 'tinygif'
5
- | 'nanogif'
6
- | 'mp4'
7
- | 'loopedmp4'
8
- | 'tinymp4'
9
- | 'nanomp4'
10
- | 'webm'
11
- | 'tinywebm'
12
- | 'nanowebm';
13
- export interface MediaObject {
14
- preview: string;
15
- url: string;
16
- dims: number[];
17
- size: number;
18
- }
19
-
20
- export interface FetchGifRequest {
21
- key?: string;
22
- keyword: string;
23
- pos?: string;
24
- limit?: number;
25
- }
26
-
27
- export interface GifObject {
28
- created: number;
29
- hasaudio: boolean;
30
- id: string;
31
- media: Record<GifFormat, MediaObject>[];
32
- tags: string[];
33
- title: string;
34
- itemurl: string;
35
- hascaption: boolean;
36
- url: string;
37
- }
38
-
39
- export interface FetchGifResponse {
40
- next: string;
41
- results: GifObject[];
42
- }
43
-
44
- export const fetchGif = ({
45
- key,
46
- keyword,
47
- pos,
48
- limit,
49
- }: FetchGifRequest): Promise<FetchGifResponse> => {
50
- const baseUrl = `https://g.tenor.com/v1/search`;
51
- const query = new URLSearchParams('media_filter=minimal');
52
- query.set('key', key || 'PAY5JLFIH6V6');
53
- query.set('limit', (limit || 20).toString());
54
- query.set('pos', pos || '');
55
- query.set('q', keyword);
56
-
57
- return fetch(`${baseUrl}?${query.toString()}`, {
58
- headers: {
59
- // eslint-disable-next-line @typescript-eslint/naming-convention
60
- 'Content-Type': 'application/json',
61
- },
62
- }).then((resp) => resp.json() as Promise<FetchGifResponse>);
63
- };
@@ -1,15 +0,0 @@
1
- export const throttle = (
2
- func: (...args: any[]) => void,
3
- timeout = 300
4
- ): ((...args: []) => void) => {
5
- let timer: number | null;
6
-
7
- return (...args: []): void => {
8
- if (timer) clearTimeout(timer);
9
-
10
- timer = window.setTimeout(() => {
11
- func(...args);
12
- timer = null;
13
- }, timeout);
14
- };
15
- };