Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrease zustand usage #28

Merged
merged 3 commits into from Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 2 additions & 19 deletions apps/client/src/components/Layouts/MainLayout.tsx
@@ -1,37 +1,20 @@
import React, { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { getLibraries } from '~api/library';
import Lazy from '~components/Lazy';
import Topbar from '~components/Topbar';
import Sidebar from '~components/Sidebar/Sidebar';
import { useStore } from '~store/store';

import { Box, Flex, useColorModeValue } from '@chakra-ui/react';
import { AxiosError } from 'axios';
import client from '~api/client';
import { useUser } from '~hooks/useUser';
import { useLibraries } from '~hooks/useLibraries';

export default function MainLayout() {
const location = useLocation();

const setLibraries = useStore((state) => state.setLibraries);

const _user = useUser();

const { isLoading, error } = useQuery(['getLibraries'], getLibraries, {
onSuccess(res) {
setLibraries(res.data.data);
},
onError(err: AxiosError) {
// 401 errors will be handled below
if (err.response?.status !== 401) {
throw new Error(err.message);
}
},
// Send all non-401 errors to the error page
useErrorBoundary: (err: AxiosError) => !err || (err.response?.status ?? 500) !== 401,
});
const { isLoading, error } = useLibraries();

const hideSidebar = useMemo(() => {
// hide sidebar when on /books/:id/pages/:page or /epub/
Expand Down
4 changes: 3 additions & 1 deletion apps/client/src/components/Library/LibraryModalForm.tsx
Expand Up @@ -23,6 +23,7 @@ import TextArea from '~ui/TextArea';
import { TagOption } from '~hooks/useTags';
import { useStore } from '~store/store';
import { Library } from '@stump/core';
import { useLibraries } from '~hooks/useLibraries';

interface Props {
tags: TagOption[];
Expand All @@ -37,7 +38,8 @@ interface Props {
* It is not intended to be used outside of those components.
*/
export default function LibraryModalForm({ tags, onSubmit, fetchingTags, reset, library }: Props) {
const libraries = useStore((state) => state.libraries);
// const libraries = useStore((state) => state.libraries);
const { libraries } = useLibraries();

const schema = z.object({
name: z
Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/components/Library/LibraryOptionsMenu.tsx
Expand Up @@ -15,7 +15,7 @@ interface Props {
}

export default function LibraryOptionsMenu({ library }: Props) {
const user = useUser();
const { user } = useUser();

const { mutate: scan } = useMutation(['scanLibary'], { mutationFn: scanLibary });

Expand Down
3 changes: 3 additions & 0 deletions apps/client/src/components/Media/LazyEpubReader.tsx
Expand Up @@ -75,6 +75,9 @@ export default function LazyEpubReader({ id, loc }: LazyEpubReaderProps) {
setBook(
new Book(`${baseURL}/media/${id}/file`, {
openAs: 'epub',
// @ts-ignore: more incorrect types >:( I really truly cannot stress enough how much I want to just
// rip out my eyes working with epubjs...
requestCredentials: true,
}),
);
}
Expand Down
13 changes: 7 additions & 6 deletions apps/client/src/components/Settings/General/ProfileForm.tsx
@@ -1,17 +1,18 @@
import React from 'react';
import { FormControl, FormLabel } from '@chakra-ui/react';
import { zodResolver } from '@hookform/resolvers/zod';
import { FieldValues, useForm } from 'react-hook-form';
import { z } from 'zod';
import { useLocale } from '~hooks/useLocale';
import { useUser } from '~hooks/useUser';
import Button from '~ui/Button';
import Form from '~ui/Form';
import Input, { PasswordInput } from '~ui/Input';
import { useLocale } from '~hooks/useLocale';
import { useUser } from '~hooks/useUser';

import { FormControl, FormLabel } from '@chakra-ui/react';
import { zodResolver } from '@hookform/resolvers/zod';

import SettingsFormContainer from '../SettingsFormContainer';

export default function ProfileForm() {
const user = useUser();
const { user } = useUser();
const { t } = useLocale();

if (!user) {
Expand Down
7 changes: 3 additions & 4 deletions apps/client/src/components/Sidebar/Sidebar.tsx
Expand Up @@ -24,6 +24,7 @@ import CreateLibraryModal from '~components/Library/CreateLibraryModal';
import Logout from './Logout';
import { useLocale } from '~hooks/useLocale';
import { Library } from '@stump/core';
import { useLibraries } from '~hooks/useLibraries';

interface NavMenuItemProps extends Library {
href: string;
Expand Down Expand Up @@ -127,17 +128,15 @@ export function SidebarContent() {

const { locale, t } = useLocale();

const libraries = useStore((state) => state.libraries, shallow);

// console.log('libraries', libraries);
const { libraries } = useLibraries();

const links: Array<NavItemProps> = useMemo(
() => [
{ name: t('sidebar.buttons.home'), icon: House as any, href: '/' },
{
name: t('sidebar.buttons.libraries'),
icon: Books as any,
items: libraries.map((library) => ({
items: libraries?.map((library) => ({
...library,
href: `/libraries/${library.id}`,
})),
Expand Down
7 changes: 4 additions & 3 deletions apps/client/src/components/Topbar/SortConfig.tsx
@@ -1,3 +1,7 @@
import { SortAscending, SortDescending } from 'phosphor-react';
import { useState } from 'react';
import Button from '~ui/Button';

import {
ButtonGroup,
Menu,
Expand All @@ -6,9 +10,6 @@ import {
MenuList,
useColorModeValue,
} from '@chakra-ui/react';
import { SortAscending, SortDescending } from 'phosphor-react';
import React, { useState } from 'react';
import Button from '~ui/Button';

interface SortConfigProps {}

Expand Down
31 changes: 31 additions & 0 deletions apps/client/src/hooks/useLibraries.ts
@@ -0,0 +1,31 @@
import { Library, PageInfo } from '@stump/core';
import { useQuery } from '@tanstack/react-query';
import { AxiosError } from 'axios';
import { useMemo } from 'react';
import { getLibraries } from '~api/library';

export function useLibraries() {
const { data, ...rest } = useQuery(['getLibraries'], getLibraries, {
// Send all non-401 errors to the error page
useErrorBoundary: (err: AxiosError) => !err || (err.response?.status ?? 500) !== 401,
});

const { libraries, pageData } = useMemo<{ libraries: Library[]; pageData?: PageInfo }>(() => {
if (data?.data) {
return {
libraries: data.data.data,
pageData: data.data._page,
};
}

return { libraries: [] };
}, [data]);

console.log({ libraries });

return {
libraries,
pageData,
...rest,
};
}
23 changes: 16 additions & 7 deletions apps/client/src/hooks/useLocale.ts
@@ -1,22 +1,31 @@
import '~i18n/config';
import { useTranslation } from 'react-i18next';
import { useStore } from '~store/store';
import shallow from 'zustand/shallow';
import { Locale } from '~util/enums';
import { useUser } from './useUser';

// FIXME: When a user logs out, their locale should be persisted. Right now,
// user.preferences doesn't exist in a logged out state. I think what I should maybe do
// instead of clearing it on logout is set an auth state to false or something? this way,
// preferences remain (mainly for locale) and shows correct language on login.
// Alternatively, I can be lazy and just persist a separate locale state. ~shrug~
export function useLocale() {
const userPreferences = useStore((state) => state.userPreferences, shallow);
const setLocale = useStore((state) => state.setLocale);
const { preferences, updatePreferences } = useUser();

function setLocaleFromStr(localeStr: string) {
let locale = localeStr as Locale;

if (locale) {
setLocale(locale);
if (preferences && locale) {
updatePreferences({ ...preferences, locale });
}
}

const locale: string = userPreferences?.locale || 'en';
function setLocale(locale: Locale) {
if (preferences && locale) {
updatePreferences({ ...preferences, locale });
}
}

const locale: string = preferences?.locale || 'en';

const { t } = useTranslation(locale);

Expand Down
18 changes: 0 additions & 18 deletions apps/client/src/hooks/usePreferences.ts

This file was deleted.

40 changes: 37 additions & 3 deletions apps/client/src/hooks/useUser.ts
@@ -1,12 +1,19 @@
import { useQuery } from '@tanstack/react-query';
import toast from 'react-hot-toast';
import shallow from 'zustand/shallow';
import { me } from '~api/auth';
import { updateUserPreferences } from '~api/user';
import { useStore } from '~store/store';

import { UserPreferences } from '@stump/core';
import { useMutation, useQuery } from '@tanstack/react-query';

export function useUser() {
const user = useStore((state) => state.user, shallow);

const { setUser } = useStore((state) => ({ setUser: state.setUserAndPreferences }));
const { setUser, setUserPreferences } = useStore((state) => ({
setUser: state.setUser,
setUserPreferences: state.setUserPreferences,
}));

const _ = useQuery(['getViewer'], me, {
// Do not run query unless there is no user in the store
Expand All @@ -18,7 +25,34 @@ export function useUser() {
},
});

const { mutateAsync } = useMutation(
['updateUserPreferences', user?.id],
(preferences: UserPreferences) => updateUserPreferences(user!.id, preferences),
{
onSuccess(res) {
setUserPreferences(res.data);
},
},
);

function updatePreferences(preferences: UserPreferences, showToast = false) {
if (user?.id && preferences) {
if (showToast) {
toast.promise(mutateAsync(preferences), {
loading: 'Updating...',
success: 'Preferences updated!',
error: 'Failed to update preferences.',
});
} else {
mutateAsync(preferences).catch((err) => {
console.error(err);
toast.error('Failed to update preferences.');
});
}
}
}

// TODO: handle on 401?

return user;
return { user, preferences: user?.preferences, updatePreferences };
}
38 changes: 27 additions & 11 deletions apps/client/src/hooks/useViewMode.ts
@@ -1,19 +1,35 @@
import { ViewMode } from '@stump/core';
import { useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import shallow from 'zustand/shallow';
import { useStore } from '~store/store';
import { useUser } from './useUser';

export function useViewMode() {
const location = useLocation();

const { userPreferences, setLibraryViewMode, setSeriesViewMode } = useStore(
({ userPreferences, setLibraryViewMode, setSeriesViewMode }) => ({
userPreferences,
setLibraryViewMode,
setSeriesViewMode,
}),
shallow,
);
const { preferences, updatePreferences } = useUser();

function setLibraryViewMode(viewMode: ViewMode) {
if (preferences) {
updatePreferences({ ...preferences, libraryViewMode: viewMode });
}
}

function setSeriesViewMode(viewMode: ViewMode) {
if (preferences) {
updatePreferences({ ...preferences, seriesViewMode: viewMode });
}
}

// const { userPreferences, setLibraryViewMode, setSeriesViewMode } = useStore(
// ({ userPreferences, setLibraryViewMode, setSeriesViewMode }) => ({
// userPreferences,
// setLibraryViewMode,
// setSeriesViewMode,
// }),
// shallow,
// );

const { showViewOptions, viewAsGrid, onViewModeChange } = useMemo(() => {
let _showViewOptions =
Expand All @@ -29,10 +45,10 @@ export function useViewMode() {
let _onViewModeChange;

if (location.pathname.match(/\/libraries\/.+$/)) {
_viewAsGrid = userPreferences?.libraryViewMode === 'GRID';
_viewAsGrid = preferences?.libraryViewMode === 'GRID';
_onViewModeChange = setLibraryViewMode;
} else if (location.pathname.match(/\/series\/.+$/)) {
_viewAsGrid = userPreferences?.seriesViewMode === 'GRID';
_viewAsGrid = preferences?.seriesViewMode === 'GRID';
_onViewModeChange = setSeriesViewMode;
}

Expand All @@ -48,7 +64,7 @@ export function useViewMode() {
viewAsGrid: _viewAsGrid,
onViewModeChange: _onViewModeChange,
};
}, [location.pathname, userPreferences]);
}, [location.pathname, preferences]);

return { showViewOptions, viewAsGrid, onViewModeChange };
}
9 changes: 3 additions & 6 deletions apps/client/src/pages/Auth/Login.tsx
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { FieldValues, useForm } from 'react-hook-form';
import { Navigate } from 'react-router-dom';
import { z } from 'zod';
Expand Down Expand Up @@ -46,10 +46,7 @@ export default function Login() {
}
}, [claimCheck]);

const { user, setUserAndPreferences } = useStore(
({ user, setUserAndPreferences }) => ({ user, setUserAndPreferences }),
shallow,
);
const { user, setUser } = useStore(({ user, setUser }) => ({ user, setUser }), shallow);

const { isLoading: isLoggingIn, mutateAsync: loginUser } = useMutation(['loginUser'], {
mutationFn: login,
Expand All @@ -60,7 +57,7 @@ export default function Login() {

client.invalidateQueries(['getLibraries']);

setUserAndPreferences(res.data);
setUser(res.data);
},
onError: (err) => {
// TODO: handle this error
Expand Down