wallet-core/packages/demobank-ui/src/hooks/access.ts

384 lines
11 KiB
TypeScript
Raw Normal View History

2023-02-08 21:41:19 +01:00
/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import {
HttpResponse,
HttpResponseOk,
HttpResponsePaginated,
2023-02-17 20:23:37 +01:00
RequestError,
2023-02-08 21:41:19 +01:00
} from "@gnu-taler/web-util/lib/index.browser";
2023-02-10 13:51:37 +01:00
import { useEffect, useState } from "preact/hooks";
2023-02-08 21:41:19 +01:00
import { useBackendContext } from "../context/backend.js";
2023-02-10 13:51:37 +01:00
import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
import {
useAuthenticatedBackend,
useMatchMutate,
2023-02-17 20:23:37 +01:00
usePublicBackend,
2023-02-10 13:51:37 +01:00
} from "./backend.js";
2023-02-08 21:41:19 +01:00
2023-02-16 16:14:24 +01:00
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
2023-02-17 20:23:37 +01:00
import _useSWR, { SWRHook } from "swr";
const useSWR = _useSWR as unknown as SWRHook;
2023-02-16 16:14:24 +01:00
2023-02-08 21:41:19 +01:00
export function useAccessAPI(): AccessAPI {
const mutateAll = useMatchMutate();
const { request } = useAuthenticatedBackend();
2023-02-10 13:51:37 +01:00
const { state } = useBackendContext();
2023-02-08 21:41:19 +01:00
if (state.status === "loggedOut") {
2023-02-10 13:51:37 +01:00
throw Error("access-api can't be used when the user is not logged In");
2023-02-08 21:41:19 +01:00
}
2023-02-10 13:51:37 +01:00
const account = state.username;
2023-02-08 21:41:19 +01:00
const createWithdrawal = async (
data: SandboxBackend.Access.BankAccountCreateWithdrawalRequest,
2023-02-10 13:51:37 +01:00
): Promise<
HttpResponseOk<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>
> => {
const res =
await request<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>(
`access-api/accounts/${account}/withdrawals`,
{
method: "POST",
data,
contentType: "json",
},
);
2023-02-08 21:41:19 +01:00
return res;
};
2023-02-10 13:51:37 +01:00
const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => {
const res = await request<void>(
`access-api/accounts/${account}/withdrawals/${id}`,
{
method: "POST",
contentType: "json",
},
);
2023-02-08 21:41:19 +01:00
await mutateAll(/.*accounts\/.*\/withdrawals\/.*/);
return res;
};
const confirmWithdrawal = async (
id: string,
): Promise<HttpResponseOk<void>> => {
2023-02-10 13:51:37 +01:00
const res = await request<void>(
`access-api/accounts/${account}/withdrawals/${id}`,
{
method: "POST",
contentType: "json",
},
);
2023-02-08 21:41:19 +01:00
await mutateAll(/.*accounts\/.*\/withdrawals\/.*/);
return res;
};
const createTransaction = async (
2023-02-10 13:51:37 +01:00
data: SandboxBackend.Access.CreateBankAccountTransactionCreate,
2023-02-08 21:41:19 +01:00
): Promise<HttpResponseOk<void>> => {
2023-02-10 13:51:37 +01:00
const res = await request<void>(
`access-api/accounts/${account}/transactions`,
{
method: "POST",
data,
contentType: "json",
},
);
2023-02-08 21:41:19 +01:00
await mutateAll(/.*accounts\/.*\/transactions.*/);
return res;
};
2023-02-10 13:51:37 +01:00
const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
2023-02-08 21:41:19 +01:00
const res = await request<void>(`access-api/accounts/${account}`, {
method: "DELETE",
2023-02-10 13:51:37 +01:00
contentType: "json",
2023-02-08 21:41:19 +01:00
});
await mutateAll(/.*accounts\/.*/);
return res;
};
2023-02-10 13:51:37 +01:00
return {
abortWithdrawal,
confirmWithdrawal,
createWithdrawal,
createTransaction,
deleteAccount,
};
2023-02-08 21:41:19 +01:00
}
export function useTestingAPI(): TestingAPI {
const mutateAll = useMatchMutate();
const { request: noAuthRequest } = usePublicBackend();
const register = async (
2023-02-10 13:51:37 +01:00
data: SandboxBackend.Access.BankRegistrationRequest,
2023-02-08 21:41:19 +01:00
): Promise<HttpResponseOk<void>> => {
const res = await noAuthRequest<void>(`access-api/testing/register`, {
method: "POST",
data,
2023-02-10 13:51:37 +01:00
contentType: "json",
2023-02-08 21:41:19 +01:00
});
await mutateAll(/.*accounts\/.*/);
return res;
};
return { register };
}
export interface TestingAPI {
register: (
2023-02-10 13:51:37 +01:00
data: SandboxBackend.Access.BankRegistrationRequest,
2023-02-08 21:41:19 +01:00
) => Promise<HttpResponseOk<void>>;
}
export interface AccessAPI {
createWithdrawal: (
data: SandboxBackend.Access.BankAccountCreateWithdrawalRequest,
2023-02-10 13:51:37 +01:00
) => Promise<
HttpResponseOk<SandboxBackend.Access.BankAccountCreateWithdrawalResponse>
>;
abortWithdrawal: (wid: string) => Promise<HttpResponseOk<void>>;
confirmWithdrawal: (wid: string) => Promise<HttpResponseOk<void>>;
2023-02-08 21:41:19 +01:00
createTransaction: (
2023-02-10 13:51:37 +01:00
data: SandboxBackend.Access.CreateBankAccountTransactionCreate,
2023-02-08 21:41:19 +01:00
) => Promise<HttpResponseOk<void>>;
deleteAccount: () => Promise<HttpResponseOk<void>>;
}
export interface InstanceTemplateFilter {
//FIXME: add filter to the template list
position?: string;
}
2023-02-10 13:51:37 +01:00
export function useAccountDetails(
account: string,
): HttpResponse<
SandboxBackend.Access.BankAccountBalanceResponse,
SandboxBackend.SandboxError
> {
2023-02-08 21:41:19 +01:00
const { fetcher } = useAuthenticatedBackend();
2023-02-16 16:14:24 +01:00
const { data, error } = useSWR<
2023-02-08 21:41:19 +01:00
HttpResponseOk<SandboxBackend.Access.BankAccountBalanceResponse>,
2023-02-10 13:51:37 +01:00
RequestError<SandboxBackend.SandboxError>
2023-02-08 21:41:19 +01:00
>([`access-api/accounts/${account}`], fetcher, {
refreshInterval: 0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
errorRetryCount: 0,
errorRetryInterval: 1,
shouldRetryOnError: false,
keepPreviousData: true,
});
if (data) return data;
2023-02-10 13:51:37 +01:00
if (error) return error.info;
2023-02-08 21:41:19 +01:00
return { loading: true };
}
// FIXME: should poll
2023-02-10 13:51:37 +01:00
export function useWithdrawalDetails(
account: string,
wid: string,
): HttpResponse<
SandboxBackend.Access.BankAccountGetWithdrawalResponse,
SandboxBackend.SandboxError
> {
2023-02-08 21:41:19 +01:00
const { fetcher } = useAuthenticatedBackend();
2023-02-16 16:14:24 +01:00
const { data, error } = useSWR<
2023-02-08 21:41:19 +01:00
HttpResponseOk<SandboxBackend.Access.BankAccountGetWithdrawalResponse>,
2023-02-10 13:51:37 +01:00
RequestError<SandboxBackend.SandboxError>
2023-02-08 21:41:19 +01:00
>([`access-api/accounts/${account}/withdrawals/${wid}`], fetcher, {
refreshInterval: 1000,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
errorRetryCount: 0,
errorRetryInterval: 1,
shouldRetryOnError: false,
keepPreviousData: true,
});
// if (isValidating) return { loading: true, data: data?.data };
if (data) return data;
2023-02-10 13:51:37 +01:00
if (error) return error.info;
2023-02-08 21:41:19 +01:00
return { loading: true };
}
2023-02-10 13:51:37 +01:00
export function useTransactionDetails(
account: string,
tid: string,
): HttpResponse<
SandboxBackend.Access.BankAccountTransactionInfo,
SandboxBackend.SandboxError
> {
2023-02-08 21:41:19 +01:00
const { fetcher } = useAuthenticatedBackend();
2023-02-16 16:14:24 +01:00
const { data, error } = useSWR<
2023-02-08 21:41:19 +01:00
HttpResponseOk<SandboxBackend.Access.BankAccountTransactionInfo>,
2023-02-10 13:51:37 +01:00
RequestError<SandboxBackend.SandboxError>
2023-02-08 21:41:19 +01:00
>([`access-api/accounts/${account}/transactions/${tid}`], fetcher, {
refreshInterval: 0,
refreshWhenHidden: false,
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
errorRetryCount: 0,
errorRetryInterval: 1,
shouldRetryOnError: false,
keepPreviousData: true,
});
// if (isValidating) return { loading: true, data: data?.data };
if (data) return data;
2023-02-10 13:51:37 +01:00
if (error) return error.info;
2023-02-08 21:41:19 +01:00
return { loading: true };
}
interface PaginationFilter {
2023-02-10 13:51:37 +01:00
page: number;
2023-02-08 21:41:19 +01:00
}
export function usePublicAccounts(
args?: PaginationFilter,
2023-02-10 13:51:37 +01:00
): HttpResponsePaginated<
SandboxBackend.Access.PublicAccountsResponse,
SandboxBackend.SandboxError
> {
2023-02-08 21:41:19 +01:00
const { paginatedFetcher } = usePublicBackend();
const [page, setPage] = useState(1);
const {
data: afterData,
error: afterError,
isValidating: loadingAfter,
2023-02-16 16:14:24 +01:00
} = useSWR<
2023-02-08 21:41:19 +01:00
HttpResponseOk<SandboxBackend.Access.PublicAccountsResponse>,
2023-02-10 13:51:37 +01:00
RequestError<SandboxBackend.SandboxError>
2023-02-08 21:41:19 +01:00
>([`public-accounts`, args?.page, PAGE_SIZE], paginatedFetcher);
const [lastAfter, setLastAfter] = useState<
2023-02-10 13:51:37 +01:00
HttpResponse<
SandboxBackend.Access.PublicAccountsResponse,
SandboxBackend.SandboxError
>
2023-02-08 21:41:19 +01:00
>({ loading: true });
useEffect(() => {
if (afterData) setLastAfter(afterData);
}, [afterData]);
2023-02-10 13:51:37 +01:00
if (afterError) return afterError.info;
2023-02-08 21:41:19 +01:00
// if the query returns less that we ask, then we have reach the end or beginning
const isReachingEnd =
afterData && afterData.data.publicAccounts.length < PAGE_SIZE;
const isReachingStart = false;
const pagination = {
isReachingEnd,
isReachingStart,
loadMore: () => {
if (!afterData || isReachingEnd) return;
if (afterData.data.publicAccounts.length < MAX_RESULT_SIZE) {
setPage(page + 1);
}
},
loadMorePrev: () => {
2023-02-10 13:51:37 +01:00
null;
2023-02-08 21:41:19 +01:00
},
};
2023-02-10 13:51:37 +01:00
const publicAccounts = !afterData
? []
: (afterData || lastAfter).data.publicAccounts;
if (loadingAfter) return { loading: true, data: { publicAccounts } };
2023-02-08 21:41:19 +01:00
if (afterData) {
return { ok: true, data: { publicAccounts }, ...pagination };
}
return { loading: true };
}
/**
* FIXME: mutate result when balance change (transaction )
2023-02-10 13:51:37 +01:00
* @param account
* @param args
* @returns
2023-02-08 21:41:19 +01:00
*/
export function useTransactions(
account: string,
args?: PaginationFilter,
2023-02-10 13:51:37 +01:00
): HttpResponsePaginated<
SandboxBackend.Access.BankAccountTransactionsResponse,
SandboxBackend.SandboxError
> {
2023-02-08 21:41:19 +01:00
const { paginatedFetcher } = useAuthenticatedBackend();
const [page, setPage] = useState(1);
const {
data: afterData,
error: afterError,
isValidating: loadingAfter,
2023-02-16 16:14:24 +01:00
} = useSWR<
2023-02-08 21:41:19 +01:00
HttpResponseOk<SandboxBackend.Access.BankAccountTransactionsResponse>,
2023-02-10 13:51:37 +01:00
RequestError<SandboxBackend.SandboxError>
>(
[`access-api/accounts/${account}/transactions`, args?.page, PAGE_SIZE],
paginatedFetcher,
);
2023-02-08 21:41:19 +01:00
const [lastAfter, setLastAfter] = useState<
2023-02-10 13:51:37 +01:00
HttpResponse<
SandboxBackend.Access.BankAccountTransactionsResponse,
SandboxBackend.SandboxError
>
2023-02-08 21:41:19 +01:00
>({ loading: true });
useEffect(() => {
if (afterData) setLastAfter(afterData);
}, [afterData]);
2023-02-10 13:51:37 +01:00
if (afterError) return afterError.info;
2023-02-08 21:41:19 +01:00
// if the query returns less that we ask, then we have reach the end or beginning
const isReachingEnd =
afterData && afterData.data.transactions.length < PAGE_SIZE;
const isReachingStart = false;
const pagination = {
isReachingEnd,
isReachingStart,
loadMore: () => {
if (!afterData || isReachingEnd) return;
if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
setPage(page + 1);
}
},
loadMorePrev: () => {
2023-02-10 13:51:37 +01:00
null;
2023-02-08 21:41:19 +01:00
},
};
2023-02-10 13:51:37 +01:00
const transactions = !afterData
? []
: (afterData || lastAfter).data.transactions;
if (loadingAfter) return { loading: true, data: { transactions } };
2023-02-08 21:41:19 +01:00
if (afterData) {
return { ok: true, data: { transactions }, ...pagination };
}
return { loading: true };
}