diff --git a/commafeed-client/.eslintrc b/commafeed-client/.eslintrc index b4a46a5f..3607f9e1 100644 --- a/commafeed-client/.eslintrc +++ b/commafeed-client/.eslintrc @@ -35,7 +35,7 @@ "react-hooks/exhaustive-deps": [ "warn", { - "additionalHooks": "useAsync" + "additionalHooks": "(^useAsync$)" } ], @@ -67,8 +67,9 @@ "useState", "useAppSelector", "useAppDispatch", + "useAsync", "useForm", - "useMutation", + "useAsyncCallback", "useCallback", "useEffect" ] diff --git a/commafeed-client/package-lock.json b/commafeed-client/package-lock.json index 42126cce..cf21dc21 100644 --- a/commafeed-client/package-lock.json +++ b/commafeed-client/package-lock.json @@ -32,8 +32,7 @@ "react-infinite-scroller": "^1.2.6", "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", - "tinycon": "^0.6.8", - "use-mutation": "^2.2.1" + "tinycon": "^0.6.8" }, "devDependencies": { "@lingui/cli": "^3.14.0", @@ -8490,31 +8489,6 @@ } } }, - "node_modules/use-mutation": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/use-mutation/-/use-mutation-2.2.1.tgz", - "integrity": "sha512-qGvtQpVgj2Ekcrrykiq4rOlOhUrlebaO/sVVGpXjrb0IEOBtZqNzq8hKJwrYGkyUWqu3pBPpOEyss8OHRhS3Ow==", - "dependencies": { - "use-safe-callback": "^1.0.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/use-safe-callback": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/use-safe-callback/-/use-safe-callback-1.0.2.tgz", - "integrity": "sha512-RVeoyxgjJNfu9baNwulBM+HuPQGlcE1MJtrA1w50B5Hd0tyeKvrBIPdCfRx4Trrrxe34M6qRTys/bfpqsKRcyA==", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16" - } - }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -14903,20 +14877,6 @@ "use-isomorphic-layout-effect": "^1.1.1" } }, - "use-mutation": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/use-mutation/-/use-mutation-2.2.1.tgz", - "integrity": "sha512-qGvtQpVgj2Ekcrrykiq4rOlOhUrlebaO/sVVGpXjrb0IEOBtZqNzq8hKJwrYGkyUWqu3pBPpOEyss8OHRhS3Ow==", - "requires": { - "use-safe-callback": "^1.0.2" - } - }, - "use-safe-callback": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/use-safe-callback/-/use-safe-callback-1.0.2.tgz", - "integrity": "sha512-RVeoyxgjJNfu9baNwulBM+HuPQGlcE1MJtrA1w50B5Hd0tyeKvrBIPdCfRx4Trrrxe34M6qRTys/bfpqsKRcyA==", - "requires": {} - }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", diff --git a/commafeed-client/package.json b/commafeed-client/package.json index 20612dd1..8a0bbeb3 100644 --- a/commafeed-client/package.json +++ b/commafeed-client/package.json @@ -39,8 +39,7 @@ "react-infinite-scroller": "^1.2.6", "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", - "tinycon": "^0.6.8", - "use-mutation": "^2.2.1" + "tinycon": "^0.6.8" }, "devDependencies": { "@lingui/cli": "^3.14.0", diff --git a/commafeed-client/src/app/client.ts b/commafeed-client/src/app/client.ts index 27882e4a..51e7b07f 100644 --- a/commafeed-client/src/app/client.ts +++ b/commafeed-client/src/app/client.ts @@ -101,5 +101,3 @@ export const errorToStrings = (err: any) => { return strings } - -export const errorsToStrings = (errors: any[]) => errors.map(errorToStrings).flat() diff --git a/commafeed-client/src/components/admin/UserEdit.tsx b/commafeed-client/src/components/admin/UserEdit.tsx index 178bf072..266392dc 100644 --- a/commafeed-client/src/components/admin/UserEdit.tsx +++ b/commafeed-client/src/components/admin/UserEdit.tsx @@ -4,8 +4,8 @@ import { useForm } from "@mantine/form" import { client, errorToStrings } from "app/client" import { UserModel } from "app/types" import { Alert } from "components/Alert" +import { useAsyncCallback } from "react-async-hook" import { TbDeviceFloppy } from "react-icons/tb" -import useMutation from "use-mutation" interface UserEditProps { user?: UserModel @@ -17,18 +17,17 @@ export function UserEdit(props: UserEditProps) { const form = useForm({ initialValues: props.user ?? ({ enabled: true } as UserModel), }) - const [saveUser, saveUserResult] = useMutation(client.admin.saveUser, { onSuccess: props.onSave }) - const errors = errorToStrings(saveUserResult.error) + const saveUser = useAsyncCallback(client.admin.saveUser, { onSuccess: props.onSave }) return ( <> - {errors.length > 0 && ( + {saveUser.error && ( - + )} -
+ @@ -40,7 +39,7 @@ export function UserEdit(props: UserEditProps) { - diff --git a/commafeed-client/src/components/content/add/AddCategory.tsx b/commafeed-client/src/components/content/add/AddCategory.tsx index 58527d40..1bdc7c57 100644 --- a/commafeed-client/src/components/content/add/AddCategory.tsx +++ b/commafeed-client/src/components/content/add/AddCategory.tsx @@ -7,8 +7,8 @@ import { reloadTree } from "app/slices/tree" import { useAppDispatch } from "app/store" import { AddCategoryRequest } from "app/types" import { Alert } from "components/Alert" +import { useAsyncCallback } from "react-async-hook" import { TbFolderPlus } from "react-icons/tb" -import useMutation from "use-mutation" import { CategorySelect } from "./CategorySelect" export function AddCategory() { @@ -16,23 +16,22 @@ export function AddCategory() { const form = useForm() - const [addCategory, addCategoryResult] = useMutation(client.category.add, { + const addCategory = useAsyncCallback(client.category.add, { onSuccess: () => { dispatch(reloadTree()) dispatch(redirectToSelectedSource()) }, }) - const errors = errorToStrings(addCategoryResult.error) return ( <> - {errors.length > 0 && ( + {addCategory.error && ( - + )} - + @@ -40,7 +39,7 @@ export function AddCategory() { - diff --git a/commafeed-client/src/components/content/add/ImportOpml.tsx b/commafeed-client/src/components/content/add/ImportOpml.tsx index 122dfa22..00ec9f7b 100644 --- a/commafeed-client/src/components/content/add/ImportOpml.tsx +++ b/commafeed-client/src/components/content/add/ImportOpml.tsx @@ -6,8 +6,8 @@ import { redirectToSelectedSource } from "app/slices/redirect" import { reloadTree } from "app/slices/tree" import { useAppDispatch } from "app/store" import { Alert } from "components/Alert" +import { useAsyncCallback } from "react-async-hook" import { TbFileImport } from "react-icons/tb" -import useMutation from "use-mutation" export function ImportOpml() { const dispatch = useAppDispatch() @@ -18,23 +18,22 @@ export function ImportOpml() { }, }) - const [importOpml, importOpmlResult] = useMutation(client.feed.importOpml, { + const importOpml = useAsyncCallback(client.feed.importOpml, { onSuccess: () => { dispatch(reloadTree()) dispatch(redirectToSelectedSource()) }, }) - const errors = errorToStrings(importOpmlResult.error) return ( <> - {errors.length > 0 && ( + {importOpml.error && ( - + )} - importOpml(v.file))}> + importOpml.execute(v.file))}> dispatch(redirectToSelectedSource())}> Cancel - diff --git a/commafeed-client/src/components/content/add/Subscribe.tsx b/commafeed-client/src/components/content/add/Subscribe.tsx index b8bac67b..d7e110f6 100644 --- a/commafeed-client/src/components/content/add/Subscribe.tsx +++ b/commafeed-client/src/components/content/add/Subscribe.tsx @@ -1,7 +1,7 @@ import { t, Trans } from "@lingui/macro" import { Box, Button, Group, Stack, Stepper, TextInput } from "@mantine/core" import { useForm } from "@mantine/form" -import { client, errorsToStrings, errorToStrings } from "app/client" +import { client, errorToStrings } from "app/client" import { Constants } from "app/constants" import { redirectToFeed, redirectToSelectedSource } from "app/slices/redirect" import { reloadTree } from "app/slices/tree" @@ -9,8 +9,8 @@ import { useAppDispatch } from "app/store" import { FeedInfoRequest, SubscribeRequest } from "app/types" import { Alert } from "components/Alert" import { useState } from "react" +import { useAsyncCallback } from "react-async-hook" import { TbRss } from "react-icons/tb" -import useMutation from "use-mutation" import { CategorySelect } from "./CategorySelect" export function Subscribe() { @@ -31,20 +31,19 @@ export function Subscribe() { }, }) - const [fetchFeed, fetchFeedResult] = useMutation(client.feed.fetchFeed, { + const fetchFeed = useAsyncCallback(client.feed.fetchFeed, { onSuccess: ({ data }) => { - step1Form.setFieldValue("url", data.data.url) - step1Form.setFieldValue("title", data.data.title) + step1Form.setFieldValue("url", data.url) + step1Form.setFieldValue("title", data.title) setActiveStep(step => step + 1) }, }) - const [subscribe, subscribeResult] = useMutation(client.feed.subscribe, { + const subscribe = useAsyncCallback(client.feed.subscribe, { onSuccess: sub => { dispatch(reloadTree()) - dispatch(redirectToFeed(sub.data.data.id)) + dispatch(redirectToFeed(sub.data.id)) }, }) - const errors = errorsToStrings([fetchFeedResult.error, errorToStrings(subscribeResult.error)]) const previousStep = () => { if (activeStep === 0) dispatch(redirectToSelectedSource()) @@ -52,17 +51,23 @@ export function Subscribe() { } const nextStep = (e: React.FormEvent) => { if (activeStep === 0) { - step0Form.onSubmit(fetchFeed)(e) + step0Form.onSubmit(fetchFeed.execute)(e) } else if (activeStep === 1) { - step1Form.onSubmit(subscribe)(e) + step1Form.onSubmit(subscribe.execute)(e) } } return ( <> - {errors.length > 0 && ( + {fetchFeed.error && ( - + + + )} + + {subscribe.error && ( + + )} @@ -96,16 +101,12 @@ export function Subscribe() { Back {activeStep === 0 && ( - )} {activeStep === 1 && ( - )} diff --git a/commafeed-client/src/components/settings/ProfileSettings.tsx b/commafeed-client/src/components/settings/ProfileSettings.tsx index afd4bbd8..c802e015 100644 --- a/commafeed-client/src/components/settings/ProfileSettings.tsx +++ b/commafeed-client/src/components/settings/ProfileSettings.tsx @@ -2,15 +2,15 @@ import { t, Trans } from "@lingui/macro" import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core" import { useForm } from "@mantine/form" import { openConfirmModal } from "@mantine/modals" -import { client, errorsToStrings } from "app/client" +import { client, errorToStrings } from "app/client" import { redirectToLogin, redirectToSelectedSource } from "app/slices/redirect" import { reloadProfile } from "app/slices/user" import { useAppDispatch, useAppSelector } from "app/store" import { ProfileModificationRequest } from "app/types" import { Alert } from "components/Alert" import { useEffect } from "react" +import { useAsyncCallback } from "react-async-hook" import { TbDeviceFloppy, TbTrash } from "react-icons/tb" -import useMutation from "use-mutation" interface FormData extends ProfileModificationRequest { newPasswordConfirmation?: string @@ -27,18 +27,17 @@ export function ProfileSettings() { }) const { setValues } = form - const [saveProfile, saveProfileResult] = useMutation(client.user.saveProfile, { + const saveProfile = useAsyncCallback(client.user.saveProfile, { onSuccess: () => { dispatch(reloadProfile()) dispatch(redirectToSelectedSource()) }, }) - const [deleteProfile, deleteProfileResult] = useMutation(client.user.deleteProfile, { + const deleteProfile = useAsyncCallback(client.user.deleteProfile, { onSuccess: () => { dispatch(redirectToLogin()) }, }) - const errors = errorsToStrings([saveProfileResult.error, deleteProfileResult.error]) const openDeleteProfileModal = () => openConfirmModal({ @@ -50,7 +49,7 @@ export function ProfileSettings() { ), labels: { confirm: t`Confirm`, cancel: t`Cancel` }, confirmProps: { color: "red" }, - onConfirm: () => deleteProfile({}), + onConfirm: () => deleteProfile.execute(), }) useEffect(() => { @@ -64,13 +63,19 @@ export function ProfileSettings() { return ( <> - {errors.length > 0 && ( + {saveProfile.error && ( - + )} - + {deleteProfile.error && ( + + + + )} + + {profile?.name} @@ -105,7 +110,7 @@ export function ProfileSettings() { - @@ -113,7 +118,7 @@ export function ProfileSettings() { color="red" leftIcon={} onClick={() => openDeleteProfileModal()} - loading={deleteProfileResult.status === "running"} + loading={deleteProfile.loading} > Delete account diff --git a/commafeed-client/src/pages/admin/AdminUsersPage.tsx b/commafeed-client/src/pages/admin/AdminUsersPage.tsx index 20ca5ca0..73ba8919 100644 --- a/commafeed-client/src/pages/admin/AdminUsersPage.tsx +++ b/commafeed-client/src/pages/admin/AdminUsersPage.tsx @@ -7,9 +7,8 @@ import { UserEdit } from "components/admin/UserEdit" import { Alert } from "components/Alert" import { Loader } from "components/Loader" import { RelativeDate } from "components/RelativeDate" -import { useAsync } from "react-async-hook" +import { useAsync, useAsyncCallback } from "react-async-hook" import { TbCheck, TbPencil, TbPlus, TbTrash, TbX } from "react-icons/tb" -import useMutation from "use-mutation" function BooleanIcon({ value }: { value: boolean }) { return value ? : @@ -20,13 +19,12 @@ export function AdminUsersPage() { const query = useAsync(() => client.admin.getAllUsers(), []) const users = query.result?.data.sort((a, b) => a.id - b.id) - const [deleteUser, deleteUserResult] = useMutation(client.admin.deleteUser, { + const deleteUser = useAsyncCallback(client.admin.deleteUser, { onSuccess: () => { query.execute() closeAllModals() }, }) - const errors = errorToStrings(deleteUserResult.error) const openUserEditModal = (title: string, user?: UserModel) => { openModal({ @@ -57,7 +55,7 @@ export function AdminUsersPage() { ), labels: { confirm: t`Confirm`, cancel: t`Cancel` }, confirmProps: { color: "red" }, - onConfirm: () => deleteUser({ id: user.id }), + onConfirm: () => deleteUser.execute({ id: user.id }), }) } @@ -73,9 +71,9 @@ export function AdminUsersPage() { - {errors.length > 0 && ( + {deleteUser.error && ( - + )} @@ -134,7 +132,7 @@ export function AdminUsersPage() { openUserDeleteModal(u)} - loading={deleteUserResult.status === "running"} + loading={deleteUser.loading} > diff --git a/commafeed-client/src/pages/app/CategoryDetailsPage.tsx b/commafeed-client/src/pages/app/CategoryDetailsPage.tsx index 80f4eaf1..b8ee8ec8 100644 --- a/commafeed-client/src/pages/app/CategoryDetailsPage.tsx +++ b/commafeed-client/src/pages/app/CategoryDetailsPage.tsx @@ -13,35 +13,34 @@ import { Alert } from "components/Alert" import { CategorySelect } from "components/content/add/CategorySelect" import { Loader } from "components/Loader" import { useEffect } from "react" -import { useAsync } from "react-async-hook" +import { useAsync, useAsyncCallback } from "react-async-hook" import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { useParams } from "react-router-dom" -import useMutation from "use-mutation" export function CategoryDetailsPage() { const { id = Constants.categoryIds.all } = useParams() const apiKey = useAppSelector(state => state.user.profile?.apiKey) const dispatch = useAppDispatch() + const query = useAsync(() => client.category.getRoot(), []) const category = query.result && flattenCategoryTree(query.result.data).find(c => c.id === id) const form = useForm() const { setValues } = form - const [modify, modifyResult] = useMutation(client.category.modify, { + const modifyCategory = useAsyncCallback(client.category.modify, { onSuccess: () => { dispatch(reloadTree()) dispatch(redirectToSelectedSource()) }, }) - const [deleteCategory, deleteCategoryResult] = useMutation(client.category.delete, { + const deleteCategory = useAsyncCallback(client.category.delete, { onSuccess: () => { dispatch(reloadTree()) dispatch(redirectToRootCategory()) }, }) - const errors = [...errorToStrings(modifyResult.error), ...errorToStrings(deleteCategoryResult.error)] const openDeleteCategoryModal = () => { const categoryName = category?.name @@ -56,7 +55,7 @@ export function CategoryDetailsPage() { ), labels: { confirm: t`Confirm`, cancel: t`Cancel` }, confirmProps: { color: "red" }, - onConfirm: () => deleteCategory({ id: +id }), + onConfirm: () => deleteCategory.execute({ id: +id }), }) } @@ -73,13 +72,19 @@ export function CategoryDetailsPage() { if (!category) return return ( - {errors.length > 0 && ( + {modifyCategory.error && ( - + )} - + {deleteCategory.error && ( + + + + )} + + {category.name} @@ -105,7 +110,7 @@ export function CategoryDetailsPage() { - @@ -113,7 +118,7 @@ export function CategoryDetailsPage() { color="red" leftIcon={} onClick={() => openDeleteCategoryModal()} - loading={deleteCategoryResult.status === "running"} + loading={deleteCategory.loading} > Delete diff --git a/commafeed-client/src/pages/app/FeedDetailsPage.tsx b/commafeed-client/src/pages/app/FeedDetailsPage.tsx index 77549055..b46eb006 100644 --- a/commafeed-client/src/pages/app/FeedDetailsPage.tsx +++ b/commafeed-client/src/pages/app/FeedDetailsPage.tsx @@ -2,7 +2,7 @@ import { t, Trans } from "@lingui/macro" import { Anchor, Box, Button, Code, Container, Divider, Group, Input, NumberInput, Stack, Text, TextInput, Title } from "@mantine/core" import { useForm } from "@mantine/form" import { openConfirmModal } from "@mantine/modals" -import { client, errorsToStrings } from "app/client" +import { client, errorToStrings } from "app/client" import { redirectToRootCategory, redirectToSelectedSource } from "app/slices/redirect" import { reloadTree } from "app/slices/tree" import { useAppDispatch, useAppSelector } from "app/store" @@ -12,10 +12,9 @@ import { CategorySelect } from "components/content/add/CategorySelect" import { Loader } from "components/Loader" import { RelativeDate } from "components/RelativeDate" import { useEffect } from "react" -import { useAsync } from "react-async-hook" +import { useAsync, useAsyncCallback } from "react-async-hook" import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { useParams } from "react-router-dom" -import useMutation from "use-mutation" function FilteringExpressionDescription() { const example = url.contains('youtube') or (author eq 'athou' and title.contains('github') @@ -60,19 +59,18 @@ export function FeedDetailsPage() { const form = useForm() const { setValues } = form - const [modify, modifyResult] = useMutation(client.feed.modify, { + const modifyFeed = useAsyncCallback(client.feed.modify, { onSuccess: () => { dispatch(reloadTree()) dispatch(redirectToSelectedSource()) }, }) - const [unsubscribe, unsubscribeResult] = useMutation(client.feed.unsubscribe, { + const unsubscribe = useAsyncCallback(client.feed.unsubscribe, { onSuccess: () => { dispatch(reloadTree()) dispatch(redirectToRootCategory()) }, }) - const errors = errorsToStrings([modifyResult.error, unsubscribeResult.error]) const openUnsubscribeModal = () => { const feedName = feed?.name @@ -87,7 +85,7 @@ export function FeedDetailsPage() { ), labels: { confirm: t`Confirm`, cancel: t`Cancel` }, confirmProps: { color: "red" }, - onConfirm: () => unsubscribe({ id: +id }), + onConfirm: () => unsubscribe.execute({ id: +id }), }) } @@ -99,13 +97,19 @@ export function FeedDetailsPage() { if (!feed) return return ( - {errors.length > 0 && ( + {modifyFeed.error && ( - + )} - + {unsubscribe.error && ( + + + + )} + + {feed.name} @@ -159,7 +163,7 @@ export function FeedDetailsPage() { - @@ -167,7 +171,7 @@ export function FeedDetailsPage() { color="red" leftIcon={} onClick={() => openUnsubscribeModal()} - loading={unsubscribeResult.status === "running"} + loading={unsubscribe.loading} > Unsubscribe diff --git a/commafeed-client/src/pages/auth/LoginPage.tsx b/commafeed-client/src/pages/auth/LoginPage.tsx index c3d00449..1dcb573c 100644 --- a/commafeed-client/src/pages/auth/LoginPage.tsx +++ b/commafeed-client/src/pages/auth/LoginPage.tsx @@ -7,8 +7,8 @@ import { useAppDispatch, useAppSelector } from "app/store" import { LoginRequest } from "app/types" import { Alert } from "components/Alert" import { Logo } from "components/Logo" +import { useAsyncCallback } from "react-async-hook" import { Link } from "react-router-dom" -import useMutation from "use-mutation" export function LoginPage() { const serverInfos = useAppSelector(state => state.server.serverInfos) @@ -21,12 +21,11 @@ export function LoginPage() { }, }) - const [login, loginResult] = useMutation(client.user.login, { + const login = useAsyncCallback(client.user.login, { onSuccess: () => { dispatch(redirectToRootCategory()) }, }) - const errors = errorToStrings(loginResult.error) return ( @@ -40,12 +39,12 @@ export function LoginPage() { <Trans>Log in</Trans> - {errors.length > 0 && ( + {login.error && ( - + )} - + )} - {serverInfos?.allowRegistrations && ( diff --git a/commafeed-client/src/pages/auth/PasswordRecoveryPage.tsx b/commafeed-client/src/pages/auth/PasswordRecoveryPage.tsx index ddbcbd8d..bad8b440 100644 --- a/commafeed-client/src/pages/auth/PasswordRecoveryPage.tsx +++ b/commafeed-client/src/pages/auth/PasswordRecoveryPage.tsx @@ -6,8 +6,8 @@ import { PasswordResetRequest } from "app/types" import { Alert } from "components/Alert" import { Logo } from "components/Logo" import { useState } from "react" +import { useAsyncCallback } from "react-async-hook" import { Link } from "react-router-dom" -import useMutation from "use-mutation" export function PasswordRecoveryPage() { const [message, setMessage] = useState("") @@ -18,15 +18,11 @@ export function PasswordRecoveryPage() { }, }) - const [recoverPassword, recoverPasswordResult] = useMutation(client.user.passwordReset, { - onMutate: () => { - setMessage("") - }, + const recoverPassword = useAsyncCallback(client.user.passwordReset, { onSuccess: () => { setMessage(t`An email has been sent if this address was registered. Check your inbox.`) }, }) - const errors = errorToStrings(recoverPasswordResult.error) return ( @@ -40,17 +36,25 @@ export function PasswordRecoveryPage() { <Trans>Password Recovery</Trans> - {errors.length > 0 && ( + + {recoverPassword.error && ( - + )} + {message && ( )} - + + { + setMessage("") + recoverPassword.execute(req) + })} + > - diff --git a/commafeed-client/src/pages/auth/RegistrationPage.tsx b/commafeed-client/src/pages/auth/RegistrationPage.tsx index cb9f18b7..d410b73c 100644 --- a/commafeed-client/src/pages/auth/RegistrationPage.tsx +++ b/commafeed-client/src/pages/auth/RegistrationPage.tsx @@ -7,8 +7,8 @@ import { useAppDispatch, useAppSelector } from "app/store" import { RegistrationRequest } from "app/types" import { Alert } from "components/Alert" import { Logo } from "components/Logo" +import { useAsyncCallback } from "react-async-hook" import { Link } from "react-router-dom" -import useMutation from "use-mutation" export function RegistrationPage() { const serverInfos = useAppSelector(state => state.server.serverInfos) @@ -22,12 +22,11 @@ export function RegistrationPage() { }, }) - const [register, registerResult] = useMutation(client.user.register, { + const register = useAsyncCallback(client.user.register, { onSuccess: () => { dispatch(redirectToRootCategory()) }, }) - const errors = errorToStrings(registerResult.error) return ( @@ -48,13 +47,13 @@ export function RegistrationPage() { )} {serverInfos?.allowRegistrations && ( <> - {errors.length > 0 && ( + {register.error && ( - + )} - + -