|
1 | | -import type { UserResponse } from 'stream-chat'; |
2 | | - |
3 | | -export const accentsMap: { [key: string]: string } = { |
4 | | - a: 'á|à|ã|â|À|Á|Ã|Â', |
5 | | - c: 'ç|Ç', |
6 | | - e: 'é|è|ê|É|È|Ê', |
7 | | - i: 'í|ì|î|Í|Ì|Î', |
8 | | - n: 'ñ|Ñ', |
9 | | - o: 'ó|ò|ô|ő|õ|Ó|Ò|Ô|Õ', |
10 | | - u: 'ú|ù|û|ü|Ú|Ù|Û|Ü', |
11 | | -}; |
12 | | - |
13 | | -export const removeDiacritics = (text?: string) => { |
14 | | - if (!text) return ''; |
15 | | - return Object.keys(accentsMap).reduce( |
16 | | - (acc, current) => acc.replace(new RegExp(accentsMap[current], 'g'), current), |
17 | | - text, |
18 | | - ); |
19 | | -}; |
20 | | - |
21 | | -export const calculateLevenshtein = (query: string, name: string) => { |
22 | | - if (query.length === 0) return name.length; |
23 | | - if (name.length === 0) return query.length; |
24 | | - |
25 | | - const matrix = []; |
26 | | - |
27 | | - let i; |
28 | | - for (i = 0; i <= name.length; i++) { |
29 | | - matrix[i] = [i]; |
30 | | - } |
31 | | - |
32 | | - let j; |
33 | | - for (j = 0; j <= query.length; j++) { |
34 | | - matrix[0][j] = j; |
35 | | - } |
36 | | - |
37 | | - for (i = 1; i <= name.length; i++) { |
38 | | - for (j = 1; j <= query.length; j++) { |
39 | | - if (name.charAt(i - 1) === query.charAt(j - 1)) { |
40 | | - matrix[i][j] = matrix[i - 1][j - 1]; |
41 | | - } else { |
42 | | - matrix[i][j] = Math.min( |
43 | | - matrix[i - 1][j - 1] + 1, // substitution |
44 | | - Math.min( |
45 | | - matrix[i][j - 1] + 1, // insertion |
46 | | - matrix[i - 1][j] + 1, |
47 | | - ), |
48 | | - ); // deletion |
49 | | - } |
50 | | - } |
51 | | - } |
52 | | - |
53 | | - return matrix[name.length][query.length]; |
54 | | -}; |
55 | | - |
56 | | -export type SearchLocalUserParams = { |
57 | | - ownUserId: string | undefined; |
58 | | - query: string; |
59 | | - text: string; |
60 | | - users: UserResponse[]; |
61 | | - useMentionsTransliteration?: boolean; |
62 | | -}; |
63 | | - |
64 | | -export const searchLocalUsers = (params: SearchLocalUserParams): UserResponse[] => { |
65 | | - const { ownUserId, query, text, useMentionsTransliteration, users } = params; |
66 | | - |
67 | | - const matchingUsers = users.filter((user) => { |
68 | | - if (user.id === ownUserId) return false; |
69 | | - if (!query) return true; |
70 | | - |
71 | | - let updatedId = removeDiacritics(user.id).toLowerCase(); |
72 | | - let updatedName = removeDiacritics(user.name).toLowerCase(); |
73 | | - let updatedQuery = removeDiacritics(query).toLowerCase(); |
74 | | - |
75 | | - if (useMentionsTransliteration) { |
76 | | - (async () => { |
77 | | - // eslint-disable-next-line import/no-extraneous-dependencies |
78 | | - const { default: transliterate } = await import('@stream-io/transliterate'); |
79 | | - updatedName = transliterate(user.name || '').toLowerCase(); |
80 | | - updatedQuery = transliterate(query).toLowerCase(); |
81 | | - updatedId = transliterate(user.id).toLowerCase(); |
82 | | - })(); |
83 | | - } |
84 | | - |
85 | | - const maxDistance = 3; |
86 | | - const lastDigits = text.slice(-(maxDistance + 1)).includes('@'); |
87 | | - |
88 | | - if (updatedName) { |
89 | | - const levenshtein = calculateLevenshtein(updatedQuery, updatedName); |
90 | | - if ( |
91 | | - updatedName.includes(updatedQuery) || |
92 | | - (levenshtein <= maxDistance && lastDigits) |
93 | | - ) { |
94 | | - return true; |
95 | | - } |
96 | | - } |
97 | | - |
98 | | - const levenshtein = calculateLevenshtein(updatedQuery, updatedId); |
99 | | - |
100 | | - return updatedId.includes(updatedQuery) || (levenshtein <= maxDistance && lastDigits); |
101 | | - }); |
102 | | - |
103 | | - return matchingUsers; |
104 | | -}; |
105 | | - |
106 | 1 | export function prettifyFileSize(bytes: number, precision = 3) { |
107 | 2 | const units = ['B', 'kB', 'MB', 'GB']; |
108 | 3 | const exponent = Math.min( |
|
0 commit comments