diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index 77992c3b41..ce47ad59ec 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -810,9 +810,7 @@ const schema = yup NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: yup.string(), NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN: yup.string(), NEXT_PUBLIC_UNIVERSAL_PROFILES_API_URL: yup.string(), - NEXT_PUBLIC_ALGOLIA_APP_ID: yup.string(), - NEXT_PUBLIC_ALGOLIA_API_KEY: yup.string(), - NEXT_PUBLIC_ALGOLIA_INDEX_NAME: yup.string(), + NEXT_PUBLIC_GRAPH_URL: yup.string(), NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY: yup.string(), // Misc diff --git a/docs/ENVS.md b/docs/ENVS.md index 595ceb6366..856bf6e665 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -236,9 +236,7 @@ Settings for meta tags, OG tags and SEO | NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED | `boolean` | Set to `true` if SolidityScan reports are supported | - | - | `true` | v1.19.0+ | | NEXT_PUBLIC_VIEWS_CONTRACT_EXTRA_VERIFICATION_METHODS | `Array<'solidity-hardhat' \| 'solidity-foundry'>` | Pass an array of additional methods from which users can choose while verifying a smart contract. Both methods are available by default, pass `'none'` string to disable them all. | - | - | `['solidity-hardhat']` | v1.33.0+ | | NEXT_PUBLIC_UNIVERSAL_PROFILES_API_URL | `string` | LUKSO UNIVERSAL PROFILE API URL used for getting various universal profile data | - | - | `https://api.universalprofile.com` | - | -| NEXT_PUBLIC_ALGOLIA_APP_ID | `string` | Algolia App's ID | - | - | `ABCD1234` | - | -| NEXT_PUBLIC_ALGOLIA_API_KEY | `string` | Algolia API key used for authenticating requests | - | - | `abcd1234defg5678 `| - | -| NEXT_PUBLIC_ALGOLIA_INDEX_NAME | `string` | Index name from where data should be | - | - | `prod_mainnet_data`| - | +| NEXT_PUBLIC_GRAPH_URL | `string` | URL of LUKSO Graph Indexer | - | - | `https://envio.mainnet.lukso.dev/`| - | ##### Address views list | Id | Description | diff --git a/lib/api/buildUniversalProfileUrl.ts b/lib/api/buildUniversalProfileUrl.ts deleted file mode 100644 index 690da58e06..0000000000 --- a/lib/api/buildUniversalProfileUrl.ts +++ /dev/null @@ -1,11 +0,0 @@ -import algoliasearch from 'algoliasearch'; - -import { getEnvValue } from '../../configs/app/utils'; - -const algolia = { - appId: getEnvValue('NEXT_PUBLIC_ALGOLIA_APP_ID') || '', - apiKey: getEnvValue('NEXT_PUBLIC_ALGOLIA_API_KEY') || '', - index: getEnvValue('NEXT_PUBLIC_ALGOLIA_INDEX_NAME') || '', -}; - -export const algoliaIndex = algoliasearch(algolia.appId, algolia.apiKey).initIndex(algolia.index); diff --git a/lib/api/graphClient.ts b/lib/api/graphClient.ts new file mode 100644 index 0000000000..93d1b7b104 --- /dev/null +++ b/lib/api/graphClient.ts @@ -0,0 +1,47 @@ +import type { GraphResponse } from 'types/api/universalProfile'; + +import { getEnvValue } from 'configs/app/utils'; + +import { constructQuery } from './graphQueries'; +import type { QueryOperation, SearchProfileQueryResponse } from './graphTypes'; + +const graphUrl = getEnvValue('NEXT_PUBLIC_GRAPH_URL') || ''; + +type FetchFunc = (queryParams?: string) => Promise | null> + +type GraphClient = { + getProfiles: FetchFunc; +}; + +const queryParamsToGraphQuery = (operationName: QueryOperation, queryParams?: string): string => { + const query = constructQuery(operationName, queryParams); + + return JSON.stringify({ + operationName: operationName, + query: query, + }); +}; + +const fetchQuery = (operationName: QueryOperation): FetchFunc => { + return async(queryParams?: string): Promise | null> => { + const query = queryParamsToGraphQuery(operationName, queryParams); + + try { + const resp = await fetch(graphUrl, { + method: 'POST', + headers: {}, + body: query, + }); + const json = await resp.json(); + return json as GraphResponse; + } catch (err) { + return null; + } + }; +}; + +const createGraphClient = (): GraphClient => ({ + getProfiles: fetchQuery('search_profiles'), +}); + +export const graphClient: GraphClient = createGraphClient(); diff --git a/lib/api/graphQueries.ts b/lib/api/graphQueries.ts new file mode 100644 index 0000000000..90d36ce51f --- /dev/null +++ b/lib/api/graphQueries.ts @@ -0,0 +1,25 @@ +import type { QueryOperation } from './graphTypes'; + +type QueryConstructor = (queryParams?: string) => string; + +export const constructQuery = (operationType: QueryOperation, queryParams?: string) => { + const queryConstruct = queryConstructors[operationType]; + + return queryConstruct(queryParams); +}; + +export const searchProfileQuery = (queryParams?: string): string => `query search_profiles { + search_profiles(args: { search: "${ queryParams }" }) { + profileImages(order_by: { width: asc }) { + src, + width, + }, + id, + name, + fullName, + } +}`; + +const queryConstructors: Record = { + search_profiles: searchProfileQuery, +}; diff --git a/lib/api/graphTypes.ts b/lib/api/graphTypes.ts new file mode 100644 index 0000000000..5c4134b48a --- /dev/null +++ b/lib/api/graphTypes.ts @@ -0,0 +1,18 @@ +export type ProfileImage = { + src: string; + width: number; +} + +export type SearchProfileQueryResponse = { + profileImages: Array; + id: string; + name: string; + fullName: string; +}; + +export type GraphQuery = { + operationName: string; + query: string; +}; + +export type QueryOperation = 'search_profiles'; diff --git a/lib/api/useUniversalProfileApiFetch.ts b/lib/api/useUniversalProfileApiFetch.ts index 9eeea0a0b3..aaa71d7a12 100644 --- a/lib/api/useUniversalProfileApiFetch.ts +++ b/lib/api/useUniversalProfileApiFetch.ts @@ -1,11 +1,11 @@ import React from 'react'; import type { SearchResultAddressOrContractOrUniversalProfile } from '../../types/api/search'; -import type { UniversalProfileProxyResponse } from '../../types/api/universalProfile'; import type { Params as FetchParams } from 'lib/hooks/useFetch'; -import { algoliaIndex } from './buildUniversalProfileUrl'; +import { graphClient } from './graphClient'; +import type { SearchProfileQueryResponse } from './graphTypes'; import { isUniversalProfileEnabled } from './isUniversalProfileEnabled'; import type { ResourceName, ResourcePathParams } from './resources'; @@ -22,13 +22,19 @@ export default function useUniversalProfileApiFetch() { return [] as Array; } try { - const { hits } = await algoliaIndex.search(queryParams); - return hits.map((hit) => { - const hitAsUp = hit as unknown as UniversalProfileProxyResponse; + const result = await graphClient.getProfiles(queryParams); + if (result == null) { + return [] as Array; + } + + const hits = result.data.search_profiles as Array; + + return hits.map((hit: SearchProfileQueryResponse) => { + const hitAsUp = hit as unknown as SearchProfileQueryResponse; return { type: 'universal_profile', - name: hitAsUp.hasProfileName ? hitAsUp.LSP3Profile.name : null, - address: hit.objectID, + name: hitAsUp.name !== '' ? hitAsUp.name.trim() : null, + address: hit.id, is_smart_contract_verified: false, }; }); diff --git a/nextjs/csp/policies/universalprofile.ts b/nextjs/csp/policies/universalprofile.ts index 886889d327..14d516913f 100644 --- a/nextjs/csp/policies/universalprofile.ts +++ b/nextjs/csp/policies/universalprofile.ts @@ -6,6 +6,7 @@ export function universalProfile(): CspDev.DirectiveDescriptor { 'api.universalprofile.cloud', '*.algolianet.com', '*.algolia.net', + 'envio.mainnet.lukso.dev', ], }; } diff --git a/types/api/universalProfile.ts b/types/api/universalProfile.ts index 9e5419567d..0f28b2f277 100644 --- a/types/api/universalProfile.ts +++ b/types/api/universalProfile.ts @@ -1,3 +1,5 @@ +import type { QueryOperation } from 'lib/api/graphTypes'; + export type UniversalProfileProxyResponse = { type: string; hasProfileName: boolean; @@ -25,3 +27,9 @@ export type UniversalProfileAlgoliaResponse = { }; }; } + +export type GraphResponse = { + data: { + [key in QueryOperation]: Array; + }; +}