1import type { Dispatch, SetStateAction } from 'react'; 2 3import type { AlgoliaItemHierarchy, AlgoliaItemType } from './types'; 4 5export const getItemsAsync = async <T>( 6 query: string, 7 fetcher: (query: string, version?: string) => Promise<Response>, 8 setter: Dispatch<SetStateAction<T[]>>, 9 version?: string 10) => { 11 const { hits, libraries } = await fetcher(query, version).then(response => response.json()); 12 setter(hits || libraries || []); 13}; 14 15const getAlgoliaFetchParams = ( 16 query: string, 17 appId: string, 18 apiKey: string, 19 indexName: string, 20 hits: number, 21 additionalParams: object = {} 22): [string, RequestInit] => [ 23 `https://${appId}-dsn.algolia.net/1/indexes/${indexName}/query`, 24 { 25 method: 'POST', 26 headers: { 27 'X-Algolia-Application-Id': appId, 28 'X-Algolia-API-Key': apiKey, 29 }, 30 body: JSON.stringify({ 31 params: `query=${query}&hitsPerPage=${hits}`, 32 highlightPreTag: '<mark>', 33 highlightPostTag: '</mark>', 34 ...additionalParams, 35 }), 36 }, 37]; 38 39export const getExpoDocsResults = (query: string, version?: string) => { 40 return fetch( 41 ...getAlgoliaFetchParams(query, 'QEX7PB7D46', '6652d26570e8628af4601e1d78ad456b', 'expo', 10, { 42 facetFilters: [['version:none', `version:${version}`]], 43 }) 44 ); 45}; 46 47export const getRNDocsResults = (query: string) => { 48 return fetch( 49 ...getAlgoliaFetchParams( 50 query, 51 '8TDSE0OHGQ', 52 'c9c791d9d5fd7f315d7f3859b32c1f3b', 53 'react-native-v2', 54 5, 55 { facetFilters: [['version:current']] } 56 ) 57 ); 58}; 59 60export const getDirectoryResults = (query: string) => { 61 return fetch(`https://reactnative.directory/api/libraries?search=${encodeURI(query)}&limit=5`); 62}; 63 64export const getHighlightHTML = ( 65 item: AlgoliaItemType, 66 tag: keyof AlgoliaItemHierarchy<string> 67) => ({ 68 dangerouslySetInnerHTML: { 69 __html: item._highlightResult.hierarchy[`${tag}`]?.value || '', 70 }, 71}); 72 73const trimContent = (content: string) => { 74 if (!content || !content.length) return ''; 75 76 const trimStart = Math.max(content.indexOf('<mark>') - 36, 0); 77 const trimEnd = Math.min(content.indexOf('</mark>') + 36 + 6, content.length); 78 79 return `${trimStart !== 0 ? '…' : ''}${content.substring(trimStart, trimEnd).trim()}${ 80 trimEnd !== content.length ? '…' : '' 81 }`; 82}; 83 84export const getContentHighlightHTML = (item: AlgoliaItemType) => ({ 85 dangerouslySetInnerHTML: { 86 __html: trimContent(item._highlightResult.content?.value || ''), 87 }, 88}); 89 90// note(simek): this code make sure that browser popup blocker 91// do not prevent opening links via key press (when it fires windows.open) 92export const openLink = (url: string, isExternal: boolean = false) => { 93 const link = document.createElement('a'); 94 if (isExternal) { 95 link.target = '_blank'; 96 link.rel = 'noopener noreferrer'; 97 } 98 link.href = url; 99 link.click(); 100}; 101 102const EASPathChunks = [ 103 '/app-signing/', 104 '/build/', 105 '/build-reference/', 106 '/development/', 107 '/eas/', 108 '/eas/metadata/', 109 '/eas-update/', 110 '/submit/', 111] as const; 112 113export const isEASPath = (url: string) => { 114 for (const pathChunk of EASPathChunks) { 115 if (url.includes(pathChunk)) { 116 return true; 117 } 118 } 119 return false; 120}; 121 122export const isAppleDevice = () => { 123 return /(Mac|iPhone|iPod|iPad)/i.test( 124 navigator?.platform ?? navigator?.userAgentData?.platform ?? '' 125 ); 126}; 127 128export const addHighlight = (content: string, query: string) => { 129 const highlightStart = content.toLowerCase().indexOf(query.toLowerCase()); 130 131 if (highlightStart === -1) return content; 132 133 const highlightEnd = highlightStart + query.length; 134 return ( 135 content.substring(0, highlightStart) + 136 '<mark>' + 137 content.substring(highlightStart, highlightEnd) + 138 '</mark>' + 139 content.substring(highlightEnd) 140 ); 141}; 142