Skip to content

Commit

Permalink
Introduce bulk and refactor singular (#1)
Browse files Browse the repository at this point in the history
* Introduce getProfile

* Lint

* Introduce bulk and refactor plain

* Cleanup
  • Loading branch information
svemat01 authored Feb 19, 2024
1 parent 85932a7 commit 93805c4
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 95 deletions.
34 changes: 34 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BulkResponse, ListResponse, ProfileResponse } from './types';

export const profileFetcher = async (baseUrl: string, query: string) => {
const url = baseUrl + '/u/' + query;

const response = await fetch(url, {
method: 'GET',
});

if (!response.ok) {
throw new Error('Could not fetch profile');
}

return (await response.json()) as ProfileResponse;
};

export const bulkProfileFetcher = async (
baseUrl: string,
queries: string[]
) => {
const url = `${baseUrl}/bulk/u?queries[]=${queries.join('&queries[]=')}`;

const response = await fetch(url, {
method: 'GET',
});

if (!response.ok) {
throw new Error('Could not fetch profile');
}

return (await response.json()) as ListResponse<
BulkResponse<ProfileResponse>
>;
};
2 changes: 2 additions & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './useBulkProfile';
export * from './useProfile';
export * from './useSSEProfiles';
20 changes: 20 additions & 0 deletions src/hooks/useBulkProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import useSWR from 'swr';

import { bulkProfileFetcher } from '../helpers';
import { PUBLIC_ENDPOINT } from '../public';
import { BaseSwrHookProperties } from '../types/HookProperties';

export const useBulkProfile = (
queries: string[],
properties: BaseSwrHookProperties
) => {
return useSWR(
properties.enabled && [
properties.endpoint ?? PUBLIC_ENDPOINT,
'/bulk/u',
queries,
],
([endpoint, path, queries]) => bulkProfileFetcher(endpoint, queries),
properties.swr
);
};
97 changes: 12 additions & 85 deletions src/hooks/useProfile.ts
Original file line number Diff line number Diff line change
@@ -1,93 +1,20 @@
import useSWR, { SWRConfig } from 'swr';
import useSWR from 'swr';

import { profileFetcher } from '../helpers';
import { PUBLIC_ENDPOINT } from '../public';
import { Profile, RecordKey } from '../types';
import { BaseSwrHookProperties } from '../types/HookProperties';

const AddressRegex = /^0x[\dA-Fa-f]{40}$/;

// More accepting then it should be but good safeguard
const NameRegex = /^(?:[L-NPSZps{}]+\.){2,}[L-NPSZps{}]{2,}$/;

type NameOrAddress =
| {
name: string;
}
| {
address: string;
}
| {
nameOrAddress: string;
};

export type ProfileProperties = NameOrAddress & {
endpoint?: string;
enabled?: boolean;
keys: RecordKey[];
swr?: typeof SWRConfig;
};

const getNameAndAddress = (
properties: NameOrAddress
): { type: 'name' | 'address' | 'unknown'; value: string } => {
if ('name' in properties) {
return { type: 'name', value: properties.name };
}

if ('address' in properties) {
return { type: 'address', value: properties.address };
}

if ('nameOrAddress' in properties) {
if (properties.nameOrAddress.match(NameRegex) !== null) {
return { type: 'name', value: properties.nameOrAddress };
}

if (properties.nameOrAddress.match(AddressRegex) !== null) {
return { type: 'address', value: properties.nameOrAddress };
}
}

return { type: 'unknown', value: '' };
};

const getProfileKey = (
type: 'name' | 'address' | 'unknown',
value: string,
endpoint: string
export const useProfile = (
query: string,
properties: BaseSwrHookProperties
) => {
if (type === 'name') {
return endpoint + '/n/' + value;
}

if (type === 'address') {
return endpoint + '/a/' + value;
}
};

export const useProfile = (properties: ProfileProperties) => {
const { type, value } = getNameAndAddress(properties);

const enabled = properties.enabled ?? type !== 'unknown';

const key = getProfileKey(
type,
value,
properties.endpoint || PUBLIC_ENDPOINT
);

return useSWR(
enabled && key,
async (argument: string) => {
const response = await fetch(argument, {
method: 'GET',
});

if (!response.ok) {
throw new Error('Could not fetch profile');
}

return (await response.json()) as Profile;
},
properties.enabled && [
properties.endpoint ?? PUBLIC_ENDPOINT,
'/u',
query,
],
([endpoint, path, query]) => profileFetcher(endpoint, query),
properties.swr
);
};
54 changes: 54 additions & 0 deletions src/hooks/useSSEProfiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import useSWRSubscription, { SWRSubscriptionOptions } from 'swr/subscription';

import { PUBLIC_ENDPOINT } from '../public';
import { BulkResponse, ProfileResponse, SSEResponse } from '../types';
import { BaseSwrHookProperties } from '../types/HookProperties';

export const useStreamingBulkProfile = (
queries: string[],
properties: BaseSwrHookProperties
) =>
useSWRSubscription(
properties.enabled && [
properties.endpoint ?? PUBLIC_ENDPOINT,
'/sse/u',
queries,
],
(
[endpoint, path, queries],
{
next,
}: SWRSubscriptionOptions<
Record<string, BulkResponse<ProfileResponse>>,
undefined
>
) => {
const eventSource = new EventSource(
`${endpoint}${path}?queries[]=${queries.join('&queries[]=')}`
);

eventSource.addEventListener('message', (event) => {
const response = JSON.parse(event.data) as SSEResponse<
BulkResponse<ProfileResponse>
>;

next(undefined, (previous) => {
return {
...previous,
[response.query]: response.response,
};
});
});

eventSource.addEventListener('error', () => {
// Server Sent Events has no proper close event, so we have to rely on the error event
// to clean up the event source.
eventSource.close();
});

return () => {
eventSource.close();
};
},
properties.swr
);
44 changes: 44 additions & 0 deletions src/types/ENState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { RecordKey } from './RecordKey';

export type ProfileResponse = {
/** Name */
name: string;
/** Ethereum Mainnet Address */
address?: string;
/** Avatar URL */
avatar?: string;
/** Header URL */
header?: string;
/** Preferred Capitalization of Name */
display: string;
/** Records */
records: Record<RecordKey, string>;
/** Addresses on different chains */
chains: Record<string, string>;
/** Unix Timestamp of date it was loaded */
fresh: number;
/** Resolver the information was fetched from */
resolver: string;
ccip_urls: string[];
/** Errors encountered while fetching & decoding */
errors: Record<string, string>;
};

export type BulkResponse<Ok> =
| ({
type: 'success';
} & Ok)
| {
type: 'error';
error: string;
};

export type ListResponse<T> = {
response_length: number;
response: T[];
};

export type SSEResponse<T> = {
query: string;
response: T;
};
7 changes: 7 additions & 0 deletions src/types/HookProperties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SWRConfig } from 'swr';

export type BaseSwrHookProperties = {
endpoint?: string;
enabled?: boolean;
swr?: typeof SWRConfig;
};
9 changes: 0 additions & 9 deletions src/types/Profile.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './Profile';
export * from './ENState';
export * from './RecordKey';

0 comments on commit 93805c4

Please sign in to comment.