Skip to content

Commit

Permalink
feat(idea/frontend): dns (#1583)
Browse files Browse the repository at this point in the history
  • Loading branch information
vraja-nayaka authored Jul 4, 2024
1 parent e4fdb4b commit 5422e69
Show file tree
Hide file tree
Showing 61 changed files with 933 additions and 63 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/CI-CD-k8s-gear-js-stage.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Gear Idea: deploy to k8s stage"
name: 'Gear Idea: deploy to k8s stage'

on:
push:
Expand All @@ -23,9 +23,7 @@ env:
AWS_REGION: ${{ secrets.AWS_REGION }}
KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}


jobs:

build-frontend-image-staging:
runs-on: ubuntu-latest
environment: staging
Expand Down Expand Up @@ -58,6 +56,7 @@ jobs:
VITE_NODES_API_URL= ${{ secrets.REACT_APP_DEFAULT_NODES_URL }}
VITE_MAINNET_VOUCHERS_API_URL=${{ secrets.VITE_MAINNET_VOUCHERS_API_URL }}
VITE_TESTNET_VOUCHERS_API_URL=${{ secrets.VITE_TESTNET_VOUCHERS_API_URL }}
VITE_DNS_API_URL=${{ secrets.VITE_DNS_API_URL }}
build-indexer-image-staging:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -161,12 +160,13 @@ jobs:
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-meta-storage:qa

deploy-to-k8s-staging:
needs: [
needs:
[
build-frontend-image-staging,
build-indexer-image-staging,
build-api-gateway-image-staging,
build-test-balance-image-staging,
build-and-push-meta-storage-image
build-and-push-meta-storage-image,
]
runs-on: ubuntu-latest

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/CI-CD-k8s-release-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ jobs:
VITE_GTM_ID=${{ secrets.VITE_GTM_ID}}
VITE_MAINNET_VOUCHERS_API_URL=${{ secrets.VITE_MAINNET_VOUCHERS_API_URL }}
VITE_TESTNET_VOUCHERS_API_URL=${{ secrets.VITE_TESTNET_VOUCHERS_API_URL }}
VITE_DNS_API_URL=${{ secrets.VITE_DNS_API_URL }}
build-indexer-image-prod:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions idea/frontend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ VITE_NODE_ADDRESS=
VITE_HCAPTCHA_SITE_KEY=
VITE_GTM_ID=
VITE_DEFAULT_TRANSFER_BALANCE_VALUE=
VITE_DNS_API_URL=
6 changes: 4 additions & 2 deletions idea/frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ ARG VITE_NODE_ADDRESS \
VITE_HCAPTCHA_SITE_KEY \
VITE_GTM_ID \
VITE_MAINNET_VOUCHERS_API_URL \
VITE_TESTNET_VOUCHERS_API_URL
VITE_TESTNET_VOUCHERS_API_URL \
VITE_DNS_API_URL
ENV VITE_NODE_ADDRESS=${VITE_NODE_ADDRESS} \
VITE_VOUCHERS_API_URL=${VITE_VOUCHERS_API_URL} \
VITE_API_URL=${VITE_API_URL} \
Expand All @@ -30,7 +31,8 @@ ENV VITE_NODE_ADDRESS=${VITE_NODE_ADDRESS} \
VITE_HCAPTCHA_SITE_KEY=${VITE_HCAPTCHA_SITE_KEY} \
VITE_GTM_ID=${VITE_GTM_ID} \
VITE_MAINNET_VOUCHERS_API_URL=${VITE_MAINNET_VOUCHERS_API_URL} \
VITE_TESTNET_VOUCHERS_API_URL=${VITE_TESTNET_VOUCHERS_API_URL}
VITE_TESTNET_VOUCHERS_API_URL=${VITE_TESTNET_VOUCHERS_API_URL} \
VITE_DNS_API_URL=${VITE_DNS_API_URL}


RUN yarn build:frontend
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions idea/frontend/src/features/dns/consts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { z } from 'zod';

import { Values } from '../types';
import { Program } from './sails';

const DNS_API_URL = import.meta.env.VITE_DNS_API_URL as string;

const DNS_PROGRAM_QUERY_KEY = ['dnsProgram'];

const FIELD_NAME = {
DNS_NAME: 'name',
DNS_ADDRESS: 'address',
} as const;

const DEFAULT_VALUES: Values = {
[FIELD_NAME.DNS_ADDRESS]: '',
[FIELD_NAME.DNS_NAME]: '',
};

const domainNameRegex = /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const NAME_SCHEMA = z.string().trim().regex(domainNameRegex, 'Invalid domain name');

export { DNS_API_URL, DNS_PROGRAM_QUERY_KEY, FIELD_NAME, DEFAULT_VALUES, NAME_SCHEMA, Program };
72 changes: 72 additions & 0 deletions idea/frontend/src/features/dns/consts/sails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { GearApi } from '@gear-js/api';
import { TypeRegistry } from '@polkadot/types';
import { TransactionBuilder } from 'sails-js';

export type ActorId = `0x${string}`;

export interface ContractInfo {
admin: ActorId;
program_id: ActorId;
registration_time: string;
}

export class Program {
public readonly registry: TypeRegistry;
public readonly dns: Dns;

constructor(public api: GearApi, public programId?: `0x${string}`) {
const types = {
ActorId: '([u8; 32])',
ContractInfo: { admin: 'ActorId', program_id: 'ActorId', registration_time: 'String' },
};

this.registry = new TypeRegistry();
this.registry.setKnownTypes({ types });
this.registry.register(types);

this.dns = new Dns(this);
}
}

export class Dns {
constructor(private _program: Program) {}

public addNewProgram(name: string, program_id: ActorId): TransactionBuilder<null> {
if (!this._program.programId) throw new Error('Program ID is not set');
return new TransactionBuilder<null>(
this._program.api,
this._program.registry,
'send_message',
['Dns', 'AddNewProgram', name, program_id],
'(String, String, String, ActorId)',
'Null',
this._program.programId,
);
}

public changeProgramId(name: string, new_program_id: ActorId): TransactionBuilder<null> {
if (!this._program.programId) throw new Error('Program ID is not set');
return new TransactionBuilder<null>(
this._program.api,
this._program.registry,
'send_message',
['Dns', 'ChangeProgramId', name, new_program_id],
'(String, String, String, ActorId)',
'Null',
this._program.programId,
);
}

public deleteProgram(name: string): TransactionBuilder<null> {
if (!this._program.programId) throw new Error('Program ID is not set');
return new TransactionBuilder<null>(
this._program.api,
this._program.registry,
'send_message',
['Dns', 'DeleteProgram', name],
'(String, String, String)',
'Null',
this._program.programId,
);
}
}
8 changes: 8 additions & 0 deletions idea/frontend/src/features/dns/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useDnsSort } from './use-dns-sort';
import { useDns } from './use-dns';
import { useDnsFilters } from './use-dns-filters';
import { useDnsActions } from './use-dns-actions';
import { useInitDnsProgram } from './use-init-dns-program';
import { useDnsSchema } from './use-dns-schema';

export { useDns, useDnsFilters, useDnsSort, useDnsActions, useInitDnsProgram, useDnsSchema };
85 changes: 85 additions & 0 deletions idea/frontend/src/features/dns/hooks/use-dns-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { HexString } from '@gear-js/api';
import { useAccount } from '@gear-js/react-hooks';
import { web3FromSource } from '@polkadot/extension-dapp';
import { TransactionBuilder } from 'sails-js';

import { Method } from '@/features/explorer';
import { useLoading, useModal, useSignAndSend } from '@/hooks';
import { TransactionName } from '@/shared/config';

import { DNS_PROGRAM_QUERY_KEY, Program } from '../consts';
import { useQueryClient } from '@tanstack/react-query';

type ResolveRejectOptions = {
resolve?: () => void;
reject?: () => void;
};

type SendDnsMessage = {
getTransactionBuilder: () => TransactionBuilder<null>;
options?: ResolveRejectOptions;
};

const useDnsActions = () => {
const [isLoading, enableLoading, disableLoading] = useLoading();
const { showModal } = useModal();
const { account } = useAccount();
const signAndSend = useSignAndSend();
const queryClient = useQueryClient();
const state = queryClient.getQueryState<Program>(DNS_PROGRAM_QUERY_KEY);
const dnsProgram = state?.data;

const sendMessage = async ({ getTransactionBuilder, options }: SendDnsMessage) => {
if (!account || !dnsProgram) {
return;
}
const { resolve: onSuccess, reject: onError } = options || {};
const { signer } = await web3FromSource(account.meta.source);
const transaction = getTransactionBuilder();
transaction.withAccount(account.address, { signer });
await transaction.calculateGas();

const extrinsic = transaction.extrinsic;
const { partialFee } = await extrinsic.paymentInfo(account.address, { signer });

const handleConfirm = () => {
enableLoading();
signAndSend(extrinsic, Method.MessageQueued, {
onSuccess,
onError,
onFinally: () => disableLoading(),
});
};

showModal('transaction', {
fee: partialFee.toHuman(),
name: TransactionName.SendMessage,
addressFrom: account.address,
addressTo: dnsProgram.programId,
onAbort: onError,
onConfirm: handleConfirm,
});
};

const addNewProgram = (name: string, program_id: HexString, options?: ResolveRejectOptions) => {
if (!dnsProgram) throw new Error('dnsProgram is not initialized');
const getTransactionBuilder = () => dnsProgram.dns.addNewProgram(name, program_id);
return sendMessage({ getTransactionBuilder, options });
};

const changeProgramId = (name: string, program_id: HexString, options?: ResolveRejectOptions) => {
if (!dnsProgram) throw new Error('dnsProgram is not initialized');
const getTransactionBuilder = () => dnsProgram.dns.changeProgramId(name, program_id);
return sendMessage({ getTransactionBuilder, options });
};

const deleteProgram = (name: string, options?: ResolveRejectOptions) => {
if (!dnsProgram) throw new Error('dnsProgram is not initialized');
const getTransactionBuilder = () => dnsProgram.dns.deleteProgram(name);
return sendMessage({ getTransactionBuilder, options });
};

return { isLoading, addNewProgram, changeProgramId, deleteProgram };
};

export { useDnsActions };
26 changes: 26 additions & 0 deletions idea/frontend/src/features/dns/hooks/use-dns-filters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useAccount } from '@gear-js/react-hooks';
import { useMemo, useState } from 'react';

const DEFAULT_FILTER_VALUES = {
owner: 'all',
};

function useDnsFilters() {
const { account } = useAccount();
const [values, setValues] = useState(DEFAULT_FILTER_VALUES);

const getOwnerParams = () => {
if (!account) return {};

const { decodedAddress } = account;
const { owner } = values;

return owner === 'all' ? {} : { createdBy: decodedAddress };
};

const params = useMemo(() => getOwnerParams(), [values, account]);

return [values, params, setValues] as const;
}

export { useDnsFilters };
16 changes: 16 additions & 0 deletions idea/frontend/src/features/dns/hooks/use-dns-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useProgramIdSchema } from '@/hooks';
import { z } from 'zod';
import { FIELD_NAME, NAME_SCHEMA } from '../consts';

const useDnsSchema = () => {
const programIdSchema = useProgramIdSchema([]);

const dnsSchema = z.object({
[FIELD_NAME.DNS_ADDRESS]: programIdSchema,
[FIELD_NAME.DNS_NAME]: NAME_SCHEMA,
});

return dnsSchema;
};

export { useDnsSchema };
22 changes: 22 additions & 0 deletions idea/frontend/src/features/dns/hooks/use-dns-sort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useState } from 'react';
import { SortDirection } from '../types';

const DEFAULT_SORT_VALUES = {
orderByField: 'updatedAt',
orderByDirection: 'DESC' as SortDirection,
};

function useDnsSort() {
const [values, setValues] = useState(DEFAULT_SORT_VALUES);

const toggleDirection = () => {
setValues(({ orderByField, orderByDirection }) => ({
orderByField,
orderByDirection: orderByDirection === 'ASC' ? 'DESC' : 'ASC',
}));
};

return [values, toggleDirection, setValues] as const;
}

export { useDnsSort };
23 changes: 23 additions & 0 deletions idea/frontend/src/features/dns/hooks/use-dns.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useInfiniteQuery } from '@tanstack/react-query';

import { DEFAULT_LIMIT } from '@/shared/config';

import { getDns, getNextPageParam } from '../utils';
import { DnsFilterParams, DnsSortParams } from '../types';

function useDns(search: string, filterParams: DnsFilterParams, sortParams: DnsSortParams) {
const { data, isLoading, hasNextPage, fetchNextPage, refetch } = useInfiniteQuery({
queryKey: ['dns', search, filterParams, sortParams],
queryFn: ({ pageParam }) =>
getDns({ limit: DEFAULT_LIMIT, offset: pageParam, search, ...filterParams, ...sortParams }),
initialPageParam: 0,
getNextPageParam,
});

const dns = data?.pages.flatMap((page) => page.data) || [];
const dnsCount = data?.pages[0].count || 0;

return [dns, dnsCount, isLoading, hasNextPage, fetchNextPage, refetch] as const;
}

export { useDns };
30 changes: 30 additions & 0 deletions idea/frontend/src/features/dns/hooks/use-init-dns-program.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useApi } from '@gear-js/react-hooks';

import { DNS_API_URL, DNS_PROGRAM_QUERY_KEY, Program } from '../consts';
import { useQuery } from '@tanstack/react-query';

const useInitDnsProgram = () => {
const { isApiReady, api } = useApi();

const getDnsProgram = () =>
fetch(`${DNS_API_URL}/dns/contract`).then((response) => {
return response.json().then(({ contract }) => {
const programId = contract;
if (isApiReady) {
return new Program(api, programId);
}
});
});

const { data, isPending } = useQuery({
queryKey: DNS_PROGRAM_QUERY_KEY,
queryFn: getDnsProgram,
enabled: isApiReady,
});

const isLoading = isPending;

return { data, isLoading };
};

export { useInitDnsProgram };
Loading

0 comments on commit 5422e69

Please sign in to comment.