parent
7a361ebaa0
commit
ffdc32ba47
@ -19,7 +19,9 @@
|
||||
"@radix-ui/react-radio-group": "^1.1.2",
|
||||
"@stitches/react": "^1.2.8",
|
||||
"@tabler/icons-webfont": "^2.17.0",
|
||||
"@tanstack/react-query": "^4.29.5",
|
||||
"@trpc/client": "10.20.0",
|
||||
"@trpc/react-query": "^10.23.0",
|
||||
"@trpc/server": "10.20.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"deepmerge": "^4.3.1",
|
||||
|
@ -1,10 +1,8 @@
|
||||
import React, { lazy } from 'react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
|
||||
|
||||
import { SuspenseView } from './components/primitives/SuspenseView';
|
||||
|
||||
import { HeaderBar } from '@/components/HeaderBar.js';
|
||||
import { SuspenseView } from '@/components/primitives/SuspenseView';
|
||||
import { useToolsGlobalEffects } from '@/global-effects';
|
||||
import { token } from '@/misc/token';
|
||||
|
||||
@ -13,6 +11,8 @@ const IndexWelcome = lazy(() => import('@/pages/index.welcome'));
|
||||
const Settings = lazy(() => import('@/pages/settings'));
|
||||
const Appearance = lazy(() => import('@/pages/settings/appearance'));
|
||||
const Account = lazy(() => import('@/pages/settings/account'));
|
||||
const AnnouncementsPage = lazy(() => import('@/pages/announcements'));
|
||||
const NotFound = lazy(() => import('@/pages/not-found'));
|
||||
|
||||
export const App : React.FC = () => {
|
||||
useToolsGlobalEffects();
|
||||
@ -28,6 +28,7 @@ export const App : React.FC = () => {
|
||||
<Route path="account" element={<Account />}/>
|
||||
<Route path="*" element={<p>Not Found</p>}/>
|
||||
</Route>
|
||||
<Route path="/announcements/:id" element={<AnnouncementsPage />}/>
|
||||
<Route path="*" element={<NotFound />}/>
|
||||
</Routes>
|
||||
</SuspenseView>
|
||||
|
@ -5,6 +5,7 @@ import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import { App } from '@/App';
|
||||
import { BackendError } from '@/components/domains/backend-error/BackendError.js';
|
||||
import { TRPCProvider } from '@/libs/trpc';
|
||||
|
||||
import 'vite/modulepreload-polyfill';
|
||||
import 'ress';
|
||||
@ -20,8 +21,10 @@ const error = (window as any).__misshaialert?.error;
|
||||
|
||||
if (el != null) {
|
||||
createRoot(el).render(error ? (<BackendError error={error} />) : (
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
<TRPCProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</TRPCProvider>
|
||||
));
|
||||
}
|
||||
|
45
packages/frontend/src/libs/trpc.tsx
Normal file
45
packages/frontend/src/libs/trpc.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { httpLink } from '@trpc/client';
|
||||
import { createTRPCReact } from '@trpc/react-query';
|
||||
import { createTRPCJotai } from 'jotai-trpc';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
|
||||
import type { AppRouter } from 'tools-backend';
|
||||
|
||||
import { LOCALSTORAGE_KEY_TOKEN } from '@/const';
|
||||
|
||||
const link = httpLink({
|
||||
url: `${location.origin}/api`,
|
||||
headers() {
|
||||
const token = localStorage[LOCALSTORAGE_KEY_TOKEN];
|
||||
if (!token) return {};
|
||||
return {
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const init = {
|
||||
links: [ link ],
|
||||
};
|
||||
|
||||
export const trpcJotai = createTRPCJotai<AppRouter>({
|
||||
links: [ link ],
|
||||
});
|
||||
|
||||
export const trpc = createTRPCReact<AppRouter, unknown, 'ExperimentalSuspense'>();
|
||||
|
||||
const providerInit = {
|
||||
client: trpc.createClient(init),
|
||||
queryClient: new QueryClient(),
|
||||
};
|
||||
|
||||
export const TRPCProvider: React.FC<PropsWithChildren> = ({ children }) => {
|
||||
return (
|
||||
<trpc.Provider {...providerInit}>
|
||||
<QueryClientProvider client={providerInit.queryClient}>
|
||||
{children}
|
||||
</QueryClientProvider>
|
||||
</trpc.Provider>
|
||||
);
|
||||
};
|
55
packages/frontend/src/pages/announcements.tsx
Normal file
55
packages/frontend/src/pages/announcements.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { PageRoot } from '@/components/PageRoot';
|
||||
import { Text } from '@/components/primitives/Text';
|
||||
import { styled } from '@/libs/stitches';
|
||||
import { trpc } from '@/libs/trpc';
|
||||
import { languageAtom } from '@/store/client-settings';
|
||||
|
||||
const AnnouncementsPage: React.FC = () => {
|
||||
const { id } = useParams<{id: string}>();
|
||||
|
||||
const [announcement] = trpc.announcements.get.useSuspenseQuery(Number(id));
|
||||
|
||||
const language = useAtomValue(languageAtom);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
const Section = styled('section', {
|
||||
padding: '$m',
|
||||
'h1, h2, h3': {
|
||||
marginTop: '$l',
|
||||
marginBottom: '$xs',
|
||||
},
|
||||
'p, ul': {
|
||||
marginBottom: '$m',
|
||||
},
|
||||
li: {
|
||||
marginLeft: '$l',
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<PageRoot title={t('announcements') ?? ''}>
|
||||
<article>
|
||||
<h2>
|
||||
{announcement.title}{' '}
|
||||
</h2>
|
||||
<Text as="aside" color="muted" fontSize="m" css={{ marginLeft: '$l' }}>
|
||||
<i className="ti ti-clock" />
|
||||
{dayjs(announcement.createdAt).locale(language.split('_')[0]).fromNow()}
|
||||
</Text>
|
||||
<Section>
|
||||
<ReactMarkdown>{announcement.body}</ReactMarkdown>
|
||||
</Section>
|
||||
</article>
|
||||
</PageRoot>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnnouncementsPage;
|
@ -58,22 +58,13 @@ const SettingsPage: React.FC = () => {
|
||||
href: '/settings/appearance',
|
||||
iconClassName: 'ti ti-palette',
|
||||
label: t('appearance') ?? '',
|
||||
}, {
|
||||
type: 'link',
|
||||
href: '/settings/misshai',
|
||||
iconClassName: 'ti ti-antenna',
|
||||
label: t('missHaiAlert') ?? '',
|
||||
}, {
|
||||
type: 'separator',
|
||||
}, {
|
||||
type: 'button',
|
||||
iconClassName: 'ti ti-logout',
|
||||
label: t('logout') ?? '',
|
||||
} , {
|
||||
type: 'button',
|
||||
iconClassName: 'ti ti-trash',
|
||||
label: t('deleteAccount') ?? '',
|
||||
} ] as MenuItemWithoutNesting[], [t]);
|
||||
}] as MenuItemWithoutNesting[], [t]);
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { trpcJotai } from '@/store/api';
|
||||
import { trpcJotai } from '@/libs/trpc';
|
||||
|
||||
export const accountAtom = trpcJotai.account.getMyself.atomWithQuery();
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
import { trpcJotai } from '@/store/api';
|
||||
|
||||
export const announcementListAtom = trpcJotai.announcements.getAll.atomWithQuery();
|
||||
|
||||
export const currentAnnouncementIdAtom = atom(0);
|
||||
|
||||
export const announcementAtom = trpcJotai.announcements.get.atomWithQuery((get) => get(currentAnnouncementIdAtom));
|
||||
|
||||
export const likeAtom = trpcJotai.announcements.like.atomWithMutation();
|
@ -1,21 +0,0 @@
|
||||
import { httpLink } from '@trpc/client';
|
||||
import { createTRPCJotai } from 'jotai-trpc';
|
||||
|
||||
import type { AppRouter } from 'tools-backend';
|
||||
|
||||
import { LOCALSTORAGE_KEY_TOKEN } from '@/const';
|
||||
|
||||
const link = httpLink({
|
||||
url: `${location.origin}/api`,
|
||||
headers() {
|
||||
const token = localStorage[LOCALSTORAGE_KEY_TOKEN];
|
||||
if (!token) return {};
|
||||
return {
|
||||
Authorization: `Bearer ${token}`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const trpcJotai = createTRPCJotai<AppRouter>({
|
||||
links: [ link ],
|
||||
});
|
@ -1,3 +1,3 @@
|
||||
import { trpcJotai } from '.';
|
||||
import { trpcJotai } from '@/libs/trpc';
|
||||
|
||||
export const metaAtom = trpcJotai.meta.get.atomWithQuery();
|
||||
|
@ -176,9 +176,15 @@ importers:
|
||||
'@tabler/icons-webfont':
|
||||
specifier: ^2.17.0
|
||||
version: 2.17.0
|
||||
'@tanstack/react-query':
|
||||
specifier: ^4.29.5
|
||||
version: 4.29.5(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/client':
|
||||
specifier: 10.20.0
|
||||
version: 10.20.0(@trpc/server@10.20.0)
|
||||
'@trpc/react-query':
|
||||
specifier: ^10.23.0
|
||||
version: 10.23.0(@tanstack/react-query@4.29.5)(@trpc/client@10.20.0)(@trpc/server@10.20.0)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/server':
|
||||
specifier: 10.20.0
|
||||
version: 10.20.0
|
||||
@ -3706,6 +3712,28 @@ packages:
|
||||
resolution: {integrity: sha512-UeJaylOGNRhQKyDlgZfrQ3UPSGlfVQuXcmCsTYeXioKKepibW6VZ3H36Lo1jvBTBkQD2e9m+k2NxwkztOTXwrA==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/query-core@4.29.5:
|
||||
resolution: {integrity: sha512-xXIiyQ/4r9KfaJ3k6kejqcaqFXXBTzN2aOJ5H1J6aTJE9hl/nbgAdfF6oiIu0CD5xowejJEJ6bBg8TO7BN4NuQ==}
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-query@4.29.5(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-F87cibC3s3eG0Q90g2O+hqntpCrudKFnR8P24qkH9uccEhXErnJxBC/AAI4cJRV2bfMO8IeGZQYf3WyYgmSg0w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
react-native: '*'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@tanstack/query-core': 4.29.5
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
use-sync-external-store: 1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@testing-library/dom@8.20.0:
|
||||
resolution: {integrity: sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -3742,6 +3770,22 @@ packages:
|
||||
'@trpc/server': 10.20.0
|
||||
dev: false
|
||||
|
||||
/@trpc/react-query@10.23.0(@tanstack/react-query@4.29.5)(@trpc/client@10.20.0)(@trpc/server@10.20.0)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-50vfR3HtOEcjCQMuHSGTFOG6W9WunvcI8msTp84UNAqZ9dz4YdjgBVNn2wxqB5LAQ6AGlcKuzJWjEj3yO56tvw==}
|
||||
peerDependencies:
|
||||
'@tanstack/react-query': ^4.18.0
|
||||
'@trpc/client': 10.23.0
|
||||
'@trpc/server': 10.23.0
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
'@tanstack/react-query': 4.29.5(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/client': 10.20.0(@trpc/server@10.20.0)
|
||||
'@trpc/server': 10.20.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@trpc/server@10.20.0:
|
||||
resolution: {integrity: sha512-pbrdgw2mO8lWygyZjGLbOq/EoWFWEJvLaxp8JmGsivMIh/IZR5mksPJdywAGQ4TBrbcqssKhuoJ/8OE9mhaveg==}
|
||||
dev: false
|
||||
@ -12617,6 +12661,14 @@ packages:
|
||||
tslib: 2.5.0
|
||||
dev: false
|
||||
|
||||
/use-sync-external-store@1.2.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/use@3.1.1:
|
||||
resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
Loading…
Reference in New Issue
Block a user