diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 73f837f..902bb08 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -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", diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index ca500e6..a5bf509 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -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 = () => { }/> Not Found

}/>
+ }/> }/> diff --git a/packages/frontend/src/init.tsx b/packages/frontend/src/init.tsx index 7265740..109da62 100644 --- a/packages/frontend/src/init.tsx +++ b/packages/frontend/src/init.tsx @@ -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 ? () : ( - - - + + + + + )); } diff --git a/packages/frontend/src/libs/trpc.tsx b/packages/frontend/src/libs/trpc.tsx new file mode 100644 index 0000000..558a334 --- /dev/null +++ b/packages/frontend/src/libs/trpc.tsx @@ -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({ + links: [ link ], +}); + +export const trpc = createTRPCReact(); + +const providerInit = { + client: trpc.createClient(init), + queryClient: new QueryClient(), +}; + +export const TRPCProvider: React.FC = ({ children }) => { + return ( + + + {children} + + + ); +}; diff --git a/packages/frontend/src/pages/announcements.tsx b/packages/frontend/src/pages/announcements.tsx new file mode 100644 index 0000000..cff11f5 --- /dev/null +++ b/packages/frontend/src/pages/announcements.tsx @@ -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 ( + +
+

+ {announcement.title}{' '} +

+ +   + {dayjs(announcement.createdAt).locale(language.split('_')[0]).fromNow()} + +
+ {announcement.body} +
+
+
+ ); +}; + +export default AnnouncementsPage; diff --git a/packages/frontend/src/pages/settings/index.tsx b/packages/frontend/src/pages/settings/index.tsx index deab86c..eade418 100644 --- a/packages/frontend/src/pages/settings/index.tsx +++ b/packages/frontend/src/pages/settings/index.tsx @@ -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(); diff --git a/packages/frontend/src/store/api/account.ts b/packages/frontend/src/store/api/account.ts index df192d6..45d8538 100644 --- a/packages/frontend/src/store/api/account.ts +++ b/packages/frontend/src/store/api/account.ts @@ -1,4 +1,4 @@ -import { trpcJotai } from '@/store/api'; +import { trpcJotai } from '@/libs/trpc'; export const accountAtom = trpcJotai.account.getMyself.atomWithQuery(); diff --git a/packages/frontend/src/store/api/announcements.ts b/packages/frontend/src/store/api/announcements.ts deleted file mode 100644 index a0edf1a..0000000 --- a/packages/frontend/src/store/api/announcements.ts +++ /dev/null @@ -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(); diff --git a/packages/frontend/src/store/api/index.ts b/packages/frontend/src/store/api/index.ts deleted file mode 100644 index c0a3c28..0000000 --- a/packages/frontend/src/store/api/index.ts +++ /dev/null @@ -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({ - links: [ link ], -}); diff --git a/packages/frontend/src/store/api/meta.ts b/packages/frontend/src/store/api/meta.ts index f171e8a..5a7847e 100644 --- a/packages/frontend/src/store/api/meta.ts +++ b/packages/frontend/src/store/api/meta.ts @@ -1,3 +1,3 @@ -import { trpcJotai } from '.'; +import { trpcJotai } from '@/libs/trpc'; export const metaAtom = trpcJotai.meta.get.atomWithQuery(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a430996..3a72ee7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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'}