diff --git a/frontend/.vscode/tasks.json b/frontend/.vscode/tasks.json index 305fb5bf38..8bce9eb24b 100644 --- a/frontend/.vscode/tasks.json +++ b/frontend/.vscode/tasks.json @@ -257,7 +257,7 @@ "label": "AF: Tauri UI Dev", "type": "shell", "isBackground": true, - "command": "pnpm sync:i18n && pnpm run dev", + "command": "pnpm run tauri:dev", "options": { "cwd": "${workspaceFolder}/appflowy_tauri" } @@ -297,6 +297,6 @@ "options": { "cwd": "${workspaceFolder}/appflowy_flutter" } - }, + } ] } \ No newline at end of file diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index b1e6f8e53d..4cc3b04a1b 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -51,7 +51,7 @@ "quill-delta": "^5.1.0", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", - "react-calendar": "^4.1.0", + "react-big-calendar": "^1.8.5", "react-color": "^2.19.3", "react-datepicker": "^4.23.0", "react-dom": "^18.2.0", diff --git a/frontend/appflowy_tauri/pnpm-lock.yaml b/frontend/appflowy_tauri/pnpm-lock.yaml index cc90341993..18b6eedbb7 100644 --- a/frontend/appflowy_tauri/pnpm-lock.yaml +++ b/frontend/appflowy_tauri/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - dependencies: '@emoji-mart/data': specifier: ^1.1.2 @@ -104,9 +100,9 @@ dependencies: react-beautiful-dnd: specifier: ^13.1.1 version: 13.1.1(react-dom@18.2.0)(react@18.2.0) - react-calendar: - specifier: ^4.1.0 - version: 4.2.1(react-dom@18.2.0)(react@18.2.0) + react-big-calendar: + specifier: ^1.8.5 + version: 1.8.5(react-dom@18.2.0)(react@18.2.0) react-color: specifier: ^2.19.3 version: 2.19.3(react@18.2.0) @@ -1882,6 +1878,15 @@ packages: engines: {node: '>=14'} dev: false + /@restart/hooks@0.4.15(react@18.2.0): + resolution: {integrity: sha512-cZFXYTxbpzYcieq/mBwSyXgqnGMHoBVh3J7MU0CCoIB4NRZxV9/TuwTBAaLMqpNhC3zTPMCgkQ5Ey07L02Xmcw==} + peerDependencies: + react: '>=16.8.0' + dependencies: + dequal: 2.0.3 + react: 18.2.0 + dev: false + /@rollup/pluginutils@5.0.2: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} @@ -2302,14 +2307,9 @@ packages: '@types/lodash': 4.14.194 dev: true - /@types/lodash.memoize@4.1.7: - resolution: {integrity: sha512-lGN7WeO4vO6sICVpf041Q7BX/9k1Y24Zo3FY0aUezr1QlKznpjzsDk3T3wvH8ofYzoK0QupN9TWcFAFZlyPwQQ==} - dependencies: - '@types/lodash': 4.14.194 - dev: false - /@types/lodash@4.14.194: resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==} + dev: true /@types/lodash@4.14.202: resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==} @@ -2477,6 +2477,10 @@ packages: resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} dev: true + /@types/warning@3.0.3: + resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} + dev: false + /@types/yargs-parser@21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -2631,10 +2635,6 @@ packages: - supports-color dev: true - /@wojtekmaj/date-utils@1.1.3: - resolution: {integrity: sha512-rHrDuTl1cx5LYo8F4K4HVauVjwzx4LwrKfEk4br4fj4nK8JjJZ8IG6a6pBHkYmPLBQHCOEDwstb0WNXMGsmdOw==} - dev: false - /abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true @@ -3232,6 +3232,10 @@ packages: whatwg-url: 11.0.0 dev: true + /date-arithmetic@4.1.0: + resolution: {integrity: sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==} + dev: false + /date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -3291,6 +3295,11 @@ packages: engines: {node: '>=0.4.0'} dev: true + /dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + dev: false + /derive-valtio@0.1.0(valtio@1.12.1): resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} peerDependencies: @@ -3916,13 +3925,6 @@ packages: get-intrinsic: 1.2.1 dev: true - /get-user-locale@2.2.1: - resolution: {integrity: sha512-3814zipTZ2MvczOcppEXB3jXu+0HWwj5WmPI6//SeCnUIUaRXu7W4S54eQZTEPadlMZefE+jAlPOn+zY3tD4Qw==} - dependencies: - '@types/lodash.memoize': 4.1.7 - lodash.memoize: 4.1.2 - dev: false - /glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -3958,6 +3960,10 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 + /globalize@0.1.1: + resolution: {integrity: sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==} + dev: false + /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -4175,6 +4181,12 @@ packages: side-channel: 1.0.4 dev: true + /invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + dependencies: + loose-envify: 1.4.0 + dev: false + /is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -5039,6 +5051,7 @@ packages: /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true /lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -5070,6 +5083,11 @@ packages: dependencies: yallist: 4.0.0 + /luxon@3.4.4: + resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} + engines: {node: '>=12'} + dev: false + /magic-string@0.27.0: resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} engines: {node: '>=12'} @@ -5108,6 +5126,10 @@ packages: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} dev: false + /memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + dev: false + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -5154,6 +5176,16 @@ packages: hasBin: true dev: true + /moment-timezone@0.5.44: + resolution: {integrity: sha512-nv3YpzI/8lkQn0U6RkLd+f0W/zy/JnoR5/EyPz/dNkPTBjA2jNLCVxaiQ8QpeLymhSZvX0wCL5s27NQWdOPwAw==} + dependencies: + moment: 2.30.1 + dev: false + + /moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -5692,19 +5724,30 @@ packages: - react-native dev: false - /react-calendar@4.2.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-T5oKXD+KLy/g6bmJJkZ7E9wj0iRMesWMZcrC7q2kI6ybOsu9NlPQx8uXJzG4A4C3Sh5Xi0deznyzWIVsUpF8tA==} + /react-big-calendar@1.8.5(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-cra8WPfoTSQthFfqxi0k9xm/Shv5jWSw19LkNzpSJcnQhP6XGes/eJjd8P8g/iwaJjXIWPpg3+HB5wO5wabRyA==} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.14.0 || ^17 || ^18 + react-dom: ^16.14.0 || ^17 || ^18 dependencies: - '@types/react': 18.2.6 - '@wojtekmaj/date-utils': 1.1.3 + '@babel/runtime': 7.23.4 clsx: 1.2.1 - get-user-locale: 2.2.1 + date-arithmetic: 4.1.0 + dayjs: 1.11.9 + dom-helpers: 5.2.1 + globalize: 0.1.1 + invariant: 2.2.4 + lodash: 4.17.21 + lodash-es: 4.17.21 + luxon: 3.4.4 + memoize-one: 6.0.0 + moment: 2.30.1 + moment-timezone: 0.5.44 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) + react-overlays: 5.2.1(react-dom@18.2.0)(react@18.2.0) + uncontrollable: 7.2.1(react@18.2.0) dev: false /react-color@2.19.3(react@18.2.0): @@ -5826,6 +5869,10 @@ packages: react: 18.2.0 dev: false + /react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + dev: false + /react-onclickoutside@6.13.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==} peerDependencies: @@ -5836,6 +5883,24 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /react-overlays@5.2.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==} + peerDependencies: + react: '>=16.3.0' + react-dom: '>=16.3.0' + dependencies: + '@babel/runtime': 7.23.4 + '@popperjs/core': 2.11.8 + '@restart/hooks': 0.4.15(react@18.2.0) + '@types/warning': 3.0.3 + dom-helpers: 5.2.1 + prop-types: 15.8.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + uncontrollable: 7.2.1(react@18.2.0) + warning: 4.0.3 + dev: false + /react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} peerDependencies: @@ -6780,6 +6845,18 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /uncontrollable@7.2.1(react@18.2.0): + resolution: {integrity: sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==} + peerDependencies: + react: '>=15.0.0' + dependencies: + '@babel/runtime': 7.23.4 + '@types/react': 18.2.6 + invariant: 2.2.4 + react: 18.2.0 + react-lifecycles-compat: 3.0.4 + dev: false + /universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -7119,3 +7196,7 @@ packages: /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts index ea052d869a..cd61b2108d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/AppMain.hooks.ts @@ -1,31 +1,39 @@ import { useAppDispatch, useAppSelector } from '$app/stores/store'; import { useCallback, useEffect, useMemo } from 'react'; -import { UserSettingController } from '$app/stores/effects/user/user_setting_controller'; import { currentUserActions } from '$app_reducers/current-user/slice'; import { Theme as ThemeType, ThemeMode } from '$app/stores/reducers/current-user/slice'; import { createTheme } from '@mui/material/styles'; import { getDesignTokens } from '$app/utils/mui'; import { useTranslation } from 'react-i18next'; +import { ThemeModePB } from '@/services/backend'; +import { UserService } from '$app/application/user/user.service'; export function useUserSetting() { const dispatch = useAppDispatch(); const { i18n } = useTranslation(); - const currentUser = useAppSelector((state) => state.currentUser); - const userSettingController = useMemo(() => { - if (!currentUser?.id) return; - const controller = new UserSettingController(currentUser.id); - return controller; - }, [currentUser?.id]); + const handleSystemThemeChange = useCallback(() => { + const mode = window.matchMedia('(prefers-color-scheme: dark)').matches ? ThemeMode.Dark : ThemeMode.Light; + + dispatch(currentUserActions.setUserSetting({ themeMode: mode })); + }, [dispatch]); const loadUserSetting = useCallback(async () => { - if (!userSettingController) return; - const settings = await userSettingController.getAppearanceSetting(); + const settings = await UserService.getAppearanceSetting(); if (!settings) return; dispatch(currentUserActions.setUserSetting(settings)); + + if (settings.themeMode === ThemeModePB.System) { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + + handleSystemThemeChange(); + + mediaQuery.addEventListener('change', handleSystemThemeChange); + } + await i18n.changeLanguage(settings.language); - }, [dispatch, i18n, userSettingController]); + }, [dispatch, handleSystemThemeChange, i18n]); useEffect(() => { void loadUserSetting(); @@ -35,12 +43,26 @@ export function useUserSetting() { return state.currentUser.userSetting || {}; }); + useEffect(() => { + const html = document.documentElement; + + html?.setAttribute('data-dark-mode', String(themeMode === ThemeMode.Dark)); + html?.setAttribute('data-theme', themeType); + }, [themeType, themeMode]); + + useEffect(() => { + return () => { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + + mediaQuery.removeEventListener('change', handleSystemThemeChange); + }; + }, [dispatch, handleSystemThemeChange]); + const muiTheme = useMemo(() => createTheme(getDesignTokens(themeMode)), [themeMode]); return { muiTheme, themeMode, themeType, - userSettingController, }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx b/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx index f5e5359888..c418791461 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/AppMain.tsx @@ -1,44 +1,26 @@ import React from 'react'; import { Route, Routes } from 'react-router-dom'; import { ProtectedRoutes } from '$app/components/auth/ProtectedRoutes'; -import { AllIcons } from '$app/components/tests/AllIcons'; -import { ColorPalette } from '$app/components/tests/ColorPalette'; -import { TestAPI } from '$app/components/tests/TestAPI'; -import { BoardPage } from '$app/views/BoardPage'; import { DatabasePage } from '$app/views/DatabasePage'; -import { LoginPage } from '$app/views/LoginPage'; -import { GetStarted } from '$app/components/auth/GetStarted/GetStarted'; -import { SignUpPage } from '$app/views/SignUpPage'; -import { ConfirmAccountPage } from '$app/views/ConfirmAccountPage'; + import { ThemeProvider } from '@mui/material'; import { useUserSetting } from '$app/AppMain.hooks'; -import { UserSettingControllerContext } from '$app/components/_shared/app-hooks/useUserSettingControllerContext'; import TrashPage from '$app/views/TrashPage'; import DocumentPage from '$app/views/DocumentPage'; function AppMain() { - const { muiTheme, userSettingController } = useUserSetting(); + const { muiTheme } = useUserSetting(); return ( - <UserSettingControllerContext.Provider value={userSettingController}> - <ThemeProvider theme={muiTheme}> - <Routes> - <Route path={'/'} element={<ProtectedRoutes />}> - <Route path={'/page/all-icons'} element={<AllIcons />} /> - <Route path={'/page/colors'} element={<ColorPalette />} /> - <Route path={'/page/api-test'} element={<TestAPI />} /> - <Route path={'/page/document/:id'} element={<DocumentPage />} /> - <Route path={'/page/board/:id'} element={<BoardPage />} /> - <Route path={'/page/grid/:id'} element={<DatabasePage />} /> - <Route path={'/trash'} id={'trash'} element={<TrashPage />} /> - </Route> - <Route path={'/auth/login'} element={<LoginPage />}></Route> - <Route path={'/auth/getStarted'} element={<GetStarted />}></Route> - <Route path={'/auth/signUp'} element={<SignUpPage />}></Route> - <Route path={'/auth/confirm-account'} element={<ConfirmAccountPage />}></Route> - </Routes> - </ThemeProvider> - </UserSettingControllerContext.Provider> + <ThemeProvider theme={muiTheme}> + <Routes> + <Route path={'/'} element={<ProtectedRoutes />}> + <Route path={'/page/document/:id'} element={<DocumentPage />} /> + <Route path={'/page/grid/:id'} element={<DatabasePage />} /> + <Route path={'/trash'} id={'trash'} element={<TrashPage />} /> + </Route> + </Routes> + </ThemeProvider> ); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts similarity index 94% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_listeners.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts index 0bdfcba241..c5c94daebc 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_listeners.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_listeners.ts @@ -1,4 +1,4 @@ -import { Database } from '$app/components/database/application'; +import { Database } from '$app/application/database'; import { getCell } from './cell_service'; export function didDeleteCells({ database, rowId, fieldId }: { database: Database; rowId?: string; fieldId?: string }) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts similarity index 98% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts index 16cd8a1b64..c1ca8d7583 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_service.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_service.ts @@ -5,7 +5,7 @@ import { ChecklistCellDataChangesetPB, DateChangesetPB, FieldType, -} from '@/services/backend'; +} from '../../../../services/backend'; import { DatabaseEventGetCell, DatabaseEventUpdateCell, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts similarity index 95% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts index b8cc81b20b..4633e2b2fe 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/cell_types.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/cell_types.ts @@ -6,11 +6,8 @@ import { SelectOptionCellDataPB, TimestampCellDataPB, URLCellDataPB, -} from '@/services/backend'; -import { - SelectOption, - pbToSelectOption, -} from '$app/components/database/application/field/select_option/select_option_types'; +} from '../../../../services/backend'; +import { SelectOption, pbToSelectOption } from '../field/select_option/select_option_types'; export interface Cell { rowId: string; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/cell/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/cell/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/cell/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/database/database_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/database/database_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/database/database_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/database/database_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/database/database_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/database/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/database/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/database_view/database_view_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/database_view_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/database_view/database_view_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/database_view_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/database_view/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/database_view/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/database_view/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts similarity index 87% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_listeners.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts index 426cfd6d48..ef36daa20c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_listeners.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_listeners.ts @@ -1,6 +1,6 @@ import { DatabaseFieldChangesetPB, FieldSettingsPB, FieldVisibility } from '@/services/backend'; -import { Database, fieldService } from '$app/components/database/application'; -import { didDeleteCells, didUpdateCells } from '$app/components/database/application/cell/cell_listeners'; +import { Database, fieldService } from '$app/application/database'; +import { didDeleteCells, didUpdateCells } from '$app/application/database/cell/cell_listeners'; export function didUpdateFieldSettings(database: Database, settings: FieldSettingsPB) { const { field_id: fieldId, visibility, width } = settings; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts similarity index 98% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts index dff4ee8e09..219aeb3ea5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_service.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_service.ts @@ -26,7 +26,7 @@ import { } from '@/services/backend/events/flowy-database2'; import { Field, pbToField } from './field_types'; import { bytesToTypeOption } from './type_option'; -import { Database } from '$app/components/database/application'; +import { Database } from '$app/application/database'; export async function getFields( viewId: string, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/field_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/field_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/select_option/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/select_option/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/select_option/select_option_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/select_option/select_option_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/select_option/select_option_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/select_option/select_option_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/select_option/select_option_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/type_option/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/type_option/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/type_option/type_option_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/type_option/type_option_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/type_option/type_option_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/field/type_option/type_option_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/field/type_option/type_option_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_data.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts similarity index 91% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_data.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts index 572d8187f1..5d9c9f9be0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_data.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_data.ts @@ -5,7 +5,7 @@ import { NumberFilterConditionPB, TextFilterConditionPB, } from '@/services/backend'; -import { UndeterminedFilter } from '$app/components/database/application'; +import { UndeterminedFilter } from '$app/application/database'; export function getDefaultFilter(fieldType: FieldType): UndeterminedFilter['data'] | undefined { switch (fieldType) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts similarity index 93% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_listeners.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts index 05618ca6b9..9a0cd46b2c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_listeners.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_listeners.ts @@ -1,4 +1,4 @@ -import { Database, pbToFilter } from '$app/components/database/application'; +import { Database, pbToFilter } from '$app/application/database'; import { FilterChangesetNotificationPB } from '@/services/backend'; const deleteFiltersFromChange = (database: Database, changeset: FilterChangesetNotificationPB) => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/filter_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/filter/filter_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/filter/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/filter/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/filter/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/group/group_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/group/group_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/group/group_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/group/group_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/group/group_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/group/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/group/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/group/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/group/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/row/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts similarity index 96% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_listeners.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts index a1f975409e..acee8d141b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_listeners.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_listeners.ts @@ -1,7 +1,7 @@ import { ReorderAllRowsPB, ReorderSingleRowPB, RowsChangePB, RowsVisibilityChangePB } from '@/services/backend'; import { Database } from '../database'; import { pbToRowMeta, RowMeta } from './row_types'; -import { didDeleteCells } from '$app/components/database/application/cell/cell_listeners'; +import { didDeleteCells } from '$app/application/database/cell/cell_listeners'; const deleteRowsFromChangeset = (database: Database, changeset: RowsChangePB) => { changeset.deleted_rows.forEach((rowId) => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/row/row_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/row/row_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/index.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/sort/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/sort_listeners.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_listeners.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/sort_listeners.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_listeners.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/sort_service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_service.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/sort_service.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_service.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/sort_types.ts b/frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_types.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/application/sort/sort_types.ts rename to frontend/appflowy_tauri/src/appflowy_app/application/database/sort/sort_types.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts new file mode 100644 index 0000000000..b529571c91 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/application/folder/page.service.ts @@ -0,0 +1,152 @@ +import { Page, PageIcon, parserViewPBToPage } from '$app_reducers/pages/slice'; +import { + CreateOrphanViewPayloadPB, + CreateViewPayloadPB, + MoveNestedViewPayloadPB, + RepeatedViewIdPB, + UpdateViewIconPayloadPB, + UpdateViewPayloadPB, + ViewIconPB, + ViewIdPB, + ViewPB, +} from '@/services/backend'; +import { + FolderEventCreateOrphanView, + FolderEventCreateView, + FolderEventDeleteView, + FolderEventDuplicateView, + FolderEventGetView, + FolderEventMoveNestedView, + FolderEventUpdateView, + FolderEventUpdateViewIcon, +} from '@/services/backend/events/flowy-folder'; + +export async function getPage(id: string) { + const payload = new ViewIdPB({ + value: id, + }); + + const result = await FolderEventGetView(payload); + + if (result.ok) { + return parserViewPBToPage(result.val); + } + + return Promise.reject(result.val); +} + +export const createOrphanPage = async ( + params: ReturnType<typeof CreateOrphanViewPayloadPB.prototype.toObject> +): Promise<Page> => { + const payload = CreateOrphanViewPayloadPB.fromObject(params); + + const result = await FolderEventCreateOrphanView(payload); + + if (result.ok) { + return parserViewPBToPage(result.val); + } + + return Promise.reject(result.val); +}; + +export const duplicatePage = async (id: string) => { + const page = await getPage(id); + const payload = ViewPB.fromObject(page); + + const result = await FolderEventDuplicateView(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +}; + +export const deletePage = async (id: string) => { + const payload = new RepeatedViewIdPB({ + items: [id], + }); + + const result = await FolderEventDeleteView(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +}; + +export const createPage = async (params: ReturnType<typeof CreateViewPayloadPB.prototype.toObject>): Promise<string> => { + const payload = CreateViewPayloadPB.fromObject(params); + + const result = await FolderEventCreateView(payload); + + if (result.ok) { + return result.val.id; + } + + return Promise.reject(result.err); +}; + +export const movePage = async (params: ReturnType<typeof MoveNestedViewPayloadPB.prototype.toObject>) => { + const payload = new MoveNestedViewPayloadPB(params); + + const result = await FolderEventMoveNestedView(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +}; + +export const getChildPages = async (id: string): Promise<Page[]> => { + const payload = new ViewIdPB({ + value: id, + }); + + const result = await FolderEventGetView(payload); + + if (result.ok) { + return result.val.child_views.map(parserViewPBToPage); + } + + return []; +}; + +export const updatePage = async (page: { id: string } & Partial<Page>) => { + const payload = new UpdateViewPayloadPB(); + + payload.view_id = page.id; + if (page.name !== undefined) { + payload.name = page.name; + } + + const result = await FolderEventUpdateView(payload); + + if (result.ok) { + return result.val.toObject(); + } + + return Promise.reject(result.err); +}; + +export const updatePageIcon = async (viewId: string, icon?: PageIcon) => { + const payload = new UpdateViewIconPayloadPB({ + view_id: viewId, + icon: icon + ? new ViewIconPB({ + ty: icon.ty, + value: icon.value, + }) + : undefined, + }); + + const result = await FolderEventUpdateViewIcon(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts new file mode 100644 index 0000000000..dfbe742ca0 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/application/folder/trash.service.ts @@ -0,0 +1,68 @@ +import { + FolderEventListTrashItems, + FolderEventPermanentlyDeleteAllTrashItem, + FolderEventPermanentlyDeleteTrashItem, + FolderEventRecoverAllTrashItems, + FolderEventRestoreTrashItem, + RepeatedTrashIdPB, + TrashIdPB, +} from '@/services/backend/events/flowy-folder'; + +export const getTrash = async () => { + const res = await FolderEventListTrashItems(); + + if (res.ok) { + return res.val.items; + } + + return []; +}; + +export const putback = async (id: string) => { + const payload = new TrashIdPB({ + id, + }); + + const res = await FolderEventRestoreTrashItem(payload); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); +}; + +export const deleteTrashItem = async (ids: string[]) => { + const items = ids.map((id) => new TrashIdPB({ id })); + const payload = new RepeatedTrashIdPB({ + items, + }); + + const res = await FolderEventPermanentlyDeleteTrashItem(payload); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); +}; + +export const deleteAll = async () => { + const res = await FolderEventPermanentlyDeleteAllTrashItem(); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); +}; + +export const restoreAll = async () => { + const res = await FolderEventRecoverAllTrashItems(); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts new file mode 100644 index 0000000000..0a1ac683af --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/application/folder/workspace.service.ts @@ -0,0 +1,110 @@ +import { CreateViewPayloadPB, UserWorkspaceIdPB, WorkspaceIdPB } from '@/services/backend'; +import { UserEventOpenWorkspace } from '@/services/backend/events/flowy-user'; +import { + FolderEventCreateView, + FolderEventDeleteWorkspace, + FolderEventGetCurrentWorkspaceSetting, + FolderEventReadCurrentWorkspace, + FolderEventReadWorkspaceViews, +} from '@/services/backend/events/flowy-folder'; +import { parserViewPBToPage } from '$app_reducers/pages/slice'; + +export async function openWorkspace(id: string) { + const payload = new UserWorkspaceIdPB({ + workspace_id: id, + }); + + const result = await UserEventOpenWorkspace(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +} + +export async function deleteWorkspace(id: string) { + const payload = new WorkspaceIdPB({ + value: id, + }); + + const result = await FolderEventDeleteWorkspace(payload); + + if (result.ok) { + return result.val; + } + + return Promise.reject(result.err); +} + +export async function getWorkspaceChildViews(id: string) { + const payload = new WorkspaceIdPB({ + value: id, + }); + + const result = await FolderEventReadWorkspaceViews(payload); + + if (result.ok) { + return result.val.items.map(parserViewPBToPage); + } + + return []; +} + +export async function getWorkspaces() { + const result = await FolderEventReadCurrentWorkspace(); + + if (result.ok) { + const item = result.val; + + return [ + { + id: item.id, + name: item.name, + }, + ]; + } + + return []; +} + +export async function getCurrentWorkspaceSetting() { + const res = await FolderEventGetCurrentWorkspaceSetting(); + + if (res.ok) { + return res.val; + } + + return; +} + +export async function getCurrentWorkspace() { + const result = await FolderEventReadCurrentWorkspace(); + + if (result.ok) { + const workspace = result.val; + + return { + id: workspace.id, + name: workspace.name, + }; + } + + return null; +} + +export async function createCurrentWorkspaceChildView( + params: ReturnType<typeof CreateViewPayloadPB.prototype.toObject> +) { + const payload = CreateViewPayloadPB.fromObject(params); + + const result = await FolderEventCreateView(payload); + + if (result.ok) { + const view = result.val; + + return view; + } + + return Promise.reject(result.err); +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts b/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts index d76c6c8db9..726bfabaec 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/application/notification.ts @@ -15,6 +15,13 @@ import { RowsChangePB, RowsVisibilityChangePB, SortChangesetNotificationPB, + UserNotification, + UserProfilePB, + FolderNotification, + RepeatedViewPB, + ViewPB, + RepeatedTrashPB, + ChildViewUpdatePB, } from '@/services/backend'; const Notification = { @@ -32,6 +39,11 @@ const Notification = { [DatabaseNotification.DidUpdateFieldSettings]: FieldSettingsPB, [DatabaseNotification.DidUpdateFilter]: FilterChangesetNotificationPB, [DocumentNotification.DidReceiveUpdate]: DocEventPB, + [UserNotification.DidUpdateUserProfile]: UserProfilePB, + [FolderNotification.DidUpdateWorkspaceViews]: RepeatedViewPB, + [FolderNotification.DidUpdateView]: ViewPB, + [FolderNotification.DidUpdateChildViews]: ChildViewUpdatePB, + [FolderNotification.DidUpdateTrash]: RepeatedTrashPB, }; type NotificationMap = typeof Notification; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts new file mode 100644 index 0000000000..82c0a6779b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/application/user/auth.service.ts @@ -0,0 +1,46 @@ +import { SignInPayloadPB, SignUpPayloadPB } from '@/services/backend'; +import { + UserEventSignInWithEmailPassword, + UserEventSignOut, + UserEventSignUp, +} from '@/services/backend/events/flowy-user'; +import { nanoid } from '@reduxjs/toolkit'; +import { Log } from '$app/utils/log'; + +export const AuthService = { + signIn: async (params: { email: string; password: string }) => { + const payload = SignInPayloadPB.fromObject({ email: params.email, password: params.password }); + + const res = await UserEventSignInWithEmailPassword(payload); + + if (res.ok) { + return res.val; + } + + Log.error(res.val.msg); + throw new Error(res.val.msg); + }, + + signUp: async (params: { name: string; email: string; password: string }) => { + const deviceId = nanoid(8); + const payload = SignUpPayloadPB.fromObject({ + name: params.name, + email: params.email, + password: params.password, + device_id: deviceId, + }); + + const res = await UserEventSignUp(payload); + + if (!res.ok) { + Log.error(res.val.msg); + throw new Error(res.val.msg); + } + + return res.val; + }, + + signOut: () => { + return UserEventSignOut(); + }, +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts b/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts new file mode 100644 index 0000000000..f91c39cb71 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/application/user/user.service.ts @@ -0,0 +1,55 @@ +import { Theme, ThemeMode, UserSetting } from '$app_reducers/current-user/slice'; +import { AppearanceSettingsPB } from '@/services/backend'; +import { + UserEventGetAppearanceSetting, + UserEventGetUserProfile, + UserEventSetAppearanceSetting, +} from '@/services/backend/events/flowy-user'; + +export const UserService = { + getAppearanceSetting: async (): Promise<Partial<UserSetting> | undefined> => { + const appearanceSetting = await UserEventGetAppearanceSetting(); + + if (appearanceSetting.ok) { + const res = appearanceSetting.val; + const { locale, theme = Theme.Default, theme_mode = ThemeMode.Light } = res; + let language = 'en'; + + if (locale.language_code && locale.country_code) { + language = `${locale.language_code}-${locale.country_code}`; + } else if (locale.language_code) { + language = locale.language_code; + } + + return { + themeMode: theme_mode, + theme: theme as Theme, + language: language, + }; + } + + return; + }, + + setAppearanceSetting: async (params: ReturnType<typeof AppearanceSettingsPB.prototype.toObject>) => { + const payload = AppearanceSettingsPB.fromObject(params); + + const res = await UserEventSetAppearanceSetting(payload); + + if (res.ok) { + return res.val; + } + + return Promise.reject(res.err); + }, + + getUserProfile: async () => { + const res = await UserEventGetUserProfile(); + + if (res.ok) { + return res.val; + } + + return; + }, +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/Icons 16/Page.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/Icons 16/Page.svg deleted file mode 100644 index f0fb8ce9ce..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/assets/Icons 16/Page.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M9 2.5V6C9 6.55228 9.44772 7 10 7H12" stroke="#333333"/> -<path d="M3.5 3.5C3.5 2.94771 3.94772 2.5 4.5 2.5H8H8.5C9.12951 2.5 9.72229 2.79639 10.1 3.3L12.1 5.96667C12.3596 6.31286 12.5 6.73393 12.5 7.16667V8V12.5C12.5 13.0523 12.0523 13.5 11.5 13.5H4.5C3.94772 13.5 3.5 13.0523 3.5 12.5V3.5Z" stroke="#333333"/> -</svg> diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx deleted file mode 100644 index e455831e74..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Button.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { MouseEventHandler, MouseEvent, ReactNode, useEffect, useState } from 'react'; - -export const Button = ({ - size = 'primary', - children, - onClick, -}: { - size?: 'primary' | 'medium' | 'small' | 'box-small-transparent' | 'medium-transparent'; - children: ReactNode; - onClick?: MouseEventHandler<HTMLButtonElement>; -}) => { - const [cls, setCls] = useState(''); - - useEffect(() => { - switch (size) { - case 'primary': - setCls('w-[340px] h-[48px] flex items-center justify-center rounded-lg bg-fill-default text-content-on-fill'); - break; - case 'medium': - setCls('w-[170px] h-[48px] flex items-center justify-center rounded-lg bg-fill-default text-content-on-fill'); - break; - case 'small': - setCls( - 'w-[68px] h-[32px] flex items-center justify-center rounded-lg bg-fill-default text-content-on-fill text-xs hover:bg-fill-list-hover' - ); - break; - case 'medium-transparent': - setCls( - 'w-[170px] h-[48px] flex items-center justify-center rounded-lg border border-fill-default text-fill-default transition-colors duration-300 hover:bg-content-blue-50 ' - ); - break; - case 'box-small-transparent': - setCls('text-icon-default w-[24px] h-[24px] rounded hover:bg-fill-list-hover'); - break; - } - }, [size]); - - const handleClick = (e: MouseEvent<HTMLButtonElement>) => { - e.stopPropagation(); - onClick && onClick(e); - }; - - return ( - <button className={cls} onClick={(e) => handleClick(e)}> - {children} - </button> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/CheckListProgress.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/CheckListProgress.tsx deleted file mode 100644 index 6f483bd4d2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/CheckListProgress.tsx +++ /dev/null @@ -1,27 +0,0 @@ -export const CheckListProgress = ({ completed, max }: { completed: number; max: number }) => { - return ( - <div className={'flex w-full items-center gap-4 py-1'}> - {max > 0 && ( - <> - <div className={'flex flex-1 gap-1'}> - {completed > 0 && filledCheckListBars({ amount: completed })} - {max - completed > 0 && emptyCheckListBars({ amount: max - completed })} - </div> - <div className={'text-xs text-text-caption'}>{((100 * completed) / max).toFixed(0)}%</div> - </> - )} - </div> - ); -}; - -const filledCheckListBars = ({ amount }: { amount: number }) => { - return Array(amount) - .fill(0) - .map((item, index) => <div key={index} className={'h-[4px] flex-1 flex-shrink-0 rounded bg-fill-hover'}></div>); -}; - -const emptyCheckListBars = ({ amount }: { amount: number }) => { - return Array(amount) - .fill(0) - .map((item, index) => <div key={index} className={'bg-tint-9 h-[4px] flex-1 flex-shrink-0 rounded'}></div>); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Database.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Database.hooks.ts deleted file mode 100644 index 9b0955793e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Database.hooks.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useAppSelector } from '$app/stores/store'; - -export const useDatabase = () => { - const database = useAppSelector((state) => state.database); - - const newField = () => { - /* dispatch( - databaseActions.addField({ - field: { - fieldId: nanoid(8), - fieldType: FieldType.RichText, - fieldOptions: {}, - title: 'new field', - }, - }) - );*/ - console.log('depreciated'); - }; - - const renameField = (_fieldId: string, _newTitle: string) => { - /* const field = database.fields[fieldId]; - field.title = newTitle; - - dispatch( - databaseActions.updateField({ - field, - }) - );*/ - console.log('depreciated'); - }; - - const newRow = () => { - // dispatch(databaseActions.addRow()); - console.log('depreciated'); - }; - - return { - database, - newField, - renameField, - newRow, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterItem.tsx deleted file mode 100644 index b0b7d41322..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterItem.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { FieldType } from '@/services/backend'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { - IDatabaseFilter, - ISelectOption, - SupportedOperatorsByType, - TDatabaseOperators, -} from '$app_reducers/database/slice'; -import { useAppSelector } from '$app/stores/store'; -import React, { useEffect, useMemo, useState } from 'react'; -import { FieldSelect } from '$app/components/_shared/DatabaseFilter/FieldSelect'; -import { LogicalOperatorSelect } from '$app/components/_shared/DatabaseFilter/LogicalOperatorSelect'; -import { OperatorSelect } from '$app/components/_shared/DatabaseFilter/OperatorSelect'; -import { FilterValue } from '$app/components/_shared/DatabaseFilter/FilterValue'; - -export const DatabaseFilterItem = ({ - data, - onSave, - onDelete, - index, -}: { - data: IDatabaseFilter | null; - onSave: (filter: IDatabaseFilter) => void; - onDelete?: () => void; - index: number; -}) => { - // stores - const columns = useAppSelector((state) => state.database.columns); - const fields = useAppSelector((state) => state.database.fields); - const filtersStore = useAppSelector((state) => state.database.filters); - - // values - const [currentLogicalOperator, setCurrentLogicalOperator] = useState<'and' | 'or'>('and'); - const [currentFieldId, setCurrentFieldId] = useState<string | null>(data?.fieldId ?? null); - const [currentOperator, setCurrentOperator] = useState<TDatabaseOperators | null>(data?.operator ?? null); - const [currentValue, setCurrentValue] = useState<string[] | string | boolean | null>(data?.value ?? null); - - useEffect(() => { - if (data) { - setCurrentLogicalOperator(data.logicalOperator); - setCurrentFieldId(data.fieldId); - setCurrentOperator(data.operator); - setCurrentValue(data.value); - } else { - setCurrentLogicalOperator('and'); - setCurrentFieldId(null); - setCurrentOperator(null); - setCurrentValue(null); - } - }, [data]); - - const [textInputActive, setTextInputActive] = useState(false); - - // shortcut - const currentFieldType = useMemo( - () => (currentFieldId ? fields[currentFieldId].fieldType : undefined), - [currentFieldId, fields] - ); - - useEffect(() => { - // if the user is typing in a text input, don't update the filter - if (textInputActive) return; - - if (currentFieldId && currentFieldType !== undefined && currentOperator && currentValue !== null) { - if (currentFieldType === FieldType.RichText && (currentValue as string).length === 0) { - return; - } - - onSave({ - id: data?.id, - logicalOperator: currentLogicalOperator, - fieldId: currentFieldId, - fieldType: currentFieldType, - operator: currentOperator, - value: currentValue, - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentFieldId, currentFieldType, currentOperator, currentValue, textInputActive]); - - // 1. not all field types support filtering - // 2. we don't want to show fields that are already in use - const supportedColumns = useMemo( - () => - columns - .filter((column) => SupportedOperatorsByType[fields[column.fieldId].fieldType] !== undefined) - .filter((column) => filtersStore.findIndex((filter) => filter?.fieldId === column.fieldId) === -1), - [columns, fields, filtersStore] - ); - - const onSelectFieldClick = (id: string) => { - setCurrentFieldId(id); - - switch (fields[id].fieldType) { - case FieldType.RichText: - setCurrentValue(''); - setCurrentOperator(null); - break; - case FieldType.MultiSelect: - case FieldType.SingleSelect: - setCurrentValue([]); - setCurrentOperator(null); - break; - case FieldType.Checkbox: - setCurrentOperator('is'); - setCurrentValue(false); - break; - default: - setCurrentOperator(null); - setCurrentValue(null); - } - }; - - const onSelectOperatorClick = (operator: TDatabaseOperators) => { - setCurrentOperator(operator); - }; - - const onValueOptionClick = (option: ISelectOption) => { - const value = currentValue as string[]; - - if (value.findIndex((v) => v === option.selectOptionId) === -1) { - setCurrentValue([...value, option.selectOptionId]); - } else { - setCurrentValue(value.filter((v) => v !== option.selectOptionId)); - } - }; - - return ( - <> - <div className='flex items-center gap-4'> - <div className={'w-[88px]'}> - {index === 0 ? ( - <span className={'text-sm text-text-caption'}>Where</span> - ) : ( - <LogicalOperatorSelect></LogicalOperatorSelect> - )} - </div> - - <FieldSelect - columns={supportedColumns} - fields={fields} - onSelectFieldClick={onSelectFieldClick} - currentFieldId={currentFieldId} - currentFieldType={currentFieldType} - ></FieldSelect> - - <OperatorSelect - currentOperator={currentOperator} - currentFieldType={currentFieldType} - onSelectOperatorClick={onSelectOperatorClick} - ></OperatorSelect> - - <FilterValue - currentFieldId={currentFieldId} - currentFieldType={currentFieldType} - currentValue={currentValue} - setCurrentValue={setCurrentValue} - fields={fields} - textInputActive={textInputActive} - setTextInputActive={setTextInputActive} - onValueOptionClick={onValueOptionClick} - ></FilterValue> - - <button - onClick={() => onDelete?.()} - className={`rounded p-1 hover:bg-fill-list-hover ${data ? 'opacity-100' : 'opacity-0'}`} - > - <i className={'block h-[16px] w-[16px]'}> - <TrashSvg /> - </i> - </button> - </div> - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterPopup.tsx deleted file mode 100644 index 5dbc1e50ea..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterPopup.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import { t } from 'i18next'; -import AddSvg from '../../_shared/svg/AddSvg'; -import { useAppSelector } from '$app/stores/store'; -import { MouseEventHandler, useMemo, useState } from 'react'; -import { DatabaseFilterItem } from '$app/components/_shared/DatabaseFilter/DatabaseFilterItem'; -import { IDatabaseFilter, TDatabaseOperators } from '$app_reducers/database/slice'; -import { FilterController } from '$app/stores/effects/database/filter/filter_controller'; -import { - CheckboxFilterPB, - FieldType, - SelectOptionConditionPB, - SelectOptionFilterPB, - TextFilterConditionPB, - TextFilterPB, -} from '@/services/backend'; - -export const DatabaseFilterPopup = ({ - filterController, - onOutsideClick, -}: { - filterController: FilterController; - onOutsideClick: () => void; -}) => { - // stores - const filtersStore = useAppSelector((state) => state.database.filters); - - // local copy to prevent jitter when adding new filter - const [filters, setFilters] = useState<(IDatabaseFilter | null)[]>(filtersStore); - const [showBlankFilter, setShowBlankFilter] = useState(filtersStore.length === 0); - - const onAddClick: MouseEventHandler = () => { - setShowBlankFilter(true); - }; - - const transformOperator: ( - operator: TDatabaseOperators, - type: FieldType - ) => TextFilterConditionPB | SelectOptionConditionPB = (operator, type) => { - switch (type) { - case FieldType.RichText: - switch (operator) { - case 'contains': - return TextFilterConditionPB.Contains; - case 'doesNotContain': - return TextFilterConditionPB.DoesNotContain; - case 'endsWith': - return TextFilterConditionPB.EndsWith; - case 'startWith': - return TextFilterConditionPB.StartsWith; - case 'is': - return TextFilterConditionPB.Is; - case 'isNot': - return TextFilterConditionPB.IsNot; - case 'isEmpty': - return TextFilterConditionPB.TextIsEmpty; - case 'isNotEmpty': - return TextFilterConditionPB.TextIsNotEmpty; - default: - return TextFilterConditionPB.Is; - } - - case FieldType.SingleSelect: - case FieldType.MultiSelect: - switch (operator) { - case 'is': - case 'contains': - return SelectOptionConditionPB.OptionIs; - case 'isNot': - case 'doesNotContain': - return SelectOptionConditionPB.OptionIsNot; - case 'isEmpty': - return SelectOptionConditionPB.OptionIsEmpty; - case 'isNotEmpty': - return SelectOptionConditionPB.OptionIsNotEmpty; - default: - return SelectOptionConditionPB.OptionIs; - } - - default: - return TextFilterConditionPB.Is; - } - }; - - const onSaveFilterItem = async (filter: IDatabaseFilter) => { - let val: TextFilterPB | SelectOptionFilterPB | CheckboxFilterPB; - - switch (filter.fieldType) { - case FieldType.RichText: - val = new TextFilterPB({ - condition: transformOperator(filter.operator, filter.fieldType) as TextFilterConditionPB, - content: filter.value as string, - }); - break; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - val = new SelectOptionFilterPB({ - condition: transformOperator(filter.operator, filter.fieldType) as SelectOptionConditionPB, - option_ids: filter.value as string[], - }); - break; - default: - val = new TextFilterPB({ - condition: transformOperator('is', FieldType.RichText) as TextFilterConditionPB, - content: '', - }); - break; - } - - let updatedFilter = filter; - - if (filter.id) { - await filterController.updateFilter(filter.id, filter.fieldId, filter.fieldType, val); - } else { - const newId = await filterController.addFilter(filter.fieldId, filter.fieldType, val); - - updatedFilter = { ...filter, id: newId }; - } - - const index = filters.findIndex((f) => f?.fieldId === filter.fieldId); - - if (index === -1) { - setFilters([...filters, updatedFilter]); - } else { - setFilters([...filters.slice(0, index), updatedFilter, ...filters.slice(index + 1)]); - } - - setShowBlankFilter(false); - }; - - const onDeleteFilterItem = async (filter: IDatabaseFilter | null) => { - if (!filter || !filter.id || !filter.fieldId) return; - - // add blank filter if no filters left - if (filters.length === 1) { - setShowBlankFilter(true); - } - - await filterController.removeFilter(filter.fieldId, filter.fieldType, filter.id); - - // update local copy - const index = filters.findIndex((f) => f?.fieldId === filter.fieldId); - - setFilters([...filters.slice(0, index), ...filters.slice(index + 1)]); - }; - - // null row represents new filter - const rows = useMemo(() => (showBlankFilter ? filters.concat([null]) : filters), [filters, showBlankFilter]); - - return ( - <div - className={'fixed inset-0 z-10 flex items-center justify-center overflow-y-auto backdrop-blur-sm'} - onClick={onOutsideClick} - > - <div onClick={(e) => e.stopPropagation()} className='flex flex-col rounded-lg bg-bg-body shadow-md'> - <div className='px-6 pt-6 text-sm text-text-caption'>{t('grid.settings.filter')}</div> - - <div className='flex flex-col gap-3 overflow-y-scroll px-6 py-6 text-sm'> - {rows.map((filter, index: number) => ( - <DatabaseFilterItem - data={filter} - onSave={onSaveFilterItem} - onDelete={() => onDeleteFilterItem(filter)} - key={index} - index={index} - ></DatabaseFilterItem> - ))} - </div> - - <hr /> - - <button - onClick={onAddClick} - className='flex cursor-pointer items-center gap-2 px-6 py-6 text-sm text-text-caption' - > - <div className='h-5 w-5'> - <AddSvg /> - </div> - {t('grid.settings.addFilter')} - </button> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/FieldSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/FieldSelect.tsx deleted file mode 100644 index c93e7d370b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/FieldSelect.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; -import ButtonPopoverList from '$app/components/_shared/ButtonPopoverList'; -import React, { useState } from 'react'; -import { DatabaseFieldMap, IDatabaseColumn } from '$app_reducers/database/slice'; -import { FieldType } from '@/services/backend'; - -interface IFieldSelectProps { - columns: IDatabaseColumn[]; - fields: DatabaseFieldMap; - onSelectFieldClick: (fieldId: string) => void; - currentFieldId: string | null; - currentFieldType: FieldType | undefined; -} - -const WIDTH = 180; - -export const FieldSelect = ({ - columns, - fields, - onSelectFieldClick, - currentFieldId, - currentFieldType, -}: IFieldSelectProps) => { - const [showSelect, setShowSelect] = useState(false); - - return ( - <ButtonPopoverList - isVisible={true} - popoverOptions={columns.map((column) => ({ - key: column.fieldId, - icon: ( - <i className={'block h-5 w-5'}> - <FieldTypeIcon fieldType={fields[column.fieldId].fieldType}></FieldTypeIcon> - </i> - ), - label: fields[column.fieldId].title, - onClick: () => { - onSelectFieldClick(column.fieldId); - setShowSelect(false); - }, - }))} - popoverOrigin={{ - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - }} - onClose={() => setShowSelect(false)} - sx={{ width: `${WIDTH}px` }} - > - <div - onClick={() => setShowSelect(true)} - className={`flex items-center justify-between rounded-lg border px-2 py-1 ${ - showSelect ? 'border-fill-hover' : 'border-line-border' - }`} - style={{ width: `${WIDTH}px` }} - > - {currentFieldType !== undefined && currentFieldId ? ( - <div className={'flex items-center gap-2'}> - <i className={'block h-5 w-5'}> - <FieldTypeIcon fieldType={currentFieldType}></FieldTypeIcon> - </i> - <span>{fields[currentFieldId].title}</span> - </div> - ) : ( - <span className={'text-text-placeholder'}>Select a field</span> - )} - <i className={`h-5 w-5 transition-transform duration-500 ${showSelect ? 'rotate-180' : 'rotate-0'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - </ButtonPopoverList> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/FilterValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/FilterValue.tsx deleted file mode 100644 index 28d562e40b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/FilterValue.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { FieldType } from '@/services/backend'; -import { getBgColor } from '$app/components/_shared/getColor'; -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; -import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; -import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; -import React, { useRef, useState } from 'react'; -import { DatabaseFieldMap, ISelectOption, ISelectOptionType } from '$app_reducers/database/slice'; -import { CellOption } from '$app/components/_shared/EditRow/Options/CellOption'; -import { Popover } from '@mui/material'; - -interface IFilterValueProps { - currentFieldId: string | null; - currentFieldType: FieldType | undefined; - currentValue: string[] | string | boolean | null; - fields: DatabaseFieldMap; - textInputActive: boolean; - setTextInputActive: (v: boolean) => void; - setCurrentValue: (v: string[] | string | boolean | null) => void; - onValueOptionClick: (option: ISelectOption) => void; -} - -const WIDTH = 180; - -export const FilterValue = ({ - currentFieldId, - currentFieldType, - currentValue, - fields, - textInputActive, - setTextInputActive, - setCurrentValue, - onValueOptionClick, -}: IFilterValueProps) => { - const [showValueOptions, setShowValueOptions] = useState(false); - - const refValueOptions = useRef<HTMLDivElement>(null); - - const getSelectOption = (optionId: string) => { - if (!currentFieldId) return undefined; - return (fields[currentFieldId].fieldOptions as ISelectOptionType).selectOptions.find( - (option) => option.selectOptionId === optionId - ); - }; - - return currentFieldId ? ( - <> - {(currentFieldType === FieldType.MultiSelect || currentFieldType === FieldType.SingleSelect) && ( - <> - <div - ref={refValueOptions} - onClick={() => setShowValueOptions(true)} - className={`flex items-center justify-between rounded-lg border px-2 py-1 ${ - showValueOptions ? 'border-fill-hover' : 'border-line-border' - }`} - style={{ width: `${WIDTH}px` }} - > - {currentValue ? ( - <div className={'flex flex-1 items-center gap-1 overflow-hidden'}> - {(currentValue as string[]).length === 0 && ( - <span className={'text-text-placeholder'}>none selected</span> - )} - {(currentValue as string[]).map((option, i) => ( - <span className={`${getBgColor(getSelectOption(option)?.color)} rounded px-2 py-0.5 text-xs`} key={i}> - {getSelectOption(option)?.title} - </span> - ))} - </div> - ) : ( - <span className={'text-text-placeholder'}>Select an option</span> - )} - - <i className={`h-5 w-5 transition-transform duration-500 ${showValueOptions ? 'rotate-180' : 'rotate-0'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - - <Popover - open={showValueOptions} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'left', - }} - anchorEl={refValueOptions.current} - onClose={() => setShowValueOptions(false)} - > - <div style={{ width: `${WIDTH}px` }} className={'flex flex-col gap-2 p-2 text-xs'}> - <div className={'font-medium text-text-caption'}>Value option</div> - <div className={'flex flex-col gap-1'}> - {(fields[currentFieldId].fieldOptions as ISelectOptionType).selectOptions.map((option, index) => ( - <CellOption - key={index} - option={option} - checked={(currentValue as string[]).findIndex((o) => o === option.selectOptionId) !== -1} - noSelect={true} - noDetail={true} - onOptionClick={() => onValueOptionClick(option)} - ></CellOption> - ))} - </div> - </div> - </Popover> - </> - )} - {currentFieldType === FieldType.RichText && ( - <div - className={`flex items-center justify-between rounded-lg border px-2 py-1 ${ - textInputActive ? 'border-fill-hover' : 'border-line-border' - }`} - style={{ width: `${WIDTH}px` }} - > - <input - placeholder={'Enter value'} - className={'flex-1'} - onFocus={() => setTextInputActive(true)} - onBlur={() => setTextInputActive(false)} - value={currentValue as string} - onChange={(e) => setCurrentValue(e.target.value)} - /> - </div> - )} - {currentFieldType === FieldType.Checkbox && ( - <div - onClick={() => setCurrentValue(!currentValue)} - className={`flex cursor-pointer items-center gap-2 rounded-lg border border-line-border px-2 py-1`} - style={{ width: `${WIDTH}px` }} - > - <button className={'h-5 w-5'}> - {currentValue ? <EditorCheckSvg></EditorCheckSvg> : <EditorUncheckSvg></EditorUncheckSvg>} - </button> - <span>{currentValue ? 'Checked' : 'Unchecked'}</span> - </div> - )} - </> - ) : ( - <div - className={`flex items-center justify-between rounded-lg border border-line-border px-2 py-1`} - style={{ width: `${WIDTH}px` }} - > - <span className={'text-text-placeholder'}>Select field</span> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/LogicalOperatorSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/LogicalOperatorSelect.tsx deleted file mode 100644 index e9cbc6d9aa..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/LogicalOperatorSelect.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import ButtonPopoverList from '$app/components/_shared/ButtonPopoverList'; -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; -import React, { useState } from 'react'; - -const LogicalOperators: ('and' | 'or')[] = ['and', 'or']; -const WIDTH = 88; - -export const LogicalOperatorSelect = () => { - const [showSelect, setShowSelect] = useState(false); - - return ( - <ButtonPopoverList - isVisible={true} - popoverOptions={LogicalOperators.map((operator) => ({ - key: operator, - label: operator, - icon: null, - onClick: () => { - console.log('logical operator: ', operator); - setShowSelect(false); - }, - }))} - popoverOrigin={{ - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - }} - onClose={() => setShowSelect(false)} - sx={{ width: `${WIDTH}px` }} - > - <div - onClick={() => setShowSelect(true)} - className={`flex items-center justify-between rounded-lg border px-2 py-1 ${ - showSelect ? 'border-fill-hover' : 'border-line-border' - }`} - style={{ width: `${WIDTH}px` }} - > - and - <i className={`h-5 w-5 transition-transform duration-500 ${showSelect ? 'rotate-180' : 'rotate-0'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - </ButtonPopoverList> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/OperatorSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/OperatorSelect.tsx deleted file mode 100644 index 2a9ce61d3d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/OperatorSelect.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import ButtonPopoverList from '$app/components/_shared/ButtonPopoverList'; -import React, { useState } from 'react'; -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; -import { SupportedOperatorsByType, TDatabaseOperators } from '$app_reducers/database/slice'; -import { FieldType } from '@/services/backend'; - -interface IOperatorSelectProps { - currentOperator: TDatabaseOperators | null; - currentFieldType: FieldType | undefined; - onSelectOperatorClick: (operator: TDatabaseOperators) => void; -} - -const WIDTH = 180; - -export const OperatorSelect = ({ currentOperator, currentFieldType, onSelectOperatorClick }: IOperatorSelectProps) => { - const [showSelect, setShowSelect] = useState(false); - - return ( - <ButtonPopoverList - isVisible={true} - popoverOptions={SupportedOperatorsByType[currentFieldType ? currentFieldType : FieldType.RichText].map( - (operatorName, index) => ({ - icon: null, - key: index, - label: operatorName, - onClick: () => { - onSelectOperatorClick(operatorName); - setShowSelect(false); - }, - }) - )} - popoverOrigin={{ - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - }} - onClose={() => setShowSelect(false)} - sx={{ width: `${WIDTH}px` }} - > - <div - onClick={() => setShowSelect(true)} - className={`flex items-center justify-between rounded-lg border px-2 py-1 ${ - showSelect ? 'border-fill-hover' : 'border-line-border' - }`} - style={{ width: `${WIDTH}px` }} - > - {currentOperator ? ( - <span>{currentOperator}</span> - ) : ( - <span className={'text-text-placeholder'}>Select an option</span> - )} - <i className={`h-5 w-5 transition-transform duration-500 ${showSelect ? 'rotate-180' : 'rotate-0'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - </ButtonPopoverList> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortItem.tsx deleted file mode 100644 index 5e096dce6e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortItem.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { IDatabaseSort } from '$app_reducers/database/slice'; -import React, { useEffect, useMemo, useState } from 'react'; -import { useAppSelector } from '$app/stores/store'; -import { DragElementSvg } from '$app/components/_shared/svg/DragElementSvg'; -import { SortConditionPB } from '@/services/backend'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { FieldSelect } from '$app/components/_shared/DatabaseFilter/FieldSelect'; -import { OrderSelect } from '$app/components/_shared/DatabaseSort/OrderSelect'; - -export const DatabaseSortItem = ({ - data, - onSave, - onDelete, -}: { - data: IDatabaseSort | null; - onSave: (sortItem: IDatabaseSort) => void; - onDelete?: () => void; -}) => { - // stores - const columns = useAppSelector((state) => state.database.columns); - const fields = useAppSelector((state) => state.database.fields); - const sortStore = useAppSelector((state) => state.database.sort); - - // values - const [currentFieldId, setCurrentFieldId] = useState<string | null>(data?.fieldId ?? null); - const [currentOrder, setCurrentOrder] = useState<SortConditionPB | null>(data?.order ?? null); - - const supportedColumns = useMemo( - () => columns.filter((c) => sortStore.findIndex((s) => s.fieldId === c.fieldId) === -1), - [columns, sortStore] - ); - - const currentFieldType = useMemo( - () => (currentFieldId ? fields[currentFieldId].fieldType : undefined), - [currentFieldId, fields] - ); - - useEffect(() => { - if (data) { - setCurrentFieldId(data.fieldId); - setCurrentOrder(data.order); - } else { - setCurrentFieldId(null); - setCurrentOrder(null); - } - }, [data]); - - useEffect(() => { - if (currentFieldId && currentOrder !== null) { - onSave({ - id: data?.id, - fieldId: currentFieldId, - order: currentOrder, - fieldType: fields[currentFieldId].fieldType, - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentFieldId, currentOrder]); - - const onSelectFieldClick = (id: string) => { - setCurrentFieldId(id); - // set ascending order by default - setCurrentOrder(SortConditionPB.Ascending); - }; - - const onSelectOrderClick = (order: SortConditionPB) => { - setCurrentOrder(order); - }; - - return ( - <div className={'flex items-center gap-4'}> - <button className={'flex-shrink-0 rounded p-1 hover:bg-fill-list-hover'}> - <i className={'block h-[16px] w-[16px]'}> - <DragElementSvg></DragElementSvg> - </i> - </button> - - <FieldSelect - columns={supportedColumns} - fields={fields} - onSelectFieldClick={onSelectFieldClick} - currentFieldId={currentFieldId} - currentFieldType={currentFieldType} - ></FieldSelect> - - <OrderSelect currentOrder={currentOrder} onSelectOrderClick={onSelectOrderClick}></OrderSelect> - - <button - onClick={() => onDelete?.()} - className={`rounded p-1 hover:bg-fill-list-hover ${data ? 'opacity-100' : 'opacity-0'}`} - > - <i className={'block h-[16px] w-[16px]'}> - <TrashSvg /> - </i> - </button> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortPopup.tsx deleted file mode 100644 index 98247e3a1a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortPopup.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { t } from 'i18next'; -import { MouseEventHandler, useMemo, useState } from 'react'; -import { useAppSelector } from '$app/stores/store'; -import { IDatabaseSort } from '$app_reducers/database/slice'; -import { DatabaseSortItem } from '$app/components/_shared/DatabaseSort/DatabaseSortItem'; -import AddSvg from '$app/components/_shared/svg/AddSvg'; -import { SortController } from '$app/stores/effects/database/sort/sort_controller'; - -export const DatabaseSortPopup = ({ - sortController, - onOutsideClick, -}: { - sortController: SortController; - onOutsideClick: () => void; -}) => { - // stores - const sortStore = useAppSelector((state) => state.database.sort); - const [sort, setSort] = useState<(IDatabaseSort | null)[]>(sortStore); - - const [showBlankSort, setShowBlankSort] = useState(sortStore.length === 0); - - const onSaveSortItem = async (sortItem: IDatabaseSort) => { - let updatedSort = sortItem; - - if (sortItem.id) { - await sortController.updateSort(sortItem.id, sortItem.fieldId, sortItem.fieldType, sortItem.order); - } else { - const newId = await sortController.addSort(sortItem.fieldId, sortItem.fieldType, sortItem.order); - - updatedSort = { ...updatedSort, id: newId }; - } - - // update local copy - const index = sort.findIndex((s) => s?.fieldId === sortItem.fieldId); - - if (index === -1) { - setSort([...sort, updatedSort]); - } else { - setSort([...sort.slice(0, index), updatedSort, ...sort.slice(index + 1)]); - } - - setShowBlankSort(false); - }; - - const onDeleteClick = async (sortItem: IDatabaseSort | null) => { - if (!sortItem || !sortItem.id) return; - - // add blank sort if no sorts left - if (sort.length === 1) { - setShowBlankSort(true); - } - - await sortController.removeSort(sortItem.fieldId, sortItem.fieldType, sortItem.id); - - const index = sort.findIndex((s) => s?.fieldId === sortItem.fieldId); - - setSort([...sort.slice(0, index), ...sort.slice(index + 1)]); - }; - - const onAddClick: MouseEventHandler = () => { - setShowBlankSort(true); - }; - - const rows = useMemo(() => (showBlankSort ? [...sort, null] : sort), [sort, showBlankSort]); - - return ( - <div - className={'fixed inset-0 z-10 flex items-center justify-center overflow-y-auto backdrop-blur-sm'} - onClick={onOutsideClick} - > - <div onClick={(e) => e.stopPropagation()} className='flex flex-col rounded-lg bg-white shadow-md'> - <div className='px-6 pt-6 text-sm text-text-caption'>{t('grid.settings.sort')}</div> - - <div className='flex flex-col gap-3 overflow-y-scroll px-6 py-6 text-sm'> - {rows.map((sortItem, index) => ( - <DatabaseSortItem - key={index} - data={sortItem} - onSave={onSaveSortItem} - onDelete={() => onDeleteClick(sortItem)} - /> - ))} - </div> - - <hr /> - - <button - onClick={onAddClick} - className='flex cursor-pointer items-center gap-2 px-6 py-6 text-sm text-text-caption' - > - <div className='h-5 w-5'> - <AddSvg /> - </div> - {t('grid.sort.addSort')} - </button> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/OrderSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/OrderSelect.tsx deleted file mode 100644 index c0057371d6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/OrderSelect.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import ButtonPopoverList from '$app/components/_shared/ButtonPopoverList'; -import React, { useState } from 'react'; -import { SortConditionPB } from '@/services/backend'; -import { SortAscSvg } from '$app/components/_shared/svg/SortAscSvg'; -import { SortDescSvg } from '$app/components/_shared/svg/SortDescSvg'; -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; - -interface IOrderSelectProps { - currentOrder: SortConditionPB | null; - onSelectOrderClick: (order: SortConditionPB) => void; -} - -const WIDTH = 180; - -export const OrderSelect = ({ currentOrder, onSelectOrderClick }: IOrderSelectProps) => { - const [showSelect, setShowSelect] = useState(false); - - return ( - <ButtonPopoverList - isVisible={true} - popoverOptions={[ - { - icon: ( - <i className={'block h-5 w-5'}> - <SortAscSvg></SortAscSvg> - </i> - ), - label: 'Ascending', - key: SortConditionPB.Ascending, - onClick: () => { - onSelectOrderClick(SortConditionPB.Ascending); - setShowSelect(false); - }, - }, - { - icon: ( - <i className={'block h-5 w-5'}> - <SortDescSvg></SortDescSvg> - </i> - ), - label: 'Descending', - key: SortConditionPB.Descending, - onClick: () => { - onSelectOrderClick(SortConditionPB.Descending); - setShowSelect(false); - }, - }, - ]} - popoverOrigin={{ - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - transformOrigin: { - vertical: 'top', - horizontal: 'left', - }, - }} - onClose={() => setShowSelect(false)} - sx={{ width: `${WIDTH}px` }} - > - <div - onClick={() => setShowSelect(true)} - className={`flex w-[180px] items-center justify-between rounded-lg border px-2 py-1 ${ - showSelect ? 'border-fill-hover' : 'border-line-border' - }`} - > - {currentOrder !== null ? ( - <SortLabel value={currentOrder}></SortLabel> - ) : ( - <span className={'text-text-caption'}>Select order</span> - )} - <i className={`h-5 w-5 transition-transform duration-500 ${showSelect ? 'rotate-180' : 'rotate-0'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - </ButtonPopoverList> - ); -}; - -const SortLabel = ({ value }: { value: SortConditionPB }) => { - return value === SortConditionPB.Ascending ? ( - <div className={'flex items-center gap-2'}> - <i className={'block h-5 w-5'}> - <SortAscSvg></SortAscSvg> - </i> - <span>Ascending</span> - </div> - ) : ( - <div className={'flex items-center gap-2'}> - <i className={'block h-5 w-5'}> - <SortDescSvg></SortDescSvg> - </i> - <span>Descending</span> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx deleted file mode 100644 index 9f94ddd61d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { FieldType } from '@/services/backend'; -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; -import { Popover } from '@mui/material'; - -const typesOrder: FieldType[] = [ - FieldType.RichText, - FieldType.Number, - FieldType.DateTime, - FieldType.SingleSelect, - FieldType.MultiSelect, - FieldType.Checkbox, - FieldType.URL, - FieldType.Checklist, -]; - -export const ChangeFieldTypePopup = ({ - open, - anchorEl, - onClick, - onOutsideClick, -}: { - open: boolean; - anchorEl: HTMLDivElement | null; - onClick: (newType: FieldType) => void; - onOutsideClick: () => void; -}) => { - return ( - <Popover - open={open} - anchorEl={anchorEl} - anchorOrigin={{ - vertical: 'top', - horizontal: 'right', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'left', - }} - onClose={onOutsideClick} - > - <div className={'flex flex-col p-2 text-xs'}> - {typesOrder.map((t, i) => ( - <button - onClick={() => onClick(t)} - key={i} - className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'h-5 w-5'}> - <FieldTypeIcon fieldType={t}></FieldTypeIcon> - </i> - <span> - <FieldTypeName fieldType={t}></FieldTypeName> - </span> - </button> - ))} - </div> - </Popover> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckList.tsx deleted file mode 100644 index 9bd43fd330..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckList.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { SelectOptionCellDataPB } from '@/services/backend'; -import { useEffect, useRef, useState } from 'react'; -import { ISelectOptionType } from '$app_reducers/database/slice'; -import { useAppSelector } from '$app/stores/store'; -import { CheckListProgress } from '$app/components/_shared/CheckListProgress'; - -export const CheckList = ({ - data, - fieldId, - onEditClick, -}: { - data: SelectOptionCellDataPB | undefined; - fieldId: string; - onEditClick: (left: number, top: number) => void; -}) => { - const ref = useRef<HTMLDivElement>(null); - const [allOptionsCount, setAllOptionsCount] = useState(0); - const [selectedOptionsCount, setSelectedOptionsCount] = useState(0); - const databaseStore = useAppSelector((state) => state.database); - - useEffect(() => { - setAllOptionsCount((databaseStore.fields[fieldId]?.fieldOptions as ISelectOptionType)?.selectOptions?.length ?? 0); - }, [databaseStore, fieldId]); - - useEffect(() => { - setSelectedOptionsCount((data as SelectOptionCellDataPB)?.select_options?.length ?? 0); - }, [data]); - - const onClick = () => { - if (!ref.current) return; - const { left, top } = ref.current.getBoundingClientRect(); - - onEditClick(left, top); - }; - - return ( - <div - ref={ref} - onClick={onClick} - className={'flex w-full flex-wrap items-center gap-2 px-4 py-1 text-xs text-text-title'} - > - <CheckListProgress completed={selectedOptionsCount} max={allOptionsCount} /> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckListOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckListOption.tsx deleted file mode 100644 index a897f91400..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckListOption.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { SelectOptionPB } from '@/services/backend'; -import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; -import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; -import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; -import { ISelectOption } from '$app_reducers/database/slice'; -import { MouseEventHandler } from 'react'; - -export const CheckListOption = ({ - option, - checked, - onToggleOptionClick, - openCheckListDetail, -}: { - option: ISelectOption; - checked: boolean; - onToggleOptionClick: (v: SelectOptionPB) => void; - openCheckListDetail: (left: number, top: number, option: SelectOptionPB) => void; -}) => { - const onCheckListDetailClick: MouseEventHandler = (e) => { - e.stopPropagation(); - let target = e.target as HTMLElement; - - while (!(target instanceof HTMLButtonElement)) { - if (target.parentElement === null) return; - target = target.parentElement; - } - - const selectOption = new SelectOptionPB({ - id: option.selectOptionId, - name: option.title, - }); - - const { right: _left, top: _top } = target.getBoundingClientRect(); - - openCheckListDetail(_left, _top, selectOption); - }; - - return ( - <div - className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-list-hover'} - onClick={() => - onToggleOptionClick( - new SelectOptionPB({ - id: option.selectOptionId, - name: option.title, - }) - ) - } - > - <div className={'h-5 w-5'}> - {checked ? <EditorCheckSvg></EditorCheckSvg> : <EditorUncheckSvg></EditorUncheckSvg>} - </div> - <div className={`flex-1 px-2 py-0.5`}>{option.title}</div> - <div className={'flex items-center'}> - <button onClick={onCheckListDetailClick} className={'h-6 w-6 p-1'}> - <Details2Svg></Details2Svg> - </button> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckListPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckListPopup.tsx deleted file mode 100644 index 0bf50c824d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/CheckListPopup.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { SelectOptionCellDataPB, SelectOptionPB } from '@/services/backend'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { ISelectOptionType } from '$app_reducers/database/slice'; -import { useAppSelector } from '$app/stores/store'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { useEffect, useState } from 'react'; -import { CheckListProgress } from '$app/components/_shared/CheckListProgress'; -import { NewCheckListOption } from '$app/components/_shared/EditRow/CheckList/NewCheckListOption'; -import { CheckListOption } from '$app/components/_shared/EditRow/CheckList/CheckListOption'; -import { NewCheckListButton } from '$app/components/_shared/EditRow/CheckList/NewCheckListButton'; - -export const CheckListPopup = ({ - left, - top, - cellIdentifier, - cellCache, - fieldController, - openCheckListDetail, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; - openCheckListDetail: (left: number, top: number, option: SelectOptionPB) => void; - onOutsideClick: () => void; -}) => { - const databaseStore = useAppSelector((state) => state.database); - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - const [allOptionsCount, setAllOptionsCount] = useState(0); - const [selectedOptionsCount, setSelectedOptionsCount] = useState(0); - const [newOptions, setNewOptions] = useState<string[]>([]); - - useEffect(() => { - setAllOptionsCount( - (databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType)?.selectOptions?.length ?? 0 - ); - }, [databaseStore, cellIdentifier]); - - useEffect(() => { - setSelectedOptionsCount((data as SelectOptionCellDataPB)?.select_options?.length ?? 0); - }, [data]); - - const onToggleOptionClick = async (option: SelectOptionPB) => { - if ((data as SelectOptionCellDataPB)?.select_options?.find((selectedOption) => selectedOption.id === option.id)) { - await new SelectOptionCellBackendService(cellIdentifier).unselectOption([option.id]); - } else { - await new SelectOptionCellBackendService(cellIdentifier).selectOption([option.id]); - } - }; - - return ( - <PopupWindow className={'text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}> - <div className={'min-w-[320px]'}> - <div className={'px-4 pb-4 pt-8'}> - <CheckListProgress completed={selectedOptionsCount} max={allOptionsCount} /> - </div> - - <div className={'flex flex-col p-2'}> - {(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map( - (option, index) => ( - <CheckListOption - key={index} - option={option} - checked={ - !!(data as SelectOptionCellDataPB)?.select_options?.find((so) => so.id === option.selectOptionId) - } - onToggleOptionClick={onToggleOptionClick} - openCheckListDetail={openCheckListDetail} - ></CheckListOption> - ) - )} - {newOptions.map((option, index) => ( - <NewCheckListOption - key={index} - index={index} - option={option} - newOptions={newOptions} - setNewOptions={setNewOptions} - cellIdentifier={cellIdentifier} - ></NewCheckListOption> - ))} - </div> - <div className={'h-[1px] bg-line-divider'}></div> - <div className={'p-2'}> - <NewCheckListButton newOptions={newOptions} setNewOptions={setNewOptions}></NewCheckListButton> - </div> - </div> - </PopupWindow> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/EditCheckListPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/EditCheckListPopup.tsx deleted file mode 100644 index 4dbd45712e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/EditCheckListPopup.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { KeyboardEventHandler, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SelectOptionPB } from '@/services/backend'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; - -export const EditCheckListPopup = ({ - left, - top, - cellIdentifier, - editingSelectOption, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - editingSelectOption: SelectOptionPB; - onOutsideClick: () => void; -}) => { - const inputRef = useRef<HTMLInputElement>(null); - const { t } = useTranslation(); - const [value, setValue] = useState(''); - - useEffect(() => { - setValue(editingSelectOption.name); - }, [editingSelectOption]); - - const onKeyDown: KeyboardEventHandler = async (e) => { - if (e.key === 'Enter' && value.length > 0) { - await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: value }); - setValue(''); - } - }; - - const onKeyDownWrapper: KeyboardEventHandler = (e) => { - if (e.key === 'Escape') { - onOutsideClick(); - } - }; - - const onBlur = async () => { - const svc = new SelectOptionCellBackendService(cellIdentifier); - - await svc.updateOption( - new SelectOptionPB({ - id: editingSelectOption.id, - name: value, - }) - ); - }; - - const onDeleteOptionClick = async () => { - const svc = new SelectOptionCellBackendService(cellIdentifier); - - await svc.deleteOption([editingSelectOption]); - onOutsideClick(); - }; - - return ( - <PopupWindow - className={'p-2 text-xs'} - onOutsideClick={async () => { - await onBlur(); - onOutsideClick(); - }} - left={left} - top={top} - > - <div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}> - <div className={'flex flex-1 items-center gap-2 rounded border border-line-divider bg-fill-list-hover px-2 '}> - <input - ref={inputRef} - className={'py-2'} - value={value} - onChange={(e) => setValue(e.target.value)} - onKeyDown={onKeyDown} - onBlur={() => onBlur()} - /> - <div className={'font-mono text-text-caption'}>{value.length}/30</div> - </div> - <button - onClick={() => onDeleteOptionClick()} - className={ - 'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 text-fill-default hover:bg-fill-list-hover' - } - > - <i className={'h-5 w-5'}> - <TrashSvg></TrashSvg> - </i> - <span>{t('grid.selectOption.deleteTag')}</span> - </button> - </div> - </PopupWindow> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/NewCheckListButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/NewCheckListButton.tsx deleted file mode 100644 index 898fb248dc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/NewCheckListButton.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import AddSvg from '$app/components/_shared/svg/AddSvg'; -import { useTranslation } from 'react-i18next'; - -export const NewCheckListButton = ({ - newOptions, - setNewOptions, -}: { - newOptions: string[]; - setNewOptions: (v: string[]) => void; -}) => { - const { t } = useTranslation(); - - const newOptionClick = () => { - setNewOptions([...newOptions, '']); - }; - - return ( - <button - onClick={() => newOptionClick()} - className={'flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-line-divider'} - > - <i className={'h-5 w-5'}> - <AddSvg></AddSvg> - </i> - <span>{t('grid.field.addOption')}</span> - </button> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/NewCheckListOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/NewCheckListOption.tsx deleted file mode 100644 index 940929660a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CheckList/NewCheckListOption.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { useTranslation } from 'react-i18next'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; - -export const NewCheckListOption = ({ - index, - option, - newOptions, - setNewOptions, - cellIdentifier, -}: { - index: number; - option: string; - newOptions: string[]; - setNewOptions: (v: string[]) => void; - cellIdentifier: CellIdentifier; -}) => { - const { t } = useTranslation(); - - const updateNewOption = (value: string) => { - const newOptionsCopy = [...newOptions]; - - newOptionsCopy[index] = value; - setNewOptions(newOptionsCopy); - }; - - const onNewOptionKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Enter') { - void onSaveNewOptionClick(); - } - }; - - const onSaveNewOptionClick = async () => { - await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: newOptions[index] }); - setNewOptions(newOptions.filter((_, i) => i !== index)); - }; - - return ( - <div className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-line-divider'}> - <input - onKeyDown={(e) => onNewOptionKeyDown(e as unknown as KeyboardEvent)} - className={'min-w-0 flex-1 pl-7'} - value={option} - onChange={(e) => updateNewOption(e.target.value)} - /> - <button - onClick={() => onSaveNewOptionClick()} - className={'flex items-center gap-2 rounded-lg bg-fill-hover px-4 py-2 text-white hover:bg-main-hovered'} - > - {t('grid.selectOption.create')} - </button> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateFormatPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateFormatPopup.tsx deleted file mode 100644 index 58e68fd281..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateFormatPopup.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; -import { useTranslation } from 'react-i18next'; -import { DateFormatPB } from '@/services/backend'; -import { useDateTimeFormat } from '$app/components/_shared/EditRow/Date/DateTimeFormat.hooks'; -import { useAppSelector } from '$app/stores/store'; -import { useEffect, useState } from 'react'; -import { IDateType } from '$app_reducers/database/slice'; - -export const DateFormatPopup = ({ - left, - top, - cellIdentifier, - fieldController, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - fieldController: FieldController; - onOutsideClick: () => void; -}) => { - const { t } = useTranslation(); - const { changeDateFormat } = useDateTimeFormat(cellIdentifier, fieldController); - const databaseStore = useAppSelector((state) => state.database); - const [dateType, setDateType] = useState<IDateType | undefined>(); - - useEffect(() => { - setDateType(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as IDateType); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [databaseStore]); - - const changeFormat = async (format: DateFormatPB) => { - await changeDateFormat(format); - onOutsideClick(); - }; - - return ( - <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}> - <PopupItem - changeFormat={changeFormat} - format={DateFormatPB.Friendly} - checked={dateType?.dateFormat === DateFormatPB.Friendly} - text={t('grid.field.dateFormatFriendly')} - /> - <PopupItem - changeFormat={changeFormat} - format={DateFormatPB.ISO} - checked={dateType?.dateFormat === DateFormatPB.ISO} - text={t('grid.field.dateFormatISO')} - /> - <PopupItem - changeFormat={changeFormat} - format={DateFormatPB.Local} - checked={dateType?.dateFormat === DateFormatPB.Local} - text={t('grid.field.dateFormatLocal')} - /> - <PopupItem - changeFormat={changeFormat} - format={DateFormatPB.US} - checked={dateType?.dateFormat === DateFormatPB.US} - text={t('grid.field.dateFormatUS')} - /> - </PopupWindow> - ); -}; - -function PopupItem({ - format, - text, - changeFormat, - checked, -}: { - format: DateFormatPB; - text: string; - changeFormat: (_: DateFormatPB) => Promise<void>; - checked: boolean; -}) { - return ( - <button - onClick={() => changeFormat(format)} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-list-hover' - } - > - {text} - - {checked && ( - <div className={'ml-8 h-5 w-5 p-1'}> - <CheckmarkSvg></CheckmarkSvg> - </div> - )} - </button> - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DatePickerPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DatePickerPopup.tsx deleted file mode 100644 index 8d5de41e41..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DatePickerPopup.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useEffect, useState } from 'react'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import Calendar from 'react-calendar'; -import dayjs from 'dayjs'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; -import { CalendarData } from '$app/stores/effects/database/cell/controller_builder'; -import { DateCellDataPB } from '@/services/backend'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { DateTypeOptions } from '$app/components/_shared/EditRow/Date/DateTypeOptions'; - -export const DatePickerPopup = ({ - left, - top, - cellIdentifier, - cellCache, - fieldController, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; - onOutsideClick: () => void; -}) => { - const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); - const [selectedDate, setSelectedDate] = useState<Date>(new Date()); - - useEffect(() => { - const date_pb = data as DateCellDataPB | undefined; - - if (!date_pb || !date_pb?.date.length) return; - - setSelectedDate(dayjs(date_pb.date).toDate()); - }, [data]); - - const onChange = async (v: Date | null | (Date | null)[]) => { - if (v instanceof Date) { - setSelectedDate(v); - const date = new CalendarData(dayjs(v).add(dayjs().utcOffset(), 'minutes').toDate(), false); - - await cellController?.saveCellData(date); - } - }; - - return ( - <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}> - <div className={'px-2 pb-2'}> - <Calendar onChange={(d) => onChange(d)} value={selectedDate} /> - </div> - <DateTypeOptions cellIdentifier={cellIdentifier} fieldController={fieldController}></DateTypeOptions> - </PopupWindow> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateTimeFormat.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateTimeFormat.hooks.ts deleted file mode 100644 index 55eb3597da..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateTimeFormat.hooks.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { Some } from 'ts-results'; -import { DateFormatPB, DateTypeOptionPB, FieldType, TimeFormatPB } from '@/services/backend'; -import { makeDateTypeOptionContext } from '$app/stores/effects/database/field/type_option/type_option_context'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; - -export const useDateTimeFormat = (cellIdentifier: CellIdentifier, fieldController: FieldController) => { - const changeFormat = async (change: (option: DateTypeOptionPB) => void) => { - const fieldInfo = fieldController.getField(cellIdentifier.fieldId); - - if (!fieldInfo) return; - const typeOptionController = new TypeOptionController(cellIdentifier.viewId, Some(fieldInfo), FieldType.DateTime); - - await typeOptionController.initialize(); - const dateTypeOptionContext = makeDateTypeOptionContext(typeOptionController); - const typeOption = dateTypeOptionContext.getTypeOption(); - - change(typeOption); - await dateTypeOptionContext.setTypeOption(typeOption); - }; - - const changeDateFormat = async (format: DateFormatPB) => { - await changeFormat((option) => (option.date_format = format)); - }; - - const changeTimeFormat = async (format: TimeFormatPB) => { - await changeFormat((option) => (option.time_format = format)); - }; - - const includeTime = async (_include: boolean) => { - await changeFormat((_option) => { - // option.include_time = include; - }); - }; - - return { - changeDateFormat, - changeTimeFormat, - includeTime, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateTypeOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateTypeOptions.tsx deleted file mode 100644 index c5e813b8b5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/DateTypeOptions.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { DateFormatPopup } from '$app/components/_shared/EditRow/Date/DateFormatPopup'; -import { TimeFormatPopup } from '$app/components/_shared/EditRow/Date/TimeFormatPopup'; -import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; -import { MouseEventHandler, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { IDateType } from '$app_reducers/database/slice'; -import { useAppSelector } from '$app/stores/store'; -import { useDateTimeFormat } from '$app/components/_shared/EditRow/Date/DateTimeFormat.hooks'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; - -export const DateTypeOptions = ({ - cellIdentifier, - fieldController, -}: { - cellIdentifier: CellIdentifier; - fieldController: FieldController; -}) => { - const { t } = useTranslation(); - - const [showDateFormatPopup, setShowDateFormatPopup] = useState(false); - const [dateFormatTop, setDateFormatTop] = useState(0); - const [dateFormatLeft, setDateFormatLeft] = useState(0); - - const [showTimeFormatPopup, setShowTimeFormatPopup] = useState(false); - const [timeFormatTop, setTimeFormatTop] = useState(0); - const [timeFormatLeft, setTimeFormatLeft] = useState(0); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [dateType, setDateType] = useState<IDateType | undefined>(); - - const databaseStore = useAppSelector((state) => state.database); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { includeTime } = useDateTimeFormat(cellIdentifier, fieldController); - - useEffect(() => { - setDateType(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as IDateType); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [databaseStore]); - - const onDateFormatClick = (_left: number, _top: number) => { - setShowDateFormatPopup(true); - setDateFormatLeft(_left + 10); - setDateFormatTop(_top); - }; - - const onTimeFormatClick = (_left: number, _top: number) => { - setShowTimeFormatPopup(true); - setTimeFormatLeft(_left + 10); - setTimeFormatTop(_top); - }; - - const _onDateFormatClick: MouseEventHandler = (e) => { - e.stopPropagation(); - let target = e.target as HTMLElement; - - while (!(target instanceof HTMLButtonElement)) { - if (target.parentElement === null) return; - target = target.parentElement; - } - - const { right: _left, top: _top } = target.getBoundingClientRect(); - - onDateFormatClick(_left, _top); - }; - - const _onTimeFormatClick: MouseEventHandler = (e) => { - e.stopPropagation(); - let target = e.target as HTMLElement; - - while (!(target instanceof HTMLButtonElement)) { - if (target.parentElement === null) return; - target = target.parentElement; - } - - const { right: _left, top: _top } = target.getBoundingClientRect(); - - onTimeFormatClick(_left, _top); - }; - - const toggleIncludeTime = async () => { - // if (dateType?.includeTime) { - // await includeTime(false); - // } else { - // await includeTime(true); - // } - }; - - return ( - <div className={'flex flex-col'}> - <hr className={'-mx-2 my-2 border-line-divider'} /> - <button - onClick={_onDateFormatClick} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-list-hover' - } - > - <span>{t('grid.field.dateFormat')}</span> - <i className={'h-5 w-5'}> - <MoreSvg></MoreSvg> - </i> - </button> - <hr className={'-mx-2 my-2 border-line-divider'} /> - <button - onClick={() => toggleIncludeTime()} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-list-hover' - } - > - <div className={'flex items-center gap-2'}> - <span>{t('grid.field.includeTime')}</span> - </div> - {/*<i className={'h-5 w-5'}>*/} - {/* {dateType?.includeTime ? <EditorCheckSvg></EditorCheckSvg> : <EditorUncheckSvg></EditorUncheckSvg>}*/} - {/*</i>*/} - </button> - - <button - onClick={_onTimeFormatClick} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-list-hover' - } - > - <span>{t('grid.field.timeFormat')}</span> - <i className={'h-5 w-5'}> - <MoreSvg></MoreSvg> - </i> - </button> - {showDateFormatPopup && ( - <DateFormatPopup - top={dateFormatTop} - left={dateFormatLeft} - cellIdentifier={cellIdentifier} - fieldController={fieldController} - onOutsideClick={() => setShowDateFormatPopup(false)} - ></DateFormatPopup> - )} - {showTimeFormatPopup && ( - <TimeFormatPopup - top={timeFormatTop} - left={timeFormatLeft} - cellIdentifier={cellIdentifier} - fieldController={fieldController} - onOutsideClick={() => setShowTimeFormatPopup(false)} - ></TimeFormatPopup> - )} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/EditCellDate.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/EditCellDate.tsx deleted file mode 100644 index 1fb6656c6f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/EditCellDate.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { MouseEventHandler, useRef } from 'react'; -import { DateCellDataPB } from '@/services/backend'; - -export const EditCellDate = ({ - data, - onEditClick, -}: { - data?: DateCellDataPB; - onEditClick: (left: number, top: number) => void; -}) => { - const ref = useRef<HTMLDivElement>(null); - - const onClick: MouseEventHandler = () => { - if (!ref.current) return; - const { left, top } = ref.current.getBoundingClientRect(); - - onEditClick(left, top); - }; - - return ( - <div ref={ref} onClick={onClick} className={'w-full px-4 py-1'}> - {data?.date} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/NumberFormat.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/NumberFormat.hooks.ts deleted file mode 100644 index ad91aa1bd9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/NumberFormat.hooks.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { FieldType, NumberFormatPB } from '@/services/backend'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { Some } from 'ts-results'; -import { makeNumberTypeOptionContext } from '$app/stores/effects/database/field/type_option/type_option_context'; - -export const useNumberFormat = (cellIdentifier: CellIdentifier, fieldController: FieldController) => { - const changeNumberFormat = async (format: NumberFormatPB) => { - const fieldInfo = fieldController.getField(cellIdentifier.fieldId); - - if (!fieldInfo) return; - const typeOptionController = new TypeOptionController(cellIdentifier.viewId, Some(fieldInfo), FieldType.Number); - - await typeOptionController.initialize(); - const numberTypeOptionContext = makeNumberTypeOptionContext(typeOptionController); - const typeOption = numberTypeOptionContext.getTypeOption(); - - typeOption.format = format; - await numberTypeOptionContext.setTypeOption(typeOption); - }; - - return { - changeNumberFormat, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/NumberFormatPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/NumberFormatPopup.tsx deleted file mode 100644 index df4749befc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/NumberFormatPopup.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { useNumberFormat } from '$app/components/_shared/EditRow/Date/NumberFormat.hooks'; -import { NumberFormatPB } from '@/services/backend'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; -import { useAppSelector } from '$app/stores/store'; -import { useEffect, useState } from 'react'; -import { INumberType } from '$app_reducers/database/slice'; - -const list = [ - { format: NumberFormatPB.Num, title: 'Num' }, - { format: NumberFormatPB.USD, title: 'USD' }, - { format: NumberFormatPB.CanadianDollar, title: 'CanadianDollar' }, - { format: NumberFormatPB.EUR, title: 'EUR' }, - { format: NumberFormatPB.Pound, title: 'Pound' }, - { format: NumberFormatPB.Yen, title: 'Yen' }, - { format: NumberFormatPB.Ruble, title: 'Ruble' }, - { format: NumberFormatPB.Rupee, title: 'Rupee' }, - { format: NumberFormatPB.Won, title: 'Won' }, - { format: NumberFormatPB.Yuan, title: 'Yuan' }, - { format: NumberFormatPB.Real, title: 'Real' }, - { format: NumberFormatPB.Lira, title: 'Lira' }, - { format: NumberFormatPB.Rupiah, title: 'Rupiah' }, - { format: NumberFormatPB.Franc, title: 'Franc' }, - { format: NumberFormatPB.HongKongDollar, title: 'HongKongDollar' }, - { format: NumberFormatPB.NewZealandDollar, title: 'NewZealandDollar' }, - { format: NumberFormatPB.Krona, title: 'Krona' }, - { format: NumberFormatPB.NorwegianKrone, title: 'NorwegianKrone' }, - { format: NumberFormatPB.MexicanPeso, title: 'MexicanPeso' }, - { format: NumberFormatPB.Rand, title: 'Rand' }, - { format: NumberFormatPB.NewTaiwanDollar, title: 'NewTaiwanDollar' }, - { format: NumberFormatPB.DanishKrone, title: 'DanishKrone' }, - { format: NumberFormatPB.Baht, title: 'Baht' }, - { format: NumberFormatPB.Forint, title: 'Forint' }, - { format: NumberFormatPB.Koruna, title: 'Koruna' }, - { format: NumberFormatPB.Shekel, title: 'Shekel' }, - { format: NumberFormatPB.ChileanPeso, title: 'ChileanPeso' }, - { format: NumberFormatPB.PhilippinePeso, title: 'PhilippinePeso' }, - { format: NumberFormatPB.Dirham, title: 'Dirham' }, - { format: NumberFormatPB.ColombianPeso, title: 'ColombianPeso' }, - { format: NumberFormatPB.Riyal, title: 'Riyal' }, - { format: NumberFormatPB.Ringgit, title: 'Ringgit' }, - { format: NumberFormatPB.Leu, title: 'Leu' }, - { format: NumberFormatPB.ArgentinePeso, title: 'ArgentinePeso' }, - { format: NumberFormatPB.UruguayanPeso, title: 'UruguayanPeso' }, - { format: NumberFormatPB.Percent, title: 'Percent' }, -]; - -export const NumberFormatPopup = ({ - left, - top, - cellIdentifier, - fieldController, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - fieldController: FieldController; - onOutsideClick: () => void; -}) => { - const { changeNumberFormat } = useNumberFormat(cellIdentifier, fieldController); - const databaseStore = useAppSelector((state) => state.database); - const [numberType, setNumberType] = useState<INumberType | undefined>(); - - useEffect(() => { - setNumberType(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as INumberType); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [databaseStore]); - - const changeNumberFormatClick = async (format: NumberFormatPB) => { - await changeNumberFormat(format); - onOutsideClick(); - }; - - return ( - <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}> - <div className={'h-[400px] overflow-auto'}> - {list.map((item, index) => ( - <FormatButton - key={index} - title={item.title} - checked={numberType?.numberFormat === item.format} - onClick={() => changeNumberFormatClick(item.format)} - ></FormatButton> - ))} - </div> - </PopupWindow> - ); -}; - -const FormatButton = ({ title, checked, onClick }: { title: string; checked: boolean; onClick: () => void }) => { - return ( - <button - onClick={() => onClick()} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-list-hover' - } - > - <span className={'block pr-8'}>{title}</span> - {checked && ( - <div className={'h-5 w-5 p-1'}> - <CheckmarkSvg></CheckmarkSvg> - </div> - )} - </button> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/TimeFormatPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/TimeFormatPopup.tsx deleted file mode 100644 index 287d93a091..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Date/TimeFormatPopup.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { useTranslation } from 'react-i18next'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { TimeFormatPB } from '@/services/backend'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; -import { useDateTimeFormat } from '$app/components/_shared/EditRow/Date/DateTimeFormat.hooks'; -import { useAppSelector } from '$app/stores/store'; -import { useEffect, useState } from 'react'; -import { IDateType } from '$app_reducers/database/slice'; - -export const TimeFormatPopup = ({ - left, - top, - cellIdentifier, - fieldController, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - fieldController: FieldController; - onOutsideClick: () => void; -}) => { - const { t } = useTranslation(); - const databaseStore = useAppSelector((state) => state.database); - const [dateType, setDateType] = useState<IDateType | undefined>(); - - useEffect(() => { - setDateType(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as IDateType); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [databaseStore]); - - const { changeTimeFormat } = useDateTimeFormat(cellIdentifier, fieldController); - - const changeFormat = async (format: TimeFormatPB) => { - await changeTimeFormat(format); - onOutsideClick(); - }; - - return ( - <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}> - <button - onClick={() => changeFormat(TimeFormatPB.TwelveHour)} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-list-hover' - } - > - {t('grid.field.timeFormatTwelveHour')} - - {dateType?.timeFormat === TimeFormatPB.TwelveHour && ( - <div className={'ml-8 h-5 w-5 p-1'}> - <CheckmarkSvg></CheckmarkSvg> - </div> - )} - </button> - <button - onClick={() => changeFormat(TimeFormatPB.TwentyFourHour)} - className={ - 'flex w-full cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-list-hover' - } - > - {t('grid.field.timeFormatTwentyFourHour')} - - {dateType?.timeFormat === TimeFormatPB.TwentyFourHour && ( - <div className={'ml-8 h-5 w-5 p-1'}> - <CheckmarkSvg></CheckmarkSvg> - </div> - )} - </button> - </PopupWindow> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx deleted file mode 100644 index 5436633bdb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { DateCellDataPB, FieldType, SelectOptionCellDataPB, URLCellDataPB } from '@/services/backend'; -import { useAppSelector } from '$app/stores/store'; -import { EditCellText } from '$app/components/_shared/EditRow/InlineEditFields/EditCellText'; -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { EditCellDate } from '$app/components/_shared/EditRow/Date/EditCellDate'; -import { useRef } from 'react'; -import { CellOptions } from '$app/components/_shared/EditRow/Options/CellOptions'; -import { EditCellNumber } from '$app/components/_shared/EditRow/InlineEditFields/EditCellNumber'; -import { EditCheckboxCell } from '$app/components/_shared/EditRow/InlineEditFields/EditCheckboxCell'; -import { EditCellUrl } from '$app/components/_shared/EditRow/InlineEditFields/EditCellUrl'; -import { Draggable } from 'react-beautiful-dnd'; -import { DragElementSvg } from '$app/components/_shared/svg/DragElementSvg'; -import { CheckList } from '$app/components/_shared/EditRow/CheckList/CheckList'; - -export const EditCellWrapper = ({ - index, - cellIdentifier, - cellCache, - fieldController, - onEditFieldClick, - onEditOptionsClick, - onEditDateClick, - onEditCheckListClick, -}: { - index: number; - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; - onEditFieldClick: (cell: CellIdentifier, anchorEl: HTMLDivElement) => void; - onEditOptionsClick: (cell: CellIdentifier, left: number, top: number) => void; - onEditDateClick: (cell: CellIdentifier, left: number, top: number) => void; - onEditCheckListClick: (cell: CellIdentifier, left: number, top: number) => void; -}) => { - const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); - const databaseStore = useAppSelector((state) => state.database); - const el = useRef<HTMLDivElement>(null); - - const onClick = () => { - if (!el.current) return; - - onEditFieldClick(cellIdentifier, el.current); - }; - - return ( - <Draggable draggableId={cellIdentifier.fieldId} index={index} key={cellIdentifier.fieldId}> - {(provided) => ( - <div - ref={provided.innerRef} - {...provided.draggableProps} - {...provided.dragHandleProps} - className={'flex w-full flex-col items-start gap-2 text-xs'} - > - <div className={'relative flex cursor-pointer items-center gap-2 rounded-lg transition-colors duration-200'}> - <div - ref={el} - onClick={() => onClick()} - className={'text-icon-default flex h-5 w-5 rounded hover:bg-fill-list-hover'} - > - <DragElementSvg></DragElementSvg> - </div> - - <div className={'flex h-5 w-5 flex-shrink-0 items-center justify-center text-text-caption'}> - <FieldTypeIcon fieldType={cellIdentifier.fieldType}></FieldTypeIcon> - </div> - <span className={'overflow-hidden text-ellipsis whitespace-nowrap text-text-caption'}> - {databaseStore.fields[cellIdentifier.fieldId]?.title ?? ''} - </span> - </div> - - <div className={'w-full cursor-pointer rounded-lg pl-3 text-sm hover:bg-content-blue-50'}> - {(cellIdentifier.fieldType === FieldType.SingleSelect || - cellIdentifier.fieldType === FieldType.MultiSelect) && - cellController && ( - <CellOptions - data={data as SelectOptionCellDataPB} - onEditClick={(left, top) => onEditOptionsClick(cellIdentifier, left, top)} - ></CellOptions> - )} - - {cellIdentifier.fieldType === FieldType.Checklist && cellController && ( - <CheckList - data={data as SelectOptionCellDataPB} - fieldId={cellIdentifier.fieldId} - onEditClick={(left, top) => onEditCheckListClick(cellIdentifier, left, top)} - ></CheckList> - )} - - {cellIdentifier.fieldType === FieldType.Checkbox && cellController && ( - <EditCheckboxCell - data={data as 'Yes' | 'No' | undefined} - onToggle={async () => { - if (data === 'Yes') { - await cellController?.saveCellData('No'); - } else { - await cellController?.saveCellData('Yes'); - } - }} - ></EditCheckboxCell> - )} - - {cellIdentifier.fieldType === FieldType.DateTime && ( - <EditCellDate - data={data as DateCellDataPB} - onEditClick={(left, top) => onEditDateClick(cellIdentifier, left, top)} - ></EditCellDate> - )} - - {cellIdentifier.fieldType === FieldType.Number && cellController && ( - <EditCellNumber - data={data as string | undefined} - onSave={async (value) => { - await cellController?.saveCellData(value); - }} - ></EditCellNumber> - )} - - {cellIdentifier.fieldType === FieldType.URL && cellController && ( - <EditCellUrl - data={data as URLCellDataPB} - onSave={async (value) => { - await cellController?.saveCellData(value); - }} - ></EditCellUrl> - )} - - {cellIdentifier.fieldType === FieldType.RichText && cellController && ( - <EditCellText - data={data as string | undefined} - onSave={async (value) => { - await cellController?.saveCellData(value); - }} - ></EditCellText> - )} - </div> - </div> - )} - </Draggable> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx deleted file mode 100644 index 134807e1a1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import React, { FocusEventHandler, useEffect, useRef, useState } from 'react'; -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; -import { useTranslation } from 'react-i18next'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { Some } from 'ts-results'; -import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; -import { useAppSelector } from '$app/stores/store'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { EyeClosedSvg } from '$app/components/_shared/svg/EyeClosedSvg'; -import { Popover } from '@mui/material'; -import { CopySvg } from '$app/components/_shared/svg/CopySvg'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { SkipLeftSvg } from '$app/components/_shared/svg/SkipLeftSvg'; -import { SkipRightSvg } from '$app/components/_shared/svg/SkipRightSvg'; -import { EyeOpenSvg } from '$app/components/_shared/svg/EyeOpenSvg'; - -export const EditFieldPopup = ({ - open, - anchorEl, - cellIdentifier, - viewId, - onOutsideClick, - controller, - changeFieldTypeClick, - onDeletePropertyClick, -}: { - open: boolean; - anchorEl: HTMLDivElement | null; - cellIdentifier: CellIdentifier; - viewId: string; - onOutsideClick: () => void; - controller: DatabaseController; - changeFieldTypeClick: (el: HTMLDivElement) => void; - onDeletePropertyClick: (fieldId: string) => void; -}) => { - const fieldsStore = useAppSelector((state) => state.database.fields); - const { t } = useTranslation(); - const changeTypeButtonRef = useRef<HTMLDivElement>(null); - const inputRef = useRef<HTMLInputElement>(null); - const [name, setName] = useState(''); - - useEffect(() => { - setName(fieldsStore[cellIdentifier.fieldId].title); - }, [fieldsStore, cellIdentifier]); - - // focus input on mount - useEffect(() => { - if (!inputRef.current || !name) return; - inputRef.current.focus(); - }, [inputRef, name]); - - const selectAll: FocusEventHandler<HTMLInputElement> = (e) => { - e.target.selectionStart = 0; - e.target.selectionEnd = e.target.value.length; - }; - - const save = async () => { - if (!controller) return; - const fieldInfo = controller.fieldController.getField(cellIdentifier.fieldId); - - if (!fieldInfo) return; - const typeOptionController = new TypeOptionController(viewId, Some(fieldInfo)); - - await typeOptionController.initialize(); - await typeOptionController.setFieldName(name); - }; - - const onChangeFieldTypeClick = () => { - if (!changeTypeButtonRef.current) return; - - changeFieldTypeClick(changeTypeButtonRef.current); - }; - - const toggleHideProperty = async () => { - // we need to close the popup because after hiding the field, parent element will be removed - onOutsideClick(); - const fieldInfo = controller.fieldController.getField(cellIdentifier.fieldId); - - if (fieldInfo) { - const typeController = new TypeOptionController(viewId, Some(fieldInfo)); - - await typeController.initialize(); - if (fieldInfo.field.visibility) { - await typeController.hideField(); - } else { - await typeController.showField(); - } - } - }; - - const onDuplicatePropertyClick = async () => { - onOutsideClick(); - await controller.duplicateField(cellIdentifier.fieldId); - }; - - const onAddToLeftClick = async () => { - onOutsideClick(); - await controller.addFieldToLeft(cellIdentifier.fieldId); - }; - - const onAddToRightClick = async () => { - onOutsideClick(); - await controller.addFieldToRight(cellIdentifier.fieldId); - }; - - return ( - <Popover - anchorEl={anchorEl} - open={open} - onClose={async () => { - await save(); - onOutsideClick(); - }} - disableRestoreFocus={true} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - transformOrigin={{ - vertical: 'top', - horizontal: 'left', - }} - > - <div className={'flex flex-col gap-2 p-2 text-xs'}> - <input - ref={inputRef} - onFocus={selectAll} - value={name} - onChange={(e) => setName(e.target.value)} - onBlur={() => save()} - className={ - 'flex-1 rounded border border-line-divider px-2 py-2 hover:border-fill-default focus:border-fill-default' - } - /> - - <div - ref={changeTypeButtonRef} - onClick={() => onChangeFieldTypeClick()} - className={ - 'relative flex cursor-pointer items-center justify-between rounded-lg py-2 text-text-title hover:bg-fill-list-hover' - } - > - <button className={'flex cursor-pointer items-center gap-2 rounded-lg pl-2'}> - <i className={'h-5 w-5'}> - <FieldTypeIcon fieldType={cellIdentifier.fieldType}></FieldTypeIcon> - </i> - <span> - <FieldTypeName fieldType={cellIdentifier.fieldType}></FieldTypeName> - </span> - </button> - <span className={'pr-2'}> - <i className={' block h-5 w-5'}> - <MoreSvg></MoreSvg> - </i> - </span> - </div> - - <div className={'-mx-2 h-[1px] bg-line-divider'}></div> - - <div className={'grid grid-cols-2'}> - <div className={'flex flex-col gap-2'}> - <div - onClick={toggleHideProperty} - className={'flex cursor-pointer items-center gap-2 rounded-lg p-2 pr-8 hover:bg-fill-list-hover'} - > - {fieldsStore[cellIdentifier.fieldId]?.visible ? ( - <> - <i className={'block h-5 w-5'}> - <EyeClosedSvg /> - </i> - <span>{t('grid.field.hide')}</span> - </> - ) : ( - <> - <i className={'block h-5 w-5'}> - <EyeOpenSvg /> - </i> - <span>Show</span> - </> - )} - </div> - <div - onClick={() => onDuplicatePropertyClick()} - className={'flex cursor-pointer items-center gap-2 rounded-lg p-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'block h-5 w-5'}> - <CopySvg /> - </i> - <span>{t('grid.field.duplicate')}</span> - </div> - <div - onClick={() => { - onOutsideClick(); - onDeletePropertyClick(cellIdentifier.fieldId); - }} - className={'flex cursor-pointer items-center gap-2 rounded-lg p-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'block h-5 w-5'}> - <TrashSvg /> - </i> - <span>{t('grid.field.delete')}</span> - </div> - </div> - - <div className={'flex flex-col gap-2'}> - <div - onClick={onAddToLeftClick} - className={'flex cursor-pointer items-center gap-2 rounded-lg p-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'block h-5 w-5'}> - <SkipLeftSvg /> - </i> - <span>{t('grid.field.insertLeft')}</span> - </div> - <div - onClick={onAddToRightClick} - className={'flex cursor-pointer items-center gap-2 rounded-lg p-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'block h-5 w-5'}> - <SkipRightSvg /> - </i> - <span>{t('grid.field.insertRight')}</span> - </div> - </div> - </div> - </div> - </Popover> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx deleted file mode 100644 index 90fe708859..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx +++ /dev/null @@ -1,367 +0,0 @@ -import { CloseSvg } from '$app/components/_shared/svg/CloseSvg'; -import { useRow } from '$app/components/_shared/database-hooks/useRow'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { EditCellWrapper } from '$app/components/_shared/EditRow/EditCellWrapper'; -import AddSvg from '$app/components/_shared/svg/AddSvg'; -import { useTranslation } from 'react-i18next'; -import { EditFieldPopup } from '$app/components/_shared/EditRow/EditFieldPopup'; -import { useEffect, useState } from 'react'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { ChangeFieldTypePopup } from '$app/components/_shared/EditRow/ChangeFieldTypePopup'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { Some } from 'ts-results'; -import { FieldType, SelectOptionPB } from '@/services/backend'; -import { CellOptionsPopup } from '$app/components/_shared/EditRow/Options/CellOptionsPopup'; -import { DatePickerPopup } from '$app/components/_shared/EditRow/Date/DatePickerPopup'; -import { DragDropContext, Droppable, OnDragEndResponder } from 'react-beautiful-dnd'; -import { EditCellOptionPopup } from '$app/components/_shared/EditRow/Options/EditCellOptionPopup'; -import { NumberFormatPopup } from '$app/components/_shared/EditRow/Date/NumberFormatPopup'; -import { CheckListPopup } from '$app/components/_shared/EditRow/CheckList/CheckListPopup'; -import { EditCheckListPopup } from '$app/components/_shared/EditRow/CheckList/EditCheckListPopup'; -import { PropertiesPanel } from '$app/components/_shared/EditRow/PropertiesPanel'; -import { ImageSvg } from '$app/components/_shared/svg/ImageSvg'; -import { PromptWindow } from '$app/components/_shared/PromptWindow'; -import { useAppSelector } from '$app/stores/store'; - -export const EditRow = ({ - onClose, - viewId, - controller, - rowInfo, -}: { - onClose: () => void; - viewId: string; - controller: DatabaseController; - rowInfo: RowInfo; -}) => { - const fieldsStore = useAppSelector((state) => state.database.fields); - const { cells, onNewColumnClick } = useRow(viewId, controller, rowInfo); - const { t } = useTranslation(); - const [unveil, setUnveil] = useState(false); - - const [editingCell, setEditingCell] = useState<CellIdentifier | null>(null); - const [editFieldAnchorEl, setEditFieldAnchorEl] = useState<HTMLDivElement | null>(null); - const [showFieldEditor, setShowFieldEditor] = useState(false); - - const [showChangeFieldTypePopup, setShowChangeFieldTypePopup] = useState(false); - const [changeFieldTypeAnchorEl, setChangeFieldTypeAnchorEl] = useState<HTMLDivElement | null>(null); - - const [showChangeOptionsPopup, setShowChangeOptionsPopup] = useState(false); - const [changeOptionsTop, setChangeOptionsTop] = useState(0); - const [changeOptionsLeft, setChangeOptionsLeft] = useState(0); - - const [showDatePicker, setShowDatePicker] = useState(false); - const [datePickerTop, setDatePickerTop] = useState(0); - const [datePickerLeft, setDatePickerLeft] = useState(0); - - const [showEditCellOption, setShowEditCellOption] = useState(false); - const [editCellOptionTop, setEditCellOptionTop] = useState(0); - const [editCellOptionLeft, setEditCellOptionLeft] = useState(0); - - const [editingSelectOption, setEditingSelectOption] = useState<SelectOptionPB | undefined>(); - - const [showEditCheckList, setShowEditCheckList] = useState(false); - const [editCheckListTop, setEditCheckListTop] = useState(0); - const [editCheckListLeft, setEditCheckListLeft] = useState(0); - - const [showNumberFormatPopup, setShowNumberFormatPopup] = useState(false); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [numberFormatTop, setNumberFormatTop] = useState(0); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [numberFormatLeft, setNumberFormatLeft] = useState(0); - - const [showCheckListPopup, setShowCheckListPopup] = useState(false); - const [checkListPopupTop, setCheckListPopupTop] = useState(0); - const [checkListPopupLeft, setCheckListPopupLeft] = useState(0); - - const [deletingPropertyId, setDeletingPropertyId] = useState<string | null>(null); - const [showDeletePropertyPrompt, setShowDeletePropertyPrompt] = useState(false); - - useEffect(() => { - setUnveil(true); - }, []); - - const onCloseClick = () => { - setUnveil(false); - setTimeout(() => { - onClose(); - }, 300); - }; - - const onEditFieldClick = (cellIdentifier: CellIdentifier, anchorEl: HTMLDivElement) => { - setEditFieldAnchorEl(anchorEl); - setEditingCell(cellIdentifier); - setShowFieldEditor(true); - }; - - const onOutsideEditFieldClick = () => { - setShowFieldEditor(false); - }; - - const onChangeFieldTypeClick = (el: HTMLDivElement) => { - setChangeFieldTypeAnchorEl(el); - setShowChangeFieldTypePopup(true); - }; - - const changeFieldType = async (newType: FieldType) => { - if (!editingCell) return; - - const currentField = controller.fieldController.getField(editingCell.fieldId); - - if (!currentField) return; - - const typeOptionController = new TypeOptionController(viewId, Some(currentField)); - - await typeOptionController.switchToField(newType); - - setEditingCell(new CellIdentifier(viewId, rowInfo.row.id, editingCell.fieldId, newType)); - - setShowChangeFieldTypePopup(false); - }; - - const onEditOptionsClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => { - setEditingCell(cellIdentifier); - setChangeOptionsLeft(left); - setChangeOptionsTop(top + 40); - setShowChangeOptionsPopup(true); - }; - - const onEditDateClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => { - setEditingCell(cellIdentifier); - setDatePickerLeft(left); - setDatePickerTop(top + 40); - setShowDatePicker(true); - }; - - const onOpenOptionDetailClick = (_left: number, _top: number, _select_option: SelectOptionPB) => { - setEditingSelectOption(_select_option); - setShowEditCellOption(true); - setEditCellOptionLeft(_left); - setEditCellOptionTop(_top); - }; - - const onOpenCheckListDetailClick = (_left: number, _top: number, _select_option: SelectOptionPB) => { - setEditingSelectOption(_select_option); - setShowEditCheckList(true); - setEditCheckListLeft(_left + 10); - setEditCheckListTop(_top); - }; - - const onEditCheckListClick = (cellIdentifier: CellIdentifier, left: number, top: number) => { - setEditingCell(cellIdentifier); - setShowCheckListPopup(true); - setCheckListPopupLeft(left); - setCheckListPopupTop(top + 40); - }; - - const onDragEnd: OnDragEndResponder = (result) => { - if (!result.destination?.index) return; - const fields = cells - .filter((cell) => { - return fieldsStore[cell.cellIdentifier.fieldId]?.visible; - }); - - void controller.moveField({ - fromFieldId: result.draggableId, - toFieldId: fields[result.source.index].fieldId, - }); - }; - - const onDeletePropertyClick = (fieldId: string) => { - setDeletingPropertyId(fieldId); - setShowDeletePropertyPrompt(true); - }; - - const onDelete = async () => { - if (!deletingPropertyId) return; - const fieldInfo = controller.fieldController.getField(deletingPropertyId); - - if (!fieldInfo) return; - const typeController = new TypeOptionController(viewId, Some(fieldInfo)); - - setEditingCell(null); - - await typeController.initialize(); - await typeController.deleteField(); - setShowDeletePropertyPrompt(false); - }; - - return ( - <> - <div - className={`fixed inset-0 z-10 flex items-center justify-center bg-black/30 backdrop-blur-sm transition-opacity duration-300 ${unveil ? 'opacity-100' : 'opacity-0' - }`} - onClick={() => onCloseClick()} - > - <div - onClick={(e) => { - e.stopPropagation(); - }} - className={`relative flex h-[90%] w-[70%] flex-col gap-8 rounded-xl bg-bg-body `} - > - <div onClick={() => onCloseClick()} className={'absolute right-1 top-1'}> - <button className={'block h-8 w-8 rounded-lg text-text-title hover:bg-fill-list-hover'}> - <CloseSvg></CloseSvg> - </button> - </div> - - <div className={'flex h-full'}> - <div className={'flex h-full flex-1 flex-col border-r border-line-divider pb-4 pt-6'}> - <div className={'pb-4 pl-12'}> - <button className={'flex items-center gap-2 p-4'}> - <i className={'h-5 w-5'}> - <ImageSvg></ImageSvg> - </i> - <span className={'text-xs'}>Add Cover</span> - </button> - </div> - - <DragDropContext onDragEnd={onDragEnd}> - <Droppable droppableId={'field-list'}> - {(provided) => ( - <div - {...provided.droppableProps} - ref={provided.innerRef} - className={`flex flex-1 flex-col gap-8 px-8 pb-8 ${showFieldEditor || showChangeOptionsPopup || showDatePicker ? 'overflow-hidden' : 'overflow-auto' - }`} - > - {cells - .filter((cell) => { - return fieldsStore[cell.cellIdentifier.fieldId]?.visible; - }) - .map((cell, cellIndex) => ( - <EditCellWrapper - index={cellIndex} - key={cellIndex} - cellIdentifier={cell.cellIdentifier} - cellCache={controller.databaseViewCache.getRowCache().getCellCache()} - fieldController={controller.fieldController} - onEditFieldClick={onEditFieldClick} - onEditOptionsClick={onEditOptionsClick} - onEditDateClick={onEditDateClick} - onEditCheckListClick={onEditCheckListClick} - ></EditCellWrapper> - ))} - </div> - )} - </Droppable> - </DragDropContext> - - <div className={'border-t border-line-divider px-8 pt-2'}> - <button - onClick={() => onNewColumnClick()} - className={'flex w-full items-center gap-2 rounded-lg px-4 py-2 hover:bg-fill-list-hover'} - > - <i className={'h-5 w-5'}> - <AddSvg></AddSvg> - </i> - <span>{t('grid.field.newProperty')}</span> - </button> - </div> - </div> - <PropertiesPanel - viewId={viewId} - controller={controller} - rowInfo={rowInfo} - onDeletePropertyClick={onDeletePropertyClick} - onNewColumnClick={onNewColumnClick} - ></PropertiesPanel> - </div> - - {editingCell && ( - <EditFieldPopup - open={showFieldEditor} - anchorEl={editFieldAnchorEl} - cellIdentifier={editingCell} - viewId={viewId} - onOutsideClick={onOutsideEditFieldClick} - controller={controller} - changeFieldTypeClick={onChangeFieldTypeClick} - onDeletePropertyClick={onDeletePropertyClick} - ></EditFieldPopup> - )} - {showChangeFieldTypePopup && ( - <ChangeFieldTypePopup - open={showChangeFieldTypePopup} - anchorEl={changeFieldTypeAnchorEl} - onClick={(newType) => changeFieldType(newType)} - onOutsideClick={() => setShowChangeFieldTypePopup(false)} - ></ChangeFieldTypePopup> - )} - {showChangeOptionsPopup && editingCell && ( - <CellOptionsPopup - top={changeOptionsTop} - left={changeOptionsLeft} - cellIdentifier={editingCell} - cellCache={controller.databaseViewCache.getRowCache().getCellCache()} - fieldController={controller.fieldController} - onOutsideClick={() => !showEditCellOption && setShowChangeOptionsPopup(false)} - openOptionDetail={onOpenOptionDetailClick} - ></CellOptionsPopup> - )} - {showDatePicker && editingCell && ( - <DatePickerPopup - top={datePickerTop} - left={datePickerLeft} - cellIdentifier={editingCell} - cellCache={controller.databaseViewCache.getRowCache().getCellCache()} - fieldController={controller.fieldController} - onOutsideClick={() => setShowDatePicker(false)} - ></DatePickerPopup> - )} - {showEditCellOption && editingCell && editingSelectOption && ( - <EditCellOptionPopup - top={editCellOptionTop} - left={editCellOptionLeft} - cellIdentifier={editingCell} - editingSelectOption={editingSelectOption} - setEditingSelectOption={setEditingSelectOption} - onOutsideClick={() => { - setShowEditCellOption(false); - }} - ></EditCellOptionPopup> - )} - {showNumberFormatPopup && editingCell && ( - <NumberFormatPopup - top={numberFormatTop} - left={numberFormatLeft} - cellIdentifier={editingCell} - fieldController={controller.fieldController} - onOutsideClick={() => { - setShowNumberFormatPopup(false); - }} - ></NumberFormatPopup> - )} - {showCheckListPopup && editingCell && ( - <CheckListPopup - top={checkListPopupTop} - left={checkListPopupLeft} - cellIdentifier={editingCell} - cellCache={controller.databaseViewCache.getRowCache().getCellCache()} - fieldController={controller.fieldController} - onOutsideClick={() => !showEditCheckList && setShowCheckListPopup(false)} - openCheckListDetail={onOpenCheckListDetailClick} - ></CheckListPopup> - )} - {showEditCheckList && editingCell && editingSelectOption && ( - <EditCheckListPopup - top={editCheckListTop} - left={editCheckListLeft} - cellIdentifier={editingCell} - editingSelectOption={editingSelectOption} - onOutsideClick={() => setShowEditCheckList(false)} - ></EditCheckListPopup> - )} - </div> - </div> - {showDeletePropertyPrompt && ( - <PromptWindow - msg={'Are you sure you want to delete this property?'} - onYes={() => onDelete()} - onCancel={() => setShowDeletePropertyPrompt(false)} - ></PromptWindow> - )} - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeIcon.tsx deleted file mode 100644 index 8c82601cba..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeIcon.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { FieldType } from '@/services/backend'; -import { TextTypeSvg } from '$app/components/_shared/svg/TextTypeSvg'; -import { NumberTypeSvg } from '$app/components/_shared/svg/NumberTypeSvg'; -import { DateTypeSvg } from '$app/components/_shared/svg/DateTypeSvg'; -import { SingleSelectTypeSvg } from '$app/components/_shared/svg/SingleSelectTypeSvg'; -import { MultiSelectTypeSvg } from '$app/components/_shared/svg/MultiSelectTypeSvg'; -import { ChecklistTypeSvg } from '$app/components/_shared/svg/ChecklistTypeSvg'; -import { UrlTypeSvg } from '$app/components/_shared/svg/UrlTypeSvg'; -import { CheckboxSvg } from '$app/components/_shared/svg/CheckboxSvg'; - -export const FieldTypeIcon = ({ fieldType }: { fieldType: FieldType }) => { - return ( - <> - {fieldType === FieldType.RichText && <TextTypeSvg></TextTypeSvg>} - {fieldType === FieldType.Number && <NumberTypeSvg></NumberTypeSvg>} - {fieldType === FieldType.DateTime && <DateTypeSvg></DateTypeSvg>} - {fieldType === FieldType.SingleSelect && <SingleSelectTypeSvg></SingleSelectTypeSvg>} - {fieldType === FieldType.MultiSelect && <MultiSelectTypeSvg></MultiSelectTypeSvg>} - {fieldType === FieldType.Checklist && <ChecklistTypeSvg></ChecklistTypeSvg>} - {fieldType === FieldType.URL && <UrlTypeSvg></UrlTypeSvg>} - {fieldType === FieldType.Checkbox && <CheckboxSvg></CheckboxSvg>} - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeName.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeName.tsx deleted file mode 100644 index 06d1f601ce..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/FieldTypeName.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { FieldType } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; - -export const FieldTypeName = ({ fieldType }: { fieldType: FieldType }) => { - const { t } = useTranslation(); - - return ( - <> - {fieldType === FieldType.RichText && t('grid.field.textFieldName')} - {fieldType === FieldType.Number && t('grid.field.numberFieldName')} - {fieldType === FieldType.DateTime && t('grid.field.dateFieldName')} - {fieldType === FieldType.SingleSelect && t('grid.field.singleSelectFieldName')} - {fieldType === FieldType.MultiSelect && t('grid.field.multiSelectFieldName')} - {fieldType === FieldType.Checklist && t('grid.field.checklistFieldName')} - {fieldType === FieldType.URL && t('grid.field.urlFieldName')} - {fieldType === FieldType.Checkbox && t('grid.field.checkboxFieldName')} - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellNumber.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellNumber.tsx deleted file mode 100644 index 381f25ae3e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellNumber.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useEffect, useState } from 'react'; - -export const EditCellNumber = ({ data, onSave }: { data: string | undefined; onSave: (value: string) => void }) => { - const [value, setValue] = useState(''); - - useEffect(() => { - setValue(data ?? ''); - }, [data]); - - // const save = async () => { - // await cellController?.saveCellData(value); - // }; - - return ( - <input - value={value} - onChange={(e) => setValue(e.target.value)} - onBlur={() => onSave(value)} - className={'w-full px-4 py-1'} - ></input> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellText.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellText.tsx deleted file mode 100644 index 8a92b71ce4..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellText.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useEffect, useState } from 'react'; - -export const EditCellText = ({ data, onSave }: { data: string | undefined; onSave: (value: string) => void }) => { - const [value, setValue] = useState(''); - const [contentRows, setContentRows] = useState(1); - - useEffect(() => { - setValue(data ?? ''); - }, [data]); - - useEffect(() => { - if (!value?.length) return; - setContentRows(Math.max(1, (value ?? '').split('\n').length)); - }, [value]); - - const onTextFieldChange = async (v: string) => { - setValue(v); - }; - - return ( - <div> - <textarea - className={'mt-0.5 h-full w-full resize-none px-4 py-1'} - rows={contentRows} - value={value} - onChange={(e) => onTextFieldChange(e.target.value)} - onBlur={() => onSave(value)} - /> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellUrl.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellUrl.tsx deleted file mode 100644 index 4a23fd448f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCellUrl.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { URLCellDataPB } from '@/services/backend'; -import { useEffect, useState } from 'react'; - -export const EditCellUrl = ({ data, onSave }: { data: URLCellDataPB | undefined; onSave: (value: string) => void }) => { - const [value, setValue] = useState(''); - - useEffect(() => { - setValue((data as URLCellDataPB)?.url ?? ''); - }, [data]); - - return ( - <input - value={value} - onChange={(e) => setValue(e.target.value)} - onBlur={() => onSave(value)} - className={'w-full px-4 py-1'} - ></input> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCheckboxCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCheckboxCell.tsx deleted file mode 100644 index efed3a23ba..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/InlineEditFields/EditCheckboxCell.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; -import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; - -export const EditCheckboxCell = ({ data, onToggle }: { data: 'Yes' | 'No' | undefined; onToggle: () => void }) => { - // const toggleValue = async () => { - // if (data === 'Yes') { - // await cellController?.saveCellData('No'); - // } else { - // await cellController?.saveCellData('Yes'); - // } - // }; - - return ( - <div onClick={() => onToggle()} className={'block px-4 py-1'}> - <button className={'h-5 w-5'}> - {data === 'Yes' ? <EditorCheckSvg></EditorCheckSvg> : <EditorUncheckSvg></EditorUncheckSvg>} - </button> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOption.tsx deleted file mode 100644 index 260c85c2e5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOption.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { SelectOptionColorPB, SelectOptionPB } from '@/services/backend'; -import { getBgColor } from '$app/components/_shared/getColor'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; -import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; -import { ISelectOption } from '$app_reducers/database/slice'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { MouseEventHandler } from 'react'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; - -export const CellOption = ({ - option, - checked, - cellIdentifier, - openOptionDetail, - clearValue, - noSelect, - noDetail, - onOptionClick, -}: { - option: ISelectOption; - checked: boolean; - cellIdentifier?: CellIdentifier; - openOptionDetail?: (_left: number, _top: number, _select_option: SelectOptionPB) => void; - clearValue?: () => void; - noSelect?: boolean; - noDetail?: boolean; - onOptionClick?: () => void; -}) => { - const onOptionDetailClick: MouseEventHandler = (e) => { - e.stopPropagation(); - let target = e.target as HTMLElement; - - while (!(target instanceof HTMLButtonElement)) { - if (target.parentElement === null) return; - target = target.parentElement; - } - - const selectOption = new SelectOptionPB({ - id: option.selectOptionId, - name: option.title, - color: option.color ?? SelectOptionColorPB.Purple, - }); - - const { right: _left, top: _top } = target.getBoundingClientRect(); - - openOptionDetail?.(_left, _top, selectOption); - }; - - const onToggleOptionClick: MouseEventHandler = async () => { - onOptionClick?.(); - if (noSelect || !cellIdentifier) return; - if (checked) { - await new SelectOptionCellBackendService(cellIdentifier).unselectOption([option.selectOptionId]); - } else { - await new SelectOptionCellBackendService(cellIdentifier).selectOption([option.selectOptionId]); - } - - clearValue?.(); - }; - - return ( - <div - onClick={onToggleOptionClick} - className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-1.5 hover:bg-fill-list-hover'} - > - <div className={`${getBgColor(option.color)} rounded px-2 py-0.5 text-text-title`}>{option.title}</div> - <div className={'flex items-center'}> - {checked && ( - <button className={'h-5 w-5 p-1'}> - <CheckmarkSvg></CheckmarkSvg> - </button> - )} - {!noDetail && ( - <button onClick={onOptionDetailClick} className={'h-6 w-6 p-1'}> - <Details2Svg></Details2Svg> - </button> - )} - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOptions.tsx deleted file mode 100644 index d77c075cd3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOptions.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { SelectOptionCellDataPB } from '@/services/backend'; -import { getBgColor } from '$app/components/_shared/getColor'; -import { MouseEventHandler, useRef } from 'react'; - -export const CellOptions = ({ - data, - onEditClick, -}: { - data: SelectOptionCellDataPB | undefined; - onEditClick: (left: number, top: number) => void; -}) => { - const ref = useRef<HTMLDivElement>(null); - - const onClick: MouseEventHandler = () => { - if (!ref.current) return; - const { left, top } = ref.current.getBoundingClientRect(); - - onEditClick(left, top); - }; - - return ( - <div ref={ref} onClick={onClick} className={'flex w-full flex-wrap items-center gap-2 px-4 py-1 text-xs'}> - {data?.select_options?.map((option, index) => ( - <div className={`${getBgColor(option.color)} rounded px-2 py-0.5 text-text-title`} key={index}> - {option?.name ?? ''} - </div> - ))} - - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOptionsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOptionsPopup.tsx deleted file mode 100644 index 05262d7068..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/CellOptionsPopup.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { KeyboardEventHandler, useEffect, useRef, useState } from 'react'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { SelectOptionCellDataPB, SelectOptionPB } from '@/services/backend'; -import { useTranslation } from 'react-i18next'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { useAppSelector } from '$app/stores/store'; -import { ISelectOptionType } from '$app_reducers/database/slice'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { CellOption } from '$app/components/_shared/EditRow/Options/CellOption'; -import { SelectedOption } from '$app/components/_shared/EditRow/Options/SelectedOption'; - -export const CellOptionsPopup = ({ - top, - left, - cellIdentifier, - cellCache, - fieldController, - onOutsideClick, - openOptionDetail, -}: { - top: number; - left: number; - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; - onOutsideClick: () => void; - openOptionDetail: (_left: number, _top: number, _select_option: SelectOptionPB) => void; -}) => { - const inputRef = useRef<HTMLInputElement>(null); - const { t } = useTranslation(); - const [value, setValue] = useState(''); - const { data } = useCell(cellIdentifier, cellCache, fieldController); - const databaseStore = useAppSelector((state) => state.database); - - useEffect(() => { - if (inputRef?.current) { - inputRef.current.focus(); - } - }, [inputRef]); - - const onKeyDown: KeyboardEventHandler = async (e) => { - if (e.key === 'Enter' && value.length > 0) { - await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: value, isSelect: true }); - setValue(''); - } - }; - - const onKeyDownWrapper: KeyboardEventHandler = (e) => { - if (e.key === 'Escape') { - onOutsideClick(); - } - }; - - return ( - <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}> - <div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}> - <div - className={ - 'flex flex-1 items-center gap-2 rounded border border-line-divider px-2 hover:border-fill-default focus:border-fill-default' - } - > - <div className={'flex flex-wrap items-center gap-2 text-text-title'}> - {(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => ( - <SelectedOption - option={option} - key={index} - cellIdentifier={cellIdentifier} - clearValue={() => setValue('')} - ></SelectedOption> - ))} - </div> - <input - ref={inputRef} - className={'py-2'} - value={value} - onChange={(e) => setValue(e.target.value)} - placeholder={t('grid.selectOption.searchOption') ?? ''} - onKeyDown={onKeyDown} - /> - <div className={'font-mono text-text-caption'}>{value.length}/30</div> - </div> - <div className={'-mx-4 h-[1px] bg-line-divider'}></div> - <div className={'font-medium text-text-caption'}>{t('grid.selectOption.panelTitle') ?? ''}</div> - <div className={'flex flex-col gap-1'}> - {(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map( - (option, index) => ( - <CellOption - key={index} - option={option} - checked={ - !!(data as SelectOptionCellDataPB)?.select_options?.find( - (selectedOption) => selectedOption.id === option.selectOptionId - ) - } - cellIdentifier={cellIdentifier} - openOptionDetail={openOptionDetail} - clearValue={() => setValue('')} - ></CellOption> - ) - )} - </div> - </div> - </PopupWindow> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/EditCellOptionPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/EditCellOptionPopup.tsx deleted file mode 100644 index 3b6ef80e06..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/EditCellOptionPopup.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { KeyboardEventHandler, useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { SelectOptionColorPB, SelectOptionPB } from '@/services/backend'; -import { getBgColor } from '$app/components/_shared/getColor'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { databaseActions, ISelectOptionType } from '$app_reducers/database/slice'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; - -export const EditCellOptionPopup = ({ - left, - top, - cellIdentifier, - editingSelectOption, - setEditingSelectOption, - onOutsideClick, -}: { - left: number; - top: number; - cellIdentifier: CellIdentifier; - editingSelectOption: SelectOptionPB; - setEditingSelectOption: (option: SelectOptionPB) => void; - onOutsideClick: () => void; -}) => { - const inputRef = useRef<HTMLInputElement>(null); - const { t } = useTranslation(); - const [value, setValue] = useState(''); - const fieldsStore = useAppSelector((state) => state.database.fields); - const dispatch = useAppDispatch(); - - useEffect(() => { - setValue(editingSelectOption.name); - }, [editingSelectOption]); - - const onKeyDown: KeyboardEventHandler = async (e) => { - if (e.key === 'Enter' && value.length > 0) { - await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: value }); - setValue(''); - } - }; - - const onKeyDownWrapper: KeyboardEventHandler = (e) => { - if (e.key === 'Escape') { - onOutsideClick(); - } - }; - - const onBlur = async () => { - const svc = new SelectOptionCellBackendService(cellIdentifier); - - await svc.updateOption( - new SelectOptionPB({ - id: editingSelectOption.id, - color: editingSelectOption.color, - name: value, - }) - ); - }; - - const onUpdateSelectOption = (option: SelectOptionPB) => { - const updatingField = fieldsStore[cellIdentifier.fieldId]; - const allOptions = (updatingField.fieldOptions as ISelectOptionType).selectOptions; - - dispatch( - databaseActions.updateField({ - field: { - ...updatingField, - fieldOptions: { - selectOptions: allOptions.map((o) => - o.selectOptionId === option.id - ? { - selectOptionId: option.id, - color: option.color, - title: option.name, - } - : o - ), - }, - }, - }) - ); - - setEditingSelectOption(option); - }; - - const onColorClick = async (color: SelectOptionColorPB) => { - const svc = new SelectOptionCellBackendService(cellIdentifier); - - const updatedOption = new SelectOptionPB({ - id: editingSelectOption.id, - color, - name: editingSelectOption.name, - }); - - await svc.updateOption(updatedOption); - onUpdateSelectOption(updatedOption); - }; - - const onDeleteOptionClick = async () => { - const svc = new SelectOptionCellBackendService(cellIdentifier); - - await svc.deleteOption([editingSelectOption]); - onOutsideClick(); - }; - - return ( - <PopupWindow - className={'p-2 text-xs'} - onOutsideClick={async () => { - await onBlur(); - onOutsideClick(); - }} - left={left} - top={top} - > - <div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}> - <div - className={ - 'flex flex-1 items-center gap-2 rounded border border-line-divider px-2 hover:border-fill-hover focus:border-fill-hover' - } - > - <input - ref={inputRef} - className={'py-2'} - value={value} - onChange={(e) => setValue(e.target.value)} - onKeyDown={onKeyDown} - onBlur={() => onBlur()} - /> - <div className={'font-mono text-text-caption'}>{value.length}/30</div> - </div> - <button - onClick={() => onDeleteOptionClick()} - className={ - 'text-main-alert flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-list-hover' - } - > - <i className={'h-5 w-5'}> - <TrashSvg></TrashSvg> - </i> - <span>{t('grid.selectOption.deleteTag')}</span> - </button> - <div className={'-mx-4 h-[1px] bg-line-divider'}></div> - <div className={'my-2 font-medium text-text-caption'}>{t('grid.selectOption.colorPanelTitle')}</div> - <div className={'flex flex-col'}> - <ColorItem - title={t('grid.selectOption.purpleColor')} - onClick={() => onColorClick(SelectOptionColorPB.Purple)} - bgColor={getBgColor(SelectOptionColorPB.Purple)} - checked={editingSelectOption.color === SelectOptionColorPB.Purple} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.pinkColor')} - onClick={() => onColorClick(SelectOptionColorPB.Pink)} - bgColor={getBgColor(SelectOptionColorPB.Pink)} - checked={editingSelectOption.color === SelectOptionColorPB.Pink} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.lightPinkColor')} - onClick={() => onColorClick(SelectOptionColorPB.LightPink)} - bgColor={getBgColor(SelectOptionColorPB.LightPink)} - checked={editingSelectOption.color === SelectOptionColorPB.LightPink} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.orangeColor')} - onClick={() => onColorClick(SelectOptionColorPB.Orange)} - bgColor={getBgColor(SelectOptionColorPB.Orange)} - checked={editingSelectOption.color === SelectOptionColorPB.Orange} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.yellowColor')} - onClick={() => onColorClick(SelectOptionColorPB.Yellow)} - bgColor={getBgColor(SelectOptionColorPB.Yellow)} - checked={editingSelectOption.color === SelectOptionColorPB.Yellow} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.limeColor')} - onClick={() => onColorClick(SelectOptionColorPB.Lime)} - bgColor={getBgColor(SelectOptionColorPB.Lime)} - checked={editingSelectOption.color === SelectOptionColorPB.Lime} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.greenColor')} - onClick={() => onColorClick(SelectOptionColorPB.Green)} - bgColor={getBgColor(SelectOptionColorPB.Green)} - checked={editingSelectOption.color === SelectOptionColorPB.Green} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.aquaColor')} - onClick={() => onColorClick(SelectOptionColorPB.Aqua)} - bgColor={getBgColor(SelectOptionColorPB.Aqua)} - checked={editingSelectOption.color === SelectOptionColorPB.Aqua} - ></ColorItem> - <ColorItem - title={t('grid.selectOption.blueColor')} - onClick={() => onColorClick(SelectOptionColorPB.Blue)} - bgColor={getBgColor(SelectOptionColorPB.Blue)} - checked={editingSelectOption.color === SelectOptionColorPB.Blue} - ></ColorItem> - </div> - </div> - </PopupWindow> - ); -}; - -const ColorItem = ({ - title, - bgColor, - onClick, - checked, -}: { - title: string; - bgColor: string; - onClick: () => void; - checked: boolean; -}) => { - return ( - <div - className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-fill-list-hover'} - onClick={() => onClick()} - > - <div className={'flex items-center gap-2'}> - <div className={`h-4 w-4 rounded-full ${bgColor}`}></div> - <span>{title}</span> - </div> - {checked && ( - <i className={'block h-3 w-3'}> - <CheckmarkSvg></CheckmarkSvg> - </i> - )} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/MultiSelectTypeOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/MultiSelectTypeOptions.tsx deleted file mode 100644 index f7a87e5507..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/MultiSelectTypeOptions.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { ISelectOptionType } from '$app_reducers/database/slice'; -import { CellOption } from '$app/components/_shared/EditRow/Options/CellOption'; -import { SelectOptionPB } from '@/services/backend'; -import { useAppSelector } from '$app/stores/store'; -import { KeyboardEventHandler, useEffect, useRef, useState } from 'react'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; - -export const MultiSelectTypeOptions = ({ - cellIdentifier, - openOptionDetail, -}: { - cellIdentifier: CellIdentifier; - openOptionDetail?: (_left: number, _top: number, _select_option: SelectOptionPB) => void; -}) => { - const inputRef = useRef<HTMLInputElement>(null); - const inputContainerRef = useRef<HTMLDivElement>(null); - const fieldsStore = useAppSelector((state) => state.database.fields); - const [value, setValue] = useState(''); - const [showInput, setShowInput] = useState(false); - const [newInputWidth, setNewInputWidth] = useState(0); - - const onKeyDown: KeyboardEventHandler = async (e) => { - if (e.key === 'Enter' && value.length > 0) { - await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: value, isSelect: false }); - setValue(''); - } - - if (e.key === 'Escape') { - setShowInput(false); - } - }; - - useEffect(() => { - if (inputRef?.current && showInput) { - inputRef.current.focus(); - } - }, [inputRef, showInput, newInputWidth]); - - useEffect(() => { - if (inputContainerRef?.current && showInput) { - setNewInputWidth(inputContainerRef.current.getBoundingClientRect().width - 56); - } else { - setNewInputWidth(0); - } - }, [inputContainerRef, showInput]); - - return ( - <div className={'flex flex-col'}> - <hr className={'-mx-2 my-2 border-line-divider'} /> - <div className={'flex flex-col gap-1'}> - <div className={'flex items-center justify-between px-3 py-1.5'}> - <div>Options</div> - {!showInput && <button onClick={() => setShowInput(true)}>Add option</button>} - </div> - {showInput && ( - <div - ref={inputContainerRef} - className={`border-shades-3 bg-main-selector flex items-center gap-2 rounded border px-2`} - > - {newInputWidth > 0 && ( - <input - ref={inputRef} - style={{ width: newInputWidth }} - className={'py-2'} - value={value} - onChange={(e) => setValue(e.target.value)} - onBlur={() => setShowInput(false)} - onKeyDown={onKeyDown} - /> - )} - <div className={'font-mono text-text-caption'}>{value.length}/30</div> - </div> - )} - - {(fieldsStore[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map((option, index) => ( - <CellOption - key={index} - option={option} - noSelect={true} - checked={false} - cellIdentifier={cellIdentifier} - openOptionDetail={openOptionDetail} - clearValue={() => setValue('')} - ></CellOption> - ))} - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx deleted file mode 100644 index f261aff2b9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/Options/SelectedOption.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { getBgColor } from '$app/components/_shared/getColor'; -import { CloseSvg } from '$app/components/_shared/svg/CloseSvg'; -import { SelectOptionPB } from '@/services/backend'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { MouseEventHandler } from 'react'; - -export const SelectedOption = ({ - option, - cellIdentifier, - clearValue, -}: { - option: SelectOptionPB; - cellIdentifier: CellIdentifier; - clearValue: () => void; -}) => { - const onUnselectOptionClick: MouseEventHandler = async () => { - await new SelectOptionCellBackendService(cellIdentifier).unselectOption([option.id]); - clearValue(); - }; - - return ( - <div className={`${getBgColor(option.color)} flex items-center gap-0.5 rounded px-1 py-0.5 text-content-on-fill`}> - <span className={'text-text-title'}>{option?.name ?? ''}</span> - <button onClick={onUnselectOptionClick} className={'h-5 w-5 cursor-pointer text-text-title'}> - <CloseSvg></CloseSvg> - </button> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/PropertiesPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/PropertiesPanel.tsx deleted file mode 100644 index 35feec5a5d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/PropertiesPanel.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; -import { useState } from 'react'; -import { useRow } from '$app/components/_shared/database-hooks/useRow'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { useAppSelector } from '$app/stores/store'; -import { Switch } from '$app/components/_shared/Switch'; -import { FieldType } from '@/services/backend'; -import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { MultiSelectTypeSvg } from '$app/components/_shared/svg/MultiSelectTypeSvg'; -import { DocumentSvg } from '$app/components/_shared/svg/DocumentSvg'; -import { SingleSelectTypeSvg } from '$app/components/_shared/svg/SingleSelectTypeSvg'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { Some } from 'ts-results'; -import { useTranslation } from 'react-i18next'; - -const typesOrder: FieldType[] = [ - FieldType.RichText, - FieldType.Number, - FieldType.DateTime, - FieldType.SingleSelect, - FieldType.MultiSelect, - FieldType.Checkbox, - FieldType.URL, - FieldType.Checklist, -]; - -export const PropertiesPanel = ({ - viewId, - controller, - rowInfo, - onDeletePropertyClick, - onNewColumnClick, -}: { - viewId: string; - controller: DatabaseController; - rowInfo: RowInfo; - onDeletePropertyClick: (fieldId: string) => void; - onNewColumnClick: (initialFieldType: FieldType, name?: string) => Promise<void>; -}) => { - const { cells } = useRow(viewId, controller, rowInfo); - const databaseStore = useAppSelector((state) => state.database); - const { t } = useTranslation(); - - const [showAddedProperties, setShowAddedProperties] = useState(true); - const [showBasicProperties, setShowBasicProperties] = useState(false); - const [showAdvancedProperties, setShowAdvancedProperties] = useState(false); - - const [hoveredPropertyIndex, setHoveredPropertyIndex] = useState(-1); - - const toggleHideProperty = async (v: boolean, index: number) => { - const fieldInfo = controller.fieldController.getField(cells[index].fieldId); - - if (fieldInfo) { - const typeController = new TypeOptionController(viewId, Some(fieldInfo)); - - await typeController.initialize(); - if (fieldInfo.field.visibility) { - await typeController.hideField(); - } else { - await typeController.showField(); - } - } - }; - - const addSelectedFieldType = async (fieldType: FieldType) => { - let name = 'New Field'; - - switch (fieldType) { - case FieldType.RichText: - name = t('grid.field.textFieldName'); - break; - case FieldType.Number: - name = t('grid.field.numberFieldName'); - break; - case FieldType.DateTime: - name = t('grid.field.dateFieldName'); - break; - case FieldType.SingleSelect: - name = t('grid.field.singleSelectFieldName'); - break; - case FieldType.MultiSelect: - name = t('grid.field.multiSelectFieldName'); - break; - case FieldType.Checklist: - name = t('grid.field.checklistFieldName'); - break; - case FieldType.URL: - name = t('grid.field.urlFieldName'); - break; - case FieldType.Checkbox: - name = t('grid.field.checkboxFieldName'); - break; - } - - await onNewColumnClick(fieldType, name); - }; - - return ( - <div className={'flex flex-col gap-2 overflow-auto px-4 py-12'}> - <div - onClick={() => setShowAddedProperties(!showAddedProperties)} - className={ - 'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 text-text-title hover:bg-fill-list-active' - } - > - <div className={'text-sm'}>Added Properties</div> - <i className={`h-5 w-5 transition-transform duration-500 ${showAddedProperties && 'rotate-180'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - <div className={'flex flex-col text-xs'} onMouseLeave={() => setHoveredPropertyIndex(-1)}> - {showAddedProperties && - cells.map((cell, cellIndex) => ( - <div - key={cellIndex} - onMouseEnter={() => setHoveredPropertyIndex(cellIndex)} - className={ - 'flex cursor-pointer items-center justify-between gap-4 rounded-lg px-2 py-1 hover:bg-fill-list-hover' - } - > - <div className={'flex items-center gap-2 text-text-title '}> - <div className={'flex h-5 w-5 flex-shrink-0 items-center justify-center'}> - <FieldTypeIcon fieldType={cell.cellIdentifier.fieldType}></FieldTypeIcon> - </div> - <span className={'overflow-hidden text-ellipsis whitespace-nowrap'}> - {databaseStore.fields[cell.cellIdentifier.fieldId]?.title ?? ''} - </span> - </div> - <div className={'flex items-center'}> - <i - onClick={() => onDeletePropertyClick(cell.cellIdentifier.fieldId)} - className={`h-[16px] w-[16px] text-text-title transition-opacity duration-300 ${ - hoveredPropertyIndex === cellIndex ? 'opacity-100' : 'opacity-0' - }`} - > - <TrashSvg></TrashSvg> - </i> - <Switch - value={!!databaseStore.fields[cell.cellIdentifier.fieldId]?.visible} - setValue={(v) => toggleHideProperty(v, cellIndex)} - ></Switch> - </div> - </div> - ))} - </div> - <div - onClick={() => setShowBasicProperties(!showBasicProperties)} - className={ - 'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-fill-list-active' - } - > - <div className={'text-sm'}>Basic Properties</div> - <i className={`h-5 w-5 transition-transform duration-500 ${showBasicProperties && 'rotate-180'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - <div className={'flex flex-col gap-2 text-xs'}> - {showBasicProperties && ( - <div className={'flex flex-col'}> - {typesOrder.map((type, i) => ( - <button - onClick={() => addSelectedFieldType(type)} - key={i} - className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'h-5 w-5'}> - <FieldTypeIcon fieldType={type}></FieldTypeIcon> - </i> - <span> - <FieldTypeName fieldType={type}></FieldTypeName> - </span> - </button> - ))} - </div> - )} - </div> - <div - onClick={() => setShowAdvancedProperties(!showAdvancedProperties)} - className={ - 'flex cursor-pointer items-center justify-between gap-8 rounded-lg px-2 py-2 hover:bg-fill-list-active' - } - > - <div className={'text-sm'}>Advanced Properties</div> - <i className={`h-5 w-5 transition-transform duration-500 ${showAdvancedProperties && 'rotate-180'}`}> - <DropDownShowSvg></DropDownShowSvg> - </i> - </div> - <div className={'flex flex-col gap-2 text-xs'}> - {showAdvancedProperties && ( - <div className={'flex flex-col'}> - <button - className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'h-5 w-5'}> - <MultiSelectTypeSvg></MultiSelectTypeSvg> - </i> - <span>Last edited time</span> - </button> - <button - className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'h-5 w-5'}> - <DocumentSvg></DocumentSvg> - </i> - <span>Document</span> - </button> - <button - className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-fill-list-hover'} - > - <i className={'h-5 w-5'}> - <SingleSelectTypeSvg></SingleSelectTypeSvg> - </i> - <span>Relation to</span> - </button> - </div> - )} - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/LanguageSelectPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/LanguageSelectPopup.tsx deleted file mode 100644 index cb77d8cdeb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/LanguageSelectPopup.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { IPopupItem, PopupSelect } from './PopupSelect'; -import i18n from 'i18next'; - -const supportedLanguages: { key: string; title: string }[] = [ - { - key: 'en', - title: 'English', - }, - { key: 'ar-SA', title: 'ar-SA' }, - { key: 'ca-ES', title: 'ca-ES' }, - { key: 'de-DE', title: 'de-DE' }, - { key: 'es-VE', title: 'es-VE' }, - { key: 'eu-ES', title: 'eu-ES' }, - { key: 'fr-CA', title: 'fr-CA' }, - { key: 'fr-FR', title: 'fr-FR' }, - { key: 'hu-HU', title: 'hu-HU' }, - { key: 'id-ID', title: 'id-ID' }, - { key: 'it-IT', title: 'it-IT' }, - { key: 'ja-JP', title: 'ja-JP' }, - { key: 'ko-KR', title: 'ko-KR' }, - { key: 'pl-PL', title: 'pl-PL' }, - { key: 'pt-BR', title: 'pt-BR' }, - { key: 'pt-PT', title: 'pt-PT' }, - { key: 'ru-RU', title: 'ru-RU' }, - { key: 'sv', title: 'sv' }, - { key: 'th-TH', title: 'th-TH' }, - { key: 'tr-TR', title: 'tr-TR' }, - { key: 'zh-CN', title: 'zh-CN' }, - { key: 'zh-TW', title: 'zh-TW' }, -]; - -export const LanguageSelectPopup = ({ onClose }: { onClose: () => void }) => { - const items: IPopupItem[] = supportedLanguages.map<IPopupItem>((item) => ({ - onClick: () => { - void i18n.changeLanguage(item.key); - onClose(); - }, - title: item.title, - icon: <></>, - })); - - return ( - <PopupSelect - items={items} - className={'absolute top-full right-0 z-10 w-[200px]'} - onOutsideClick={onClose} - columns={2} - ></PopupSelect> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupSelect.tsx deleted file mode 100644 index 6891ce540e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupSelect.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { CSSProperties, MouseEvent, ReactNode, useRef } from 'react'; -import useOutsideClick from './useOutsideClick'; - -export interface IPopupItem { - icon: ReactNode | (() => JSX.Element); - title: string; - onClick: () => void; -} - -export const PopupSelect = ({ - items, - className = '', - onOutsideClick, - columns = 1, - style, -}: { - items: IPopupItem[]; - className: string; - onOutsideClick?: () => void; - columns?: 1 | 2 | 3; - style?: CSSProperties; -}) => { - const ref = useRef<HTMLDivElement>(null); - - useOutsideClick(ref, () => onOutsideClick && onOutsideClick()); - - const handleClick = (e: MouseEvent, item: IPopupItem) => { - e.stopPropagation(); - item.onClick(); - }; - - return ( - <div ref={ref} className={`${className} rounded-lg bg-bg-body px-2 py-2 text-text-title shadow-md`} style={style}> - <div - className={ - (columns === 2 ? 'grid grid-cols-2' : '') + (columns === 3 ? 'grid grid-cols-3' : '') + ' w-full gap-x-4' - } - > - {items.map((item, index) => ( - <button - key={index} - className={'flex w-full cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-list-hover'} - onClick={(e) => handleClick(e, item)} - > - <> - {typeof item.icon === 'function' ? item.icon() : item.icon} - <span className={'flex-shrink-0'}>{item.title}</span> - </> - </button> - ))} - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupWindow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupWindow.tsx deleted file mode 100644 index 0075b61ea9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupWindow.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react'; -import useOutsideClick from '$app/components/_shared/useOutsideClick'; - -export const PopupWindow = ({ - children, - className, - onOutsideClick, - left, - top, - style, -}: { - children: ReactNode; - className?: string; - onOutsideClick: () => void; - left: number; - top: number; - style?: CSSProperties; -}) => { - const ref = useRef<HTMLDivElement>(null); - - useOutsideClick(ref, onOutsideClick); - - const [adjustedTop, setAdjustedTop] = useState(-100); - const [adjustedLeft, setAdjustedLeft] = useState(-100); - const [stickToBottom, setStickToBottom] = useState(false); - const [stickToRight, setStickToRight] = useState(false); - - useEffect(() => { - if (!ref.current) return; - - new ResizeObserver(() => { - if (!ref.current) return; - const { height, width } = ref.current.getBoundingClientRect(); - - setAdjustedTop(top); - if (top + height > window.innerHeight) { - setStickToBottom(true); - } else { - setStickToBottom(false); - } - - setAdjustedLeft(left); - if (left + width > window.innerWidth) { - setStickToRight(true); - } else { - setStickToRight(false); - } - }).observe(ref.current); - }, [ref, left, top]); - - return ( - <div - ref={ref} - className={ - 'fixed z-10 rounded-lg bg-bg-body shadow-md transition-opacity duration-300 ' + - (adjustedTop === -100 && adjustedLeft === -100 ? 'opacity-0 ' : 'opacity-100 ') + - (className ?? '') - } - style={{ - [stickToBottom ? 'bottom' : 'top']: `${stickToBottom ? '0' : adjustedTop}px`, - [stickToRight ? 'right' : 'left']: `${stickToRight ? '0' : adjustedLeft}px`, - ...style, - }} - > - {children} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PromptWindow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PromptWindow.tsx deleted file mode 100644 index 97c095eafb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/PromptWindow.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Button } from '$app/components/_shared/Button'; - -export const PromptWindow = ({ msg, onYes, onCancel }: { msg: string; onYes: () => void; onCancel: () => void }) => { - return ( - <div - className='fixed inset-0 z-20 flex items-center justify-center bg-black/30 backdrop-blur-sm' - onClick={() => onCancel()} - > - <div className={'rounded-xl bg-white p-16'} onClick={(e) => e.stopPropagation()}> - <div className={'flex flex-col items-center justify-center gap-8'}> - <div className={'text-text-title'}>{msg}</div> - <div className={'flex items-center justify-around gap-4'}> - <Button onClick={() => onCancel()} size={'medium-transparent'}> - Cancel - </Button> - <Button onClick={() => onYes()} size={'medium'}> - Yes - </Button> - </div> - </div> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/SearchInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/SearchInput.tsx deleted file mode 100644 index 15d020df6e..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/SearchInput.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { SearchSvg } from './svg/SearchSvg'; -import { useState } from 'react'; - -export const SearchInput = () => { - const [active, setActive] = useState(false); - - return ( - <div className={`flex items-center rounded-lg border p-2 ${active ? 'border-fill-default' : 'border-line-divider'}`}> - <i className='mr-2 h-5 w-5'> - <SearchSvg /> - </i> - <input - onFocus={() => setActive(true)} - onBlur={() => setActive(false)} - className='w-52 text-sm text-text-placeholder focus:text-text-title' - placeholder='Search' - type='search' - /> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Switch.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Switch.tsx deleted file mode 100644 index 8edcdf39fd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Switch.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export const Switch = ({ value, setValue }: { value: boolean; setValue: (v: boolean) => void }) => { - return ( - <label className='form-switch' style={{ transform: 'scale(0.5)', marginRight: '-16px' }}> - <input type='checkbox' checked={value} onChange={() => setValue(!value)} /> - <i></i> - </label> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-hooks/useUserSettingControllerContext.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-hooks/useUserSettingControllerContext.ts deleted file mode 100644 index d2a459e06a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-hooks/useUserSettingControllerContext.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { UserSettingController } from '$app/stores/effects/user/user_setting_controller'; -import { createContext, useContext } from 'react'; - -export const UserSettingControllerContext = createContext<UserSettingController | undefined>(undefined); -export function useUserSettingControllerContext() { - const context = useContext(UserSettingControllerContext); - - return context; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ButtonPopoverList/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/button_menu/ButtonMenu.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/ButtonPopoverList/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/button_menu/ButtonMenu.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/constants.ts deleted file mode 100644 index 48e51e7f9a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const INITIAL_FOLDER_HEIGHT = 40; -export const FOLDER_MARGIN = 16; -export const PAGE_ITEM_HEIGHT = 40; -export const ANIMATION_DURATION = 300; -export const NAV_PANEL_MINIMUM_WIDTH = 200; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/loadField.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/loadField.ts deleted file mode 100644 index c01eeada67..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/loadField.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { Some } from 'ts-results'; -import { IDatabaseField, ISelectOption } from '$app_reducers/database/slice'; -import { FieldType, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB } from '@/services/backend'; -import { - makeDateTypeOptionContext, - makeMultiSelectTypeOptionContext, - makeNumberTypeOptionContext, - makeSingleSelectTypeOptionContext, -} from '$app/stores/effects/database/field/type_option/type_option_context'; -import { boardActions } from '$app_reducers/board/slice'; -import { FieldInfo } from '$app/stores/effects/database/field/field_controller'; -import { AppDispatch } from '$app/stores/store'; - -export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?: AppDispatch): Promise<IDatabaseField> { - const field = fieldInfo.field; - const typeOptionController = new TypeOptionController(viewId, Some(fieldInfo)); - - // temporary hack to set grouping field - let groupingFieldSelected = false; - - switch (field.field_type) { - case FieldType.SingleSelect: - case FieldType.MultiSelect: { - let selectOptions: ISelectOption[] = []; - let typeOption: SingleSelectTypeOptionPB | MultiSelectTypeOptionPB | undefined; - - if (field.field_type === FieldType.SingleSelect) { - typeOption = makeSingleSelectTypeOptionContext(typeOptionController).getTypeOption(); - if (!groupingFieldSelected) { - if (dispatch) { - dispatch(boardActions.setGroupingFieldId({ fieldId: field.id })); - } - - groupingFieldSelected = true; - } - } - - if (field.field_type === FieldType.MultiSelect) { - typeOption = makeMultiSelectTypeOptionContext(typeOptionController).getTypeOption(); - } - - if (typeOption) { - selectOptions = typeOption.options.map<ISelectOption>((option) => { - return { - selectOptionId: option.id, - title: option.name, - color: option.color, - }; - }); - } - - return { - fieldId: field.id, - title: field.name, - fieldType: field.field_type, - visible: field.visibility, - width: field.width, - fieldOptions: { - selectOptions, - }, - }; - } - - case FieldType.Number: { - const typeOption = makeNumberTypeOptionContext(typeOptionController).getTypeOption(); - - return { - fieldId: field.id, - title: field.name, - visible: field.visibility, - width: field.width, - fieldType: field.field_type, - fieldOptions: { - numberFormat: typeOption.format, - }, - }; - } - - case FieldType.DateTime: { - const typeOption = makeDateTypeOptionContext(typeOptionController).getTypeOption(); - - return { - fieldId: field.id, - title: field.name, - visible: field.visibility, - width: field.width, - fieldType: field.field_type, - fieldOptions: { - dateFormat: typeOption.date_format, - timeFormat: typeOption.time_format, - }, - }; - } - - default: { - return { - fieldId: field.id, - title: field.name, - visible: field.visibility, - width: field.width, - fieldType: field.field_type, - }; - } - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useCell.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useCell.ts deleted file mode 100644 index 91a516ebeb..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useCell.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { CellControllerBuilder } from '$app/stores/effects/database/cell/controller_builder'; -import { DateCellDataPB, SelectOptionCellDataPB, URLCellDataPB } from '@/services/backend'; -import { useEffect, useState } from 'react'; -import { CellController } from '$app/stores/effects/database/cell/cell_controller'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { databaseActions, ISelectOptionType } from '$app_reducers/database/slice'; - -export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => { - const [data, setData] = useState<DateCellDataPB | URLCellDataPB | SelectOptionCellDataPB | string | undefined>(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [cellController, setCellController] = useState<CellController<any, any>>(); - const databaseStore = useAppSelector((state) => state.database); - const dispatch = useAppDispatch(); - - useEffect(() => { - if (!cellIdentifier || !cellCache || !fieldController) return; - const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController); - const c = builder.build(); - - setCellController(c); - - c.subscribeChanged({ - onCellChanged: (cellData) => { - if (cellData.some) { - const value = cellData.val; - - setData(value); - - // update redux store for database field if there are new select options - if ( - value instanceof SelectOptionCellDataPB && - databaseStore.fields[cellIdentifier.fieldId] && - (databaseStore.fields[cellIdentifier.fieldId].fieldOptions as ISelectOptionType).selectOptions.length !== - value.options.length - ) { - const field = { ...databaseStore.fields[cellIdentifier.fieldId] }; - const selectOptions = value.options.map((option) => ({ - selectOptionId: option.id, - title: option.name, - color: option.color, - })); - - dispatch( - databaseActions.updateField({ - field: { - ...field, - fieldOptions: { - ...field.fieldOptions, - selectOptions: selectOptions, - }, - }, - }) - ); - } - } - }, - }); - - void (async () => { - try { - const cellData = await c.getCellData(); - - if (cellData.some) { - setData(cellData.unwrap()); - } - } catch (e) { - // mute for now - } - })(); - - return () => { - void c.dispose(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [cellIdentifier, cellCache, fieldController]); - - return { - cellController, - data, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useDatabase.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useDatabase.ts deleted file mode 100644 index 70a10d19c7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useDatabase.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { - databaseActions, - DatabaseFieldMap, - IDatabaseColumn, - IDatabaseFilter, - TDatabaseOperators, -} from '$app/stores/reducers/database/slice'; -import { useAppDispatch } from '$app/stores/store'; -import loadField from './loadField'; -import { FieldInfo } from '$app/stores/effects/database/field/field_controller'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { - FieldType, - SelectOptionConditionPB, - SelectOptionFilterPB, - TextFilterConditionPB, - TextFilterPB, - ViewLayoutPB, -} from '@/services/backend'; -import { DatabaseGroupController } from '$app/stores/effects/database/group/group_controller'; -import { OnDragEndResponder } from 'react-beautiful-dnd'; -import { AsyncQueue } from '$app/utils/async_queue'; - -export const useDatabase = (viewId: string, type?: ViewLayoutPB) => { - const dispatch = useAppDispatch(); - const [controller, setController] = useState<DatabaseController>(); - const [rows, setRows] = useState<readonly RowInfo[]>([]); - const [groups, setGroups] = useState<readonly DatabaseGroupController[]>([]); - const [groupByFieldId, setGroupByFieldId] = useState(''); - - useEffect(() => { - if (!viewId.length) return; - const c = new DatabaseController(viewId); - - setController(c); - - return () => void c.dispose(); - }, [viewId]); - - const loadFields = useCallback( - async (fieldInfos: readonly FieldInfo[]) => { - const fields: DatabaseFieldMap = {}; - const columns: IDatabaseColumn[] = []; - - for (const fieldInfo of fieldInfos) { - const fieldPB = fieldInfo.field; - - columns.push({ - fieldId: fieldPB.id, - sort: 'none', - visible: fieldPB.visibility, - }); - - const field = await loadField(viewId, fieldInfo, dispatch); - - fields[field.fieldId] = field; - } - - dispatch(databaseActions.updateFields({ fields })); - dispatch(databaseActions.updateColumns({ columns })); - }, - [viewId, dispatch] - ); - - const queue = useMemo(() => { - return new AsyncQueue<readonly FieldInfo[]>(loadFields); - }, [loadFields]); - - const transformCondition: (condition: number, fieldType: FieldType) => TDatabaseOperators = (condition, fieldType) => { - switch (fieldType) { - case FieldType.RichText: - switch (condition) { - case TextFilterConditionPB.Contains: - return 'contains'; - case TextFilterConditionPB.DoesNotContain: - return 'doesNotContain'; - case TextFilterConditionPB.EndsWith: - return 'endsWith'; - case TextFilterConditionPB.StartsWith: - return 'startWith'; - case TextFilterConditionPB.Is: - return 'is'; - case TextFilterConditionPB.IsNot: - return 'isNot'; - case TextFilterConditionPB.TextIsEmpty: - return 'isEmpty'; - case TextFilterConditionPB.TextIsNotEmpty: - return 'isNotEmpty'; - default: - return 'is'; - } - - case FieldType.SingleSelect: - case FieldType.MultiSelect: - switch (condition) { - case SelectOptionConditionPB.OptionIs: - return 'is'; - case SelectOptionConditionPB.OptionIsNot: - return 'isNot'; - case SelectOptionConditionPB.OptionIsEmpty: - return 'isEmpty'; - case SelectOptionConditionPB.OptionIsNotEmpty: - return 'isNotEmpty'; - default: - return 'is'; - } - - default: - return 'is'; - } - }; - - useEffect(() => { - void (async () => { - if (!controller) return; - controller.subscribe({ - onRowsChanged: (rowInfos) => { - // TODO: this is a hack to make sure that the row cache is updated - setRows([...rowInfos]); - }, - onFieldsChanged: (fieldInfos) => { - queue.enqueue(fieldInfos); - }, - onFiltersChanged: (filters) => { - const reduxFilters = filters.map<IDatabaseFilter>((filter) => { - switch (filter.field_type) { - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return { - logicalOperator: 'and', - fieldType: filter.field_type, - fieldId: filter.field_id, - id: filter.id, - operator: transformCondition((filter.data as SelectOptionFilterPB).condition, filter.field_type), - value: (filter.data as SelectOptionFilterPB).option_ids, - }; - case FieldType.RichText: - return { - logicalOperator: 'and', - fieldType: filter.field_type, - fieldId: filter.field_id, - id: filter.id, - operator: transformCondition((filter.data as TextFilterPB).condition, filter.field_type), - value: (filter.data as TextFilterPB).content, - }; - - default: - return { - logicalOperator: 'and', - fieldType: filter.field_type, - fieldId: filter.field_id, - id: filter.id, - operator: 'is', - value: '', - }; - } - }); - - dispatch(databaseActions.updateFilters({ filters: reduxFilters })); - }, - onSortChanged: (sorts) => { - dispatch(databaseActions.updateSorts({ sorts: [...sorts] })); - }, - }); - - const openResult = await controller.open(); - - if (openResult.ok) { - setRows( - openResult.val.map((pb) => { - return new RowInfo(viewId, controller.fieldController.fieldInfos, pb); - }) - ); - } - - if (type === ViewLayoutPB.Board) { - const fieldId = await controller.getGroupByFieldId(); - - setGroupByFieldId(fieldId.unwrap()); - setGroups(controller.groups.value); - } - })(); - - return () => { - void controller?.dispose(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [controller, queue]); - - const onNewRowClick = async (index: number) => { - if (!groups) return; - if (!controller?.groups) return; - const group = groups[index]; - - await group.createRow(); - - setGroups([...controller.groups.value]); - }; - - const onDragEnd: OnDragEndResponder = async (result) => { - if (!controller) return; - const { source, destination } = result; - const group = groups.find((g) => g.groupId === source.droppableId); - - if (!group) return; - - if (source.droppableId === destination?.droppableId) { - // move inside the block (group) - await controller.exchangeGroupRow( - group.rows[source.index].id, - destination.droppableId, - group.rows[destination.index].id - ); - } else { - // move to different block (group) - if (!destination?.droppableId) return; - await controller.moveGroupRow(group.rows[source.index].id, destination.droppableId); - } - }; - - return { loadFields, controller, rows, groups, groupByFieldId, onNewRowClick, onDragEnd }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useRow.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useRow.ts deleted file mode 100644 index 2d08a5b399..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useRow.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { RowController } from '$app/stores/effects/database/row/row_controller'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { useEffect, useState } from 'react'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { useAppSelector } from '$app/stores/store'; -import { FieldType } from '@/services/backend'; -import { None } from 'ts-results'; - -export const useRow = (viewId: string, databaseController: DatabaseController, rowInfo: RowInfo) => { - const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]); - const [rowController, setRowController] = useState<RowController>(); - const databaseStore = useAppSelector((state) => state.database); - - useEffect(() => { - if (!databaseController || !rowInfo) return; - const rowCache = databaseController.databaseViewCache.getRowCache(); - const fieldController = databaseController.fieldController; - const c = new RowController(rowInfo, fieldController, rowCache); - - setRowController(c); - - return () => { - // dispose row controller in future - }; - }, [databaseController, rowInfo]); - - useEffect(() => { - if (!rowController) return; - - void (async () => { - const cellsPB = await rowController.loadCells(); - const loadingCells: { fieldId: string; cellIdentifier: CellIdentifier }[] = []; - - for (const [fieldId, cellIdentifier] of cellsPB.entries()) { - loadingCells.push({ - fieldId, - cellIdentifier, - }); - } - - setCells(loadingCells); - })(); - }, [rowController, databaseStore.columns]); - - const onNewColumnClick = async (initialFieldType: FieldType = FieldType.RichText, name?: string) => { - if (!databaseController) return; - const controller = new TypeOptionController(viewId, None, initialFieldType); - - await controller.initialize(); - if (name) { - await controller.setFieldName(name); - } - }; - - return { - cells, - onNewColumnClick, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-dialog/ConfirmDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/delete_confirm_dialog/DeleteConfirmDialog.tsx similarity index 91% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-dialog/ConfirmDialog.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/delete_confirm_dialog/DeleteConfirmDialog.tsx index e506a74990..5723505b4c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/app-dialog/ConfirmDialog.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/delete_confirm_dialog/DeleteConfirmDialog.tsx @@ -12,7 +12,7 @@ interface Props { onClose: () => void; } -function ConfirmDialog({ open, title, subtitle, onOk, onClose }: Props) { +function DeleteConfirmDialog({ open, title, subtitle, onOk, onClose }: Props) { const { t } = useTranslation(); return ( @@ -42,4 +42,4 @@ function ConfirmDialog({ open, title, subtitle, onOk, onClose }: Props) { ); } -export default ConfirmDialog; +export default DeleteConfirmDialog; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag-block/drag.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/drag.hooks.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag-block/drag.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/drag.hooks.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag-block/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag-block/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/drag_block/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPicker.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.hooks.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPicker.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.hooks.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPicker.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPickerCategories.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx similarity index 98% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPickerCategories.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx index 10f2574aaf..fff09cf7aa 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPickerCategories.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerCategories.tsx @@ -5,7 +5,7 @@ import { getRowsWithCategories, PER_ROW_EMOJI_COUNT, useVirtualizedCategories, -} from '$app/components/_shared/EmojiPicker/EmojiPicker.hooks'; +} from '$app/components/_shared/emoji_picker/EmojiPicker.hooks'; import { useTranslation } from 'react-i18next'; import { IconButton } from '@mui/material'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPickerHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx similarity index 99% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPickerHeader.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx index 06f96e7397..d4867fb517 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EmojiPicker/EmojiPickerHeader.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/emoji_picker/EmojiPickerHeader.tsx @@ -6,7 +6,7 @@ import Tooltip from '@mui/material/Tooltip'; import { randomEmoji } from '$app/utils/emoji'; import ShuffleIcon from '@mui/icons-material/Shuffle'; import Popover from '@mui/material/Popover'; -import { useSelectSkinPopoverProps } from '$app/components/_shared/EmojiPicker/EmojiPicker.hooks'; +import { useSelectSkinPopoverProps } from '$app/components/_shared/emoji_picker/EmojiPicker.hooks'; import { useTranslation } from 'react-i18next'; const skinTones = [ diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/getColor.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/getColor.ts deleted file mode 100644 index dd06cd03a3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/getColor.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { SelectOptionColorPB } from '@/services/backend'; - -export const getBgColor = (color: SelectOptionColorPB | undefined): string => { - switch (color) { - case SelectOptionColorPB.Purple: - return 'bg-tint-purple'; - case SelectOptionColorPB.Pink: - return 'bg-tint-pink'; - case SelectOptionColorPB.LightPink: - return 'bg-tint-red'; - case SelectOptionColorPB.Orange: - return 'bg-tint-orange'; - case SelectOptionColorPB.Yellow: - return 'bg-tint-yellow'; - case SelectOptionColorPB.Lime: - return 'bg-tint-lime'; - case SelectOptionColorPB.Green: - return 'bg-tint-green'; - case SelectOptionColorPB.Aqua: - return 'bg-tint-aqua'; - case SelectOptionColorPB.Blue: - return 'bg-tint-blue'; - default: - return ''; - } -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/KatexMath/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/KatexMath.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/KatexMath/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/KatexMath.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/KatexMath/index.css b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/index.css similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/KatexMath/index.css rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/katex_math/index.css diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/useOutsideClick.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/useOutsideClick.ts deleted file mode 100644 index 00a6876d25..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/useOutsideClick.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useEffect } from 'react'; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export default function useOutsideClick(ref: any, handler: (e: MouseEvent | TouchEvent) => void) { - useEffect( - () => { - const listener = (event: MouseEvent | TouchEvent) => { - // Do nothing if clicking ref's element or descendent elements - if (!ref?.current || ref.current.contains(event.target)) { - return; - } - - handler(event); - }; - - document.addEventListener('mousedown', listener); - document.addEventListener('touchstart', listener); - return () => { - document.removeEventListener('mousedown', listener); - document.removeEventListener('touchstart', listener); - }; - }, - // Add ref and handler to effect dependencies - // It's worth noting that because passed in handler is a new ... - // ... function on every render that will cause this effect ... - // ... callback/cleanup to run every render. It's not a big deal ... - // ... but to optimize you can wrap handler in useCallback before ... - // ... passing it into this hook. - [ref, handler] - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/useResizer.ts b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/useResizer.ts deleted file mode 100644 index a3367a9ad7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/useResizer.ts +++ /dev/null @@ -1,40 +0,0 @@ -import React, { useState } from 'react'; - -export const useResizer = (onEnd?: (final: number) => void) => { - const [movementX, setMovementX] = useState(0); - const [movementY, setMovementY] = useState(0); - const [newSizeX, setNewSizeX] = useState(0); - const [newSizeY, setNewSizeY] = useState(0); - - const onMouseDown = (e1: React.MouseEvent<HTMLElement>, initial = 0) => { - const startX = e1.screenX; - const startY = e1.screenY; - - setNewSizeX(initial); - setNewSizeY(initial); - - const onMouseMove = (e2: MouseEvent) => { - setNewSizeX(initial + e2.screenX - startX); - setNewSizeY(initial + e2.screenY - startY); - setMovementX(e2.movementX); - setMovementY(e2.movementY); - }; - - const onMouseUp = (e2: MouseEvent) => { - onEnd?.(initial + e2.screenX - startX); - window.removeEventListener('mousemove', onMouseMove); - window.removeEventListener('mouseup', onMouseUp); - }; - - window.addEventListener('mousemove', onMouseMove); - window.addEventListener('mouseup', onMouseUp); - }; - - return { - movementX, - movementY, - newSizeX, - newSizeY, - onMouseDown, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewBanner.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx similarity index 79% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewBanner.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx index f53c381bfd..99f444ac26 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewBanner.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewBanner.tsx @@ -1,6 +1,6 @@ -import ViewIconGroup from '$app/components/_shared/ViewTitle/ViewIconGroup'; +import ViewIconGroup from '$app/components/_shared/view_title/ViewIconGroup'; import { PageIcon } from '$app_reducers/pages/slice'; -import ViewIcon from '$app/components/_shared/ViewTitle/ViewIcon'; +import ViewIcon from '$app/components/_shared/view_title/ViewIcon'; function ViewBanner({ icon, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx similarity index 94% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewIcon.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx index 6aaa947f86..147bd1cbb0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewIcon.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIcon.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useState } from 'react'; import Popover from '@mui/material/Popover'; -import EmojiPicker from '$app/components/_shared/EmojiPicker'; +import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; import { PageIcon } from '$app_reducers/pages/slice'; function ViewIcon({ icon, onUpdateIcon }: { icon?: PageIcon; onUpdateIcon: (icon: string) => void }) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewIconGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIconGroup.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewIconGroup.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewIconGroup.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx similarity index 89% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx index 3df18b1a87..26f83ac921 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitle.tsx @@ -1,8 +1,8 @@ import React, { useCallback, useEffect, useState } from 'react'; -import ViewBanner from '$app/components/_shared/ViewTitle/ViewBanner'; +import ViewBanner from '$app/components/_shared/view_title/ViewBanner'; import { Page, PageIcon } from '$app_reducers/pages/slice'; import { ViewIconTypePB } from '@/services/backend'; -import ViewTitleInput from '$app/components/_shared/ViewTitle/ViewTitleInput'; +import ViewTitleInput from '$app/components/_shared/view_title/ViewTitleInput'; interface Props { view: Page; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewTitleInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitleInput.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/_shared/ViewTitle/ViewTitleInput.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/_shared/view_title/ViewTitleInput.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ConfirmAccount/ConfirmAccount.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ConfirmAccount/ConfirmAccount.hooks.ts deleted file mode 100644 index 2d92317fcc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ConfirmAccount/ConfirmAccount.hooks.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useState } from 'react'; -import { currentUserActions } from '../../../stores/reducers/current-user/slice'; -import { useAppDispatch, useAppSelector } from '../../../stores/store'; -import { useNavigate } from 'react-router-dom'; - -export const useConfirmAccount = () => { - const [otpValues, setOtpValues] = useState(''); - const appDispatch = useAppDispatch(); - const currentUser = useAppSelector((state) => state.currentUser); - const navigate = useNavigate(); - - const handleChange = (value: string) => { - console.log({ value }); - setOtpValues(value); - }; - - const onConfirmClick = () => { - appDispatch( - currentUserActions.updateUser({ - ...currentUser, - isAuthenticated: true, - }) - ); - navigate('/'); - }; - - return { - otpValues, - handleChange, - onConfirmClick, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ConfirmAccount/ConfirmAccount.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ConfirmAccount/ConfirmAccount.tsx deleted file mode 100644 index cca91cc10d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ConfirmAccount/ConfirmAccount.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import OtpInput from 'react18-input-otp'; -import { AppflowyLogo } from '../../_shared/svg/AppflowyLogo'; -import { useConfirmAccount } from './ConfirmAccount.hooks'; -import { Button } from '../../_shared/Button'; - -export const ConfirmAccount = () => { - const { handleChange, otpValues, onConfirmClick } = useConfirmAccount(); - - return ( - <div className='flex h-screen w-full flex-col items-center justify-center gap-12 text-center'> - <div className='flex h-10 w-10 justify-center'> - <AppflowyLogo /> - </div> - - <div className='flex flex-col gap-2'> - <span className='text-2xl font-semibold '>Enter the code sent to your phone</span> - <div> - <span className='block text-gray-500'>Confirm that this phone belongs to you.</span> - <span className='block text-gray-500'> - Code sent to <span className='text-text-title'>+86 10 6764 5489</span> - </span> - </div> - </div> - - <div className='flex h-24 flex-col gap-4 '> - <div className={'flex-1'}> - <OtpInput - value={otpValues} - onChange={handleChange} - numInputs={5} - isInputNum={true} - separator={<span> </span>} - inputStyle='border border-gray-300 rounded-lg h-full !w-14 font-semibold focus:ring-2 focus:ring-fill-hover focus:ring-opacity-50' - containerStyle='h-full w-full flex justify-around gap-2 ' - /> - </div> - - <a href='#' className='hover:text-content-hover text-xs text-fill-hover'> - <span>Send code again</span> - </a> - </div> - - <Button size={'primary'} onClick={() => onConfirmClick()}> - Get Started - </Button> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx deleted file mode 100644 index a98681f46d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { AppflowyLogo } from '../../_shared/svg/AppflowyLogo'; -import { EyeClosedSvg } from '../../_shared/svg/EyeClosedSvg'; -import { EyeOpenSvg } from '../../_shared/svg/EyeOpenSvg'; -import { useLogin } from './Login.hooks'; -import { Link } from 'react-router-dom'; -import { Button } from '../../_shared/Button'; -import { useTranslation } from 'react-i18next'; -import { EarthSvg } from '../../_shared/svg/EarthSvg'; -import { useState } from 'react'; -import { LanguageSelectPopup } from '../../_shared/LanguageSelectPopup'; - -export const Login = () => { - const { showPassword, onTogglePassword, onSignInClick, email, setEmail, password, setPassword, authError } = - useLogin(); - const { t } = useTranslation(); - const [showLanguagePopup, setShowLanguagePopup] = useState(false); - - return ( - <> - <form onSubmit={(e) => e.preventDefault()} method='POST'> - <div className='relative flex h-screen w-screen flex-col items-center justify-center gap-12 bg-bg-body text-center text-text-title'> - <div className='flex h-10 w-10 justify-center'> - <AppflowyLogo /> - </div> - - <div> - <span className='text-2xl font-semibold leading-9'> - {t('signIn.loginTitle').replace('@:appName', 'AppFlowy')} - </span> - </div> - - <div className='flex w-full max-w-[340px] flex-col gap-6 '> - <input - type='text' - className={`input w-full ${authError && 'error'}`} - placeholder={t('signIn.emailHint') ?? ''} - value={email} - onChange={(e) => setEmail(e.target.value)} - /> - <div className='relative w-full'> - {/* Password input field */} - - <input - type={showPassword ? 'text' : 'password'} - className={`input w-full !pr-10 ${authError && 'error'}`} - placeholder={t('signIn.passwordHint') ?? ''} - value={password} - onChange={(e) => setPassword(e.target.value)} - /> - - {/* Show password button */} - <button - type='button' - className='absolute right-0 top-0 flex h-full w-12 items-center justify-center ' - onClick={onTogglePassword} - > - <span className='h-6 w-6'>{showPassword ? <EyeClosedSvg /> : <EyeOpenSvg />}</span> - </button> - </div> - - <div className='flex justify-center'> - {/* Forget password link */} - <Link to={'/auth/confirm-account'}> - <span className='text-xs text-fill-default hover:text-fill-hover'>{t('signIn.forgotPassword')}</span> - </Link> - </div> - </div> - - <div className='flex w-full max-w-[340px] flex-col gap-6 '> - <Button size={'primary'} onClick={() => onSignInClick()}> - {t('signIn.loginButtonText')} - </Button> - - {/* signup link */} - <div className='flex justify-center'> - <span className='text-xs text-gray-400'> - {t('signIn.dontHaveAnAccount')} - <Link to={'/auth/signUp'}> - <span className='ml-2 text-fill-default hover:text-fill-hover'>{t('signUp.buttonText')}</span> - </Link> - </span> - </div> - </div> - - <div className={'absolute right-0 top-0 px-12 py-8'}> - <div className={'relative h-full w-full'}> - <button - className={'h-8 w-8 text-text-caption hover:text-text-title'} - onClick={() => setShowLanguagePopup(true)} - > - <EarthSvg></EarthSvg> - </button> - {showLanguagePopup && ( - <LanguageSelectPopup onClose={() => setShowLanguagePopup(false)}></LanguageSelectPopup> - )} - </div> - </div> - </div> - </form> - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx index 3f55079677..341eff871e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx @@ -1,28 +1,22 @@ import { Outlet } from 'react-router-dom'; import { useAuth } from './auth.hooks'; import Layout from '$app/components/layout/Layout'; -import { useEffect, useState } from 'react'; -import { GetStarted } from './GetStarted/GetStarted'; +import { useCallback, useEffect, useState } from 'react'; +import { GetStarted } from '$app/components/auth/get_started/GetStarted'; import { AppflowyLogo } from '../_shared/svg/AppflowyLogo'; export const ProtectedRoutes = () => { const { currentUser, checkUser } = useAuth(); const [isLoading, setIsLoading] = useState(true); - useEffect(() => { - void checkUser().then(async (result) => { - await new Promise(() => - setTimeout(() => { - setIsLoading(false); - }, 1200) - ); + const checkUserStatus = useCallback(async () => { + await checkUser(); + setIsLoading(false); + }, [checkUser]); - if (result.err) { - throw new Error(result.val.msg); - } - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + useEffect(() => { + void checkUserStatus(); + }, [checkUserStatus]); if (isLoading) { // It's better to make a fading effect to disappear the loading page @@ -50,6 +44,6 @@ const SplashScreen = ({ isAuthenticated }: { isAuthenticated: boolean }) => { </Layout> ); } else { - return <GetStarted></GetStarted>; + return <GetStarted />; } }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.hooks.ts deleted file mode 100644 index c97b73117b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.hooks.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { useState } from 'react'; -import { useAppDispatch } from '$app/stores/store'; -import { currentUserActions } from '$app_reducers/current-user/slice'; -import { useNavigate } from 'react-router-dom'; -import { useAuth } from '../auth.hooks'; - -export const useSignUp = () => { - const [email, _setEmail] = useState(''); - const [displayName, _setDisplayName] = useState(''); - const [password, _setPassword] = useState(''); - const [repeatedPassword, _setRepeatedPassword] = useState(''); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmPassword, setShowConfirmPassword] = useState(false); - const appDispatch = useAppDispatch(); - const navigate = useNavigate(); - const { register } = useAuth(); - const [authError, setAuthError] = useState(false); - - const setEmail = (v: string) => { - setAuthError(false); - _setEmail(v); - }; - - const setDisplayName = (v: string) => { - setAuthError(false); - _setDisplayName(v); - }; - - const setPassword = (v: string) => { - setAuthError(false); - _setPassword(v); - }; - - const setRepeatedPassword = (v: string) => { - setAuthError(false); - _setRepeatedPassword(v); - }; - - function onTogglePassword() { - setShowPassword(!showPassword); - } - - function onToggleConfirmPassword() { - setShowConfirmPassword(!showConfirmPassword); - } - - async function onSignUpClick() { - try { - const result = await register(email, password, displayName); - const { id, token } = result; - - appDispatch( - currentUserActions.updateUser({ - id, - token, - email, - displayName, - isAuthenticated: true, - }) - ); - navigate('/'); - } catch (e) { - setAuthError(true); - } - } - - return { - email, - setEmail, - displayName, - setDisplayName, - password, - setPassword, - repeatedPassword, - setRepeatedPassword, - showPassword, - onTogglePassword, - showConfirmPassword, - onToggleConfirmPassword, - onSignUpClick, - authError, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx deleted file mode 100644 index 135536ff05..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/SignUp/SignUp.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { AppflowyLogo } from '../../_shared/svg/AppflowyLogo'; -import { EyeClosedSvg } from '../../_shared/svg/EyeClosedSvg'; -import { EyeOpenSvg } from '../../_shared/svg/EyeOpenSvg'; - -import { useSignUp } from './SignUp.hooks'; -import { Link } from 'react-router-dom'; -import { Button } from '../../_shared/Button'; -import { EarthSvg } from '../../_shared/svg/EarthSvg'; -import { LanguageSelectPopup } from '../../_shared/LanguageSelectPopup'; -import { useTranslation } from 'react-i18next'; -import { useState } from 'react'; - -export const SignUp = () => { - const { - showPassword, - onTogglePassword, - showConfirmPassword, - onToggleConfirmPassword, - onSignUpClick, - email, - setEmail, - displayName, - setDisplayName, - password, - setPassword, - repeatedPassword, - setRepeatedPassword, - authError, - } = useSignUp(); - const { t } = useTranslation(); - const [showLanguagePopup, setShowLanguagePopup] = useState(false); - - return ( - <form method='POST' onSubmit={(e) => e.preventDefault()}> - <div className='relative flex h-screen w-full flex-col items-center justify-center gap-12 text-center'> - <div className='flex h-10 w-10 justify-center'> - <AppflowyLogo /> - </div> - - <div> - <span className='text-2xl font-semibold'>{t('signUp.title').replace('@:appName', 'AppFlowy')}</span> - </div> - - <div className='flex w-full max-w-[340px] flex-col gap-6'> - <input - type='text' - className={`input w-full ${authError && 'error'}`} - placeholder={t('signUp.emailHint') ?? ''} - value={email} - onChange={(e) => setEmail(e.target.value)} - /> - {/* new user should enter his name, need translation for this field */} - <input - type='text' - className={`input w-full ${authError && 'error'}`} - placeholder='Name' - value={displayName} - onChange={(e) => setDisplayName(e.target.value)} - /> - <div className='relative w-full'> - <input - type={showPassword ? 'text' : 'password'} - className={`input w-full !pr-10 ${authError && 'error'}`} - placeholder={t('signUp.passwordHint') ?? ''} - value={password} - onChange={(e) => setPassword(e.target.value)} - /> - - <button - className='absolute right-0 top-0 flex h-full w-12 items-center justify-center ' - onClick={onTogglePassword} - type='button' - > - <span className='h-6 w-6'>{showPassword ? <EyeClosedSvg /> : <EyeOpenSvg />}</span> - </button> - </div> - - <div className='relative w-full'> - <input - type={showConfirmPassword ? 'text' : 'password'} - className={`input w-full !pr-10 ${authError && 'error'}`} - placeholder={t('signUp.repeatPasswordHint') ?? ''} - value={repeatedPassword} - onChange={(e) => setRepeatedPassword(e.target.value)} - /> - - <button - className='absolute right-0 top-0 flex h-full w-12 items-center justify-center ' - onClick={onToggleConfirmPassword} - type='button' - > - <span className='h-6 w-6'>{showConfirmPassword ? <EyeClosedSvg /> : <EyeOpenSvg />}</span> - </button> - </div> - </div> - - <div className='flex w-full max-w-[340px] flex-col gap-6 '> - <Button size={'primary'} onClick={() => onSignUpClick()}> - {t('signUp.getStartedText')} - </Button> - - {/* signup link */} - <div className='flex justify-center'> - <span className='text-xs text-gray-500'> - {t('signUp.alreadyHaveAnAccount')} - <Link to={'/auth/login'}> - <span className='hover:text-content-hover ml-2 text-fill-hover'>{t('signIn.buttonText')}</span> - </Link> - </span> - </div> - </div> - - <div className={'absolute right-0 top-0 px-12 py-8'}> - <div className={'relative h-full w-full'}> - <button - className={'h-8 w-8 text-text-caption hover:text-text-title'} - onClick={() => setShowLanguagePopup(true)} - > - <EarthSvg></EarthSvg> - </button> - {showLanguagePopup && ( - <LanguageSelectPopup onClose={() => setShowLanguagePopup(false)}></LanguageSelectPopup> - )} - </div> - </div> - </div> - </form> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts index e3afe55c2a..b9342fb210 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts @@ -1,57 +1,44 @@ -import { currentUserActions } from '../../stores/reducers/current-user/slice'; -import { useAppDispatch, useAppSelector } from '../../stores/store'; -import { UserProfilePB } from '../../../services/backend/events/flowy-user'; -import { AuthBackendService, UserBackendService } from '../../stores/effects/user/user_bd_svc'; -import { WorkspaceSettingPB } from '../../../services/backend/models/flowy-folder/workspace'; -import { Log } from '../../utils/log'; -import { FolderEventGetCurrentWorkspaceSetting } from '@/services/backend/events/flowy-folder'; +import { currentUserActions } from '$app_reducers/current-user/slice'; +import { UserProfilePB } from '@/services/backend/events/flowy-user'; +import { UserService } from '$app/application/user/user.service'; +import { AuthService } from '$app/application/user/auth.service'; +import { useAppSelector, useAppDispatch } from '$app/stores/store'; +import { getCurrentWorkspaceSetting } from '$app/application/folder/workspace.service'; +import { useCallback } from 'react'; export const useAuth = () => { const dispatch = useAppDispatch(); const currentUser = useAppSelector((state) => state.currentUser); - const authBackendService = new AuthBackendService(); - async function checkUser() { - const result = await UserBackendService.getUserProfile(); + const checkUser = useCallback(async () => { + const userProfile = await UserService.getUserProfile(); - if (result.ok) { - const userProfile = result.val; + if (!userProfile) return; + const workspaceSetting = await getCurrentWorkspaceSetting(); - const workspaceSetting = await _openWorkspace().then((r) => { - if (r.ok) { - return r.val; - } else { - return undefined; - } - }); + dispatch( + currentUserActions.checkUser({ + id: userProfile.id, + token: userProfile.token, + email: userProfile.email, + displayName: userProfile.name, + isAuthenticated: true, + workspaceSetting: workspaceSetting, + }) + ); - dispatch( - currentUserActions.checkUser({ - id: userProfile.id, - token: userProfile.token, - email: userProfile.email, - displayName: userProfile.name, - isAuthenticated: true, - workspaceSetting: workspaceSetting, - }) - ); - } + return userProfile; + }, [dispatch]); - return result; - } + const register = useCallback( + async (email: string, password: string, name: string): Promise<UserProfilePB> => { + const userProfile = await AuthService.signUp({ email, password, name }); - async function register(email: string, password: string, name: string): Promise<UserProfilePB> { - const authResult = await authBackendService.signUp({ email, password, name }); - - if (authResult.ok) { - const userProfile = authResult.val; // Get the workspace setting after user registered. The workspace setting // contains the latest visiting page and the current workspace data. - const openWorkspaceResult = await _openWorkspace(); - - if (openWorkspaceResult.ok) { - const workspaceSetting: WorkspaceSettingPB = openWorkspaceResult.val; + const workspaceSetting = await getCurrentWorkspaceSetting(); + if (workspaceSetting) { dispatch( currentUserActions.updateUser({ id: userProfile.id, @@ -64,18 +51,15 @@ export const useAuth = () => { ); } - return authResult.val; - } else { - Log.error(authResult.val.msg); - throw new Error(authResult.val.msg); - } - } + return userProfile; + }, + [dispatch] + ); - async function login(email: string, password: string): Promise<UserProfilePB> { - const result = await authBackendService.signIn({ email, password }); - - if (result.ok) { - const { id, token, name } = result.val; + const login = useCallback( + async (email: string, password: string): Promise<UserProfilePB> => { + const user = await AuthService.signIn({ email, password }); + const { id, token, name } = user; dispatch( currentUserActions.updateUser({ @@ -86,21 +70,15 @@ export const useAuth = () => { isAuthenticated: true, }) ); - return result.val; - } else { - Log.error(result.val.msg); - throw new Error(result.val.msg); - } - } + return user; + }, + [dispatch] + ); - async function logout() { - await authBackendService.signOut(); + const logout = useCallback(async () => { + await AuthService.signOut(); dispatch(currentUserActions.logout()); - } - - async function _openWorkspace() { - return FolderEventGetCurrentWorkspaceSetting(); - } + }, [dispatch]); return { currentUser, checkUser, register, login, logout }; }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/GetStarted/GetStarted.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/GetStarted.tsx similarity index 93% rename from frontend/appflowy_tauri/src/appflowy_app/components/auth/GetStarted/GetStarted.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/GetStarted.tsx index f90e3fa188..de387a1c05 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/auth/GetStarted/GetStarted.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/GetStarted.tsx @@ -1,8 +1,7 @@ import { t } from 'i18next'; import { AppflowyLogo } from '../../_shared/svg/AppflowyLogo'; - -import { useLogin } from '../Login/Login.hooks'; import Button from '@mui/material/Button'; +import { useLogin } from '$app/components/auth/get_started/useLogin'; export const GetStarted = () => { const { onAutoSignInClick } = useLogin(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/useLogin.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/auth/Login/Login.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/auth/get_started/useLogin.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/Board.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/Board.tsx deleted file mode 100644 index a79a2c0a36..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/Board.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { SearchInput } from '../_shared/SearchInput'; -import { BoardGroup } from './BoardGroup'; -import { useDatabase } from '../_shared/database-hooks/useDatabase'; -import { ViewLayoutPB } from '@/services/backend'; -import { DragDropContext } from 'react-beautiful-dnd'; -import { useState } from 'react'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { EditRow } from '$app/components/_shared/EditRow/EditRow'; -import { BoardToolbar } from '$app/components/board/BoardToolbar'; - -export const Board = ({ viewId, title }: { viewId: string; title: string }) => { - const { controller, groups, groupByFieldId, onNewRowClick, onDragEnd } = useDatabase(viewId, ViewLayoutPB.Board); - const [showBoardRow, setShowBoardRow] = useState(false); - const [boardRowInfo, setBoardRowInfo] = useState<RowInfo>(); - - const onOpenRow = (rowInfo: RowInfo) => { - setBoardRowInfo(rowInfo); - setShowBoardRow(true); - }; - - return ( - <> - <div className='flex w-full items-center justify-between'> - <BoardToolbar title={title} /> - - <div className='flex shrink-0 items-center gap-4'> - <SearchInput /> - </div> - </div> - <DragDropContext onDragEnd={onDragEnd}> - <div className={'relative w-full flex-1 overflow-auto'}> - <div className={'absolute flex h-full flex-shrink-0 items-start justify-start gap-4'}> - {controller && - groups && - groups.map((group, index) => ( - <BoardGroup - key={group.groupId} - viewId={viewId} - controller={controller} - group={group} - groupByFieldId={groupByFieldId} - onNewRowClick={() => onNewRowClick(index)} - onOpenRow={onOpenRow} - /> - ))} - </div> - </div> - </DragDropContext> - {controller && showBoardRow && boardRowInfo && ( - <EditRow - onClose={() => setShowBoardRow(false)} - viewId={viewId} - controller={controller} - rowInfo={boardRowInfo} - ></EditRow> - )} - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx deleted file mode 100644 index 84e198ee8d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { Details2Svg } from '../_shared/svg/Details2Svg'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { useRow } from '../_shared/database-hooks/useRow'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { BoardCell } from './BoardCell'; -import { Draggable } from 'react-beautiful-dnd'; -import { MouseEventHandler, useState } from 'react'; -import { PopupWindow } from '$app/components/_shared/PopupWindow'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { useTranslation } from 'react-i18next'; -import { useAppSelector } from '$app/stores/store'; - -export const BoardCard = ({ - index, - viewId, - controller, - rowInfo, - groupByFieldId, - onOpenRow, -}: { - index: number; - viewId: string; - controller: DatabaseController; - rowInfo: RowInfo; - groupByFieldId: string; - onOpenRow: (rowId: RowInfo) => void; -}) => { - const databaseStore = useAppSelector((state) => state.database); - const { t } = useTranslation(); - - const { cells } = useRow(viewId, controller, rowInfo); - - const [showCardPopup, setShowCardPopup] = useState(false); - const [cardPopupLeft, setCardPopupLeft] = useState(0); - const [cardPopupTop, setCardPopupTop] = useState(0); - - const onDetailClick: MouseEventHandler = (e) => { - e.stopPropagation(); - let target = e.target as HTMLElement; - - while (!(target instanceof HTMLButtonElement)) { - if (target.parentElement === null) return; - target = target.parentElement; - } - - const { right: left, top } = target.getBoundingClientRect(); - - setCardPopupLeft(left); - setCardPopupTop(top); - setShowCardPopup(true); - }; - - const onDeleteRowClick = async () => { - setShowCardPopup(false); - await controller.deleteRow(rowInfo.row.id); - }; - - return ( - <> - <Draggable draggableId={rowInfo.row.id} key={rowInfo.row.id} index={index}> - {(provided) => ( - <div - ref={provided.innerRef} - {...provided.draggableProps} - {...provided.dragHandleProps} - onClick={() => onOpenRow(rowInfo)} - className={`relative cursor-pointer select-none rounded-lg bg-bg-body px-3 py-2 transition-transform duration-100 hover:bg-content-blue-50 `} - > - <button - onClick={onDetailClick} - className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-fill-list-hover'} - > - <Details2Svg></Details2Svg> - </button> - <div className={'flex flex-col gap-3'}> - {cells - .filter( - (cell) => cell.fieldId !== groupByFieldId && databaseStore.fields[cell.cellIdentifier.fieldId]?.visible - ) - .map((cell, cellIndex) => ( - <BoardCell - key={cellIndex} - cellIdentifier={cell.cellIdentifier} - cellCache={controller.databaseViewCache.getRowCache().getCellCache()} - fieldController={controller.fieldController} - ></BoardCell> - ))} - </div> - </div> - )} - </Draggable> - {showCardPopup && ( - <PopupWindow - className={'p-2 text-xs'} - onOutsideClick={() => setShowCardPopup(false)} - left={cardPopupLeft} - top={cardPopupTop} - > - <button - key={index} - className={'flex w-full cursor-pointer items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-list-hover'} - onClick={() => onDeleteRowClick()} - > - <i className={'h-5 w-5'}> - <TrashSvg></TrashSvg> - </i> - <span className={'flex-shrink-0'}>{t('grid.row.delete')}</span> - </button> - </PopupWindow> - )} - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCell.tsx deleted file mode 100644 index 4075c98c66..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCell.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { FieldType } from '@/services/backend'; -import { BoardOptionsCell } from './BoardOptionsCell'; -import { BoardDateCell } from './BoardDateCell'; -import { BoardTextCell } from './BoardTextCell'; -import { BoardUrlCell } from '$app/components/board/BoardUrlCell'; -import { BoardCheckboxCell } from '$app/components/board/BoardCheckboxCell'; -import { BoardCheckListCell } from '$app/components/board/BoardCheckListCell'; - -export const BoardCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - return ( - <> - {cellIdentifier.fieldType === FieldType.SingleSelect || cellIdentifier.fieldType === FieldType.MultiSelect ? ( - <BoardOptionsCell - cellIdentifier={cellIdentifier} - cellCache={cellCache} - fieldController={fieldController} - ></BoardOptionsCell> - ) : cellIdentifier.fieldType === FieldType.Checklist ? ( - <BoardCheckListCell - cellIdentifier={cellIdentifier} - cellCache={cellCache} - fieldController={fieldController} - ></BoardCheckListCell> - ) : cellIdentifier.fieldType === FieldType.DateTime ? ( - <BoardDateCell - cellIdentifier={cellIdentifier} - cellCache={cellCache} - fieldController={fieldController} - ></BoardDateCell> - ) : cellIdentifier.fieldType === FieldType.URL ? ( - <BoardUrlCell - cellIdentifier={cellIdentifier} - cellCache={cellCache} - fieldController={fieldController} - ></BoardUrlCell> - ) : cellIdentifier.fieldType === FieldType.Checkbox ? ( - <BoardCheckboxCell - cellIdentifier={cellIdentifier} - cellCache={cellCache} - fieldController={fieldController} - ></BoardCheckboxCell> - ) : ( - <BoardTextCell - cellIdentifier={cellIdentifier} - cellCache={cellCache} - fieldController={fieldController} - ></BoardTextCell> - )} - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCheckListCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCheckListCell.tsx deleted file mode 100644 index e3c1894f9c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCheckListCell.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; -import { useEffect, useState } from 'react'; -import { ISelectOptionType } from '$app_reducers/database/slice'; -import { SelectOptionCellDataPB } from '@/services/backend'; -import { useAppSelector } from '$app/stores/store'; -import { CheckListProgress } from '$app/components/_shared/CheckListProgress'; - -export const BoardCheckListCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - const databaseStore = useAppSelector((state) => state.database); - const [allOptionsCount, setAllOptionsCount] = useState(0); - const [selectedOptionsCount, setSelectedOptionsCount] = useState(0); - - useEffect(() => { - setAllOptionsCount( - (databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType)?.selectOptions?.length ?? 0 - ); - }, [databaseStore, cellIdentifier]); - - useEffect(() => { - setSelectedOptionsCount((data as SelectOptionCellDataPB)?.select_options?.length ?? 0); - }, [data]); - - return <CheckListProgress completed={selectedOptionsCount} max={allOptionsCount} />; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCheckboxCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCheckboxCell.tsx deleted file mode 100644 index 3d591c880d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCheckboxCell.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; -import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; - -export const BoardCheckboxCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - return ( - <i className={'h-5 w-5'}> - {data === 'Yes' ? <EditorCheckSvg></EditorCheckSvg> : <EditorUncheckSvg></EditorUncheckSvg>} - </i> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardDateCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardDateCell.tsx deleted file mode 100644 index d924c9a54d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardDateCell.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { DateCellDataPB } from '@/services/backend'; -import { useCell } from '../_shared/database-hooks/useCell'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; - -export const BoardDateCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - return <div>{(data as DateCellDataPB | undefined)?.date ?? ''} </div>; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardFieldsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardFieldsPopup.tsx deleted file mode 100644 index 6b7d09b34b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardFieldsPopup.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useAppSelector } from '$app/stores/store'; -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { useRef } from 'react'; -import useOutsideClick from '$app/components/_shared/useOutsideClick'; -import { EyeOpenSvg } from '$app/components/_shared/svg/EyeOpenSvg'; - -export const BoardFieldsPopup = ({ hidePopup }: { hidePopup: () => void }) => { - const columns = useAppSelector((state) => state.database.columns); - const fields = useAppSelector((state) => state.database.fields); - const ref = useRef<HTMLDivElement>(null); - - useOutsideClick(ref, () => hidePopup()); - - return ( - <div ref={ref} className={'absolute left-full top-full z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md'}> - {columns.map((column, index) => ( - <div - className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-list-hover'} - key={index} - > - <div className={'flex items-center gap-2 '}> - <i className={'flex h-5 w-5 flex-shrink-0 items-center justify-center'}> - <FieldTypeIcon fieldType={fields[column.fieldId].fieldType}></FieldTypeIcon> - </i> - <span className={'flex-shrink-0'}>{fields[column.fieldId].title}</span> - </div> - <div className={'ml-12'}> - <i className={'block h-5 w-5'}> - <EyeOpenSvg></EyeOpenSvg> - </i> - </div> - </div> - ))} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardGroup.tsx deleted file mode 100644 index be2b3722d0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardGroup.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { Details2Svg } from '../_shared/svg/Details2Svg'; -import AddSvg from '../_shared/svg/AddSvg'; -import { BoardCard } from './BoardCard'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { Droppable } from 'react-beautiful-dnd'; -import { DatabaseGroupController } from '$app/stores/effects/database/group/group_controller'; -import { useTranslation } from 'react-i18next'; -import { useEffect, useState } from 'react'; - -export const BoardGroup = ({ - viewId, - controller, - groupByFieldId, - onNewRowClick, - onOpenRow, - group, -}: { - viewId: string; - controller: DatabaseController; - groupByFieldId: string; - onNewRowClick: () => void; - onOpenRow: (rowId: RowInfo) => void; - group: DatabaseGroupController; -}) => { - const { t } = useTranslation(); - - const [rows, setRows] = useState<RowInfo[]>([]); - - useEffect(() => { - const reloadRows = () => { - setRows(group.rows.map((rowPB) => new RowInfo(viewId, controller.fieldController.fieldInfos, rowPB))); - }; - - reloadRows(); - group.subscribe({ - onRemoveRow: reloadRows, - onInsertRow: reloadRows, - onUpdateRow: reloadRows, - onCreateRow: reloadRows, - }); - return () => { - group.unsubscribe(); - }; - }, [controller, group, viewId]); - - return ( - <div className={'flex h-full w-[250px] flex-col rounded-lg bg-bg-base'}> - <div className={'flex items-center justify-between p-4'}> - <div className={'flex items-center gap-2'}> - <span>{group.name}</span> - <span className={'text-text-caption'}>({group.rows.length})</span> - </div> - <div className={'flex items-center gap-2'}> - <button className={'h-5 w-5 rounded hover:bg-fill-list-hover'}> - <Details2Svg></Details2Svg> - </button> - <button className={'h-5 w-5 rounded hover:bg-fill-list-hover'}> - <AddSvg></AddSvg> - </button> - </div> - </div> - <Droppable droppableId={group.groupId}> - {(provided) => ( - <div - className={'flex flex-1 flex-col gap-1 overflow-auto px-2'} - {...provided.droppableProps} - ref={provided.innerRef} - > - {rows.map((row, index) => { - return ( - <BoardCard - viewId={viewId} - controller={controller} - index={index} - key={row.row.id} - rowInfo={row} - groupByFieldId={groupByFieldId} - onOpenRow={onOpenRow} - ></BoardCard> - ); - })} - </div> - )} - </Droppable> - <div className={'p-2'}> - <button - onClick={onNewRowClick} - className={'flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-fill-list-hover'} - > - <span className={'h-5 w-5'}> - <AddSvg></AddSvg> - </span> - <span>{t('board.column.createNewCard')}</span> - </button> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardGroupFieldsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardGroupFieldsPopup.tsx deleted file mode 100644 index b6ac8b2c3d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardGroupFieldsPopup.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useAppSelector } from '$app/stores/store'; -import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; -import { useRef } from 'react'; -import useOutsideClick from '$app/components/_shared/useOutsideClick'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; - -export const BoardGroupFieldsPopup = ({ hidePopup }: { hidePopup: () => void }) => { - const columns = useAppSelector((state) => state.database.columns); - const fields = useAppSelector((state) => state.database.fields); - const ref = useRef<HTMLDivElement>(null); - - useOutsideClick(ref, () => hidePopup()); - - return ( - <div ref={ref} className={'absolute left-full top-full z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md'}> - {columns.map((column, index) => ( - <div - className={'flex cursor-pointer items-center justify-between rounded-lg px-2 py-2 hover:bg-fill-list-hover'} - key={index} - > - <div className={'flex items-center gap-2 '}> - <i className={'flex h-5 w-5 flex-shrink-0 items-center justify-center'}> - <FieldTypeIcon fieldType={fields[column.fieldId].fieldType}></FieldTypeIcon> - </i> - <span className={'flex-shrink-0'}>{fields[column.fieldId].title}</span> - </div> - <div className={'ml-12'}> - <i className={'block h-3 w-3'}> - <CheckmarkSvg></CheckmarkSvg> - </i> - </div> - </div> - ))} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardOptionsCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardOptionsCell.tsx deleted file mode 100644 index 7841e08423..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardOptionsCell.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { SelectOptionCellDataPB } from '@/services/backend'; -import { useCell } from '../_shared/database-hooks/useCell'; -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { getBgColor } from '$app/components/_shared/getColor'; - -export const BoardOptionsCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - return ( - <div className={'flex flex-wrap items-center gap-2 py-2 text-xs text-text-title'}> - {(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => ( - <div className={`${getBgColor(option.color)} rounded px-2 py-0.5`} key={index}> - {option?.name ?? ''} - </div> - ))} - - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardSettingsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardSettingsPopup.tsx deleted file mode 100644 index e1eec1e876..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardSettingsPopup.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useEffect, useState } from 'react'; -import { PropertiesSvg } from '$app/components/_shared/svg/PropertiesSvg'; -import { IPopupItem, PopupSelect } from '$app/components/_shared/PopupSelect'; -import { useTranslation } from 'react-i18next'; -import { GroupByFieldSvg } from '$app/components/_shared/svg/GroupByFieldSvg'; - -export const BoardSettingsPopup = ({ - hidePopup, - onFieldsClick, - onGroupClick, -}: { - hidePopup: () => void; - onFieldsClick: () => void; - onGroupClick: () => void; -}) => { - const [settingsItems, setSettingsItems] = useState<IPopupItem[]>([]); - const { t } = useTranslation(); - - useEffect(() => { - setSettingsItems([ - { - icon: ( - <i className={'h-5 w-5'}> - <PropertiesSvg></PropertiesSvg> - </i> - ), - title: t('grid.settings.properties'), - onClick: onFieldsClick, - }, - { - icon: ( - <i className={'h-5 w-5'}> - <GroupByFieldSvg></GroupByFieldSvg> - </i> - ), - title: t('grid.settings.group'), - onClick: onGroupClick, - }, - ]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [t]); - - return ( - <PopupSelect - onOutsideClick={() => hidePopup()} - items={settingsItems} - className={'absolute left-full top-full z-10 text-xs'} - ></PopupSelect> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardTextCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardTextCell.tsx deleted file mode 100644 index 613c797ca8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardTextCell.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { useCell } from '../_shared/database-hooks/useCell'; - -export const BoardTextCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - return ( - <div> - {((data as string | undefined) ?? '').split('\n').map((line, index) => ( - <div key={index}>{line}</div> - ))} - - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardToolbar.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardToolbar.hooks.ts deleted file mode 100644 index b9fd587734..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardToolbar.hooks.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useState } from 'react'; - -export const useBoardToolbar = () => { - const [showSettings, setShowSettings] = useState(false); - const [showAllFields, setShowAllFields] = useState(false); - const [showGroupFields, setShowGroupFields] = useState(false); - - const onSettingsClick = () => { - setShowSettings(!showSettings); - }; - - const onFieldsClick = () => { - setShowSettings(false); - setShowAllFields(true); - }; - - const onGroupClick = () => { - setShowSettings(false); - setShowGroupFields(true); - }; - - const hidePopup = () => { - setShowSettings(false); - setShowAllFields(false); - setShowGroupFields(false); - }; - - return { - showSettings, - onSettingsClick, - onFieldsClick, - onGroupClick, - hidePopup, - showAllFields, - showGroupFields, - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardToolbar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardToolbar.tsx deleted file mode 100644 index 7459f39857..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardToolbar.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { SettingsSvg } from '$app/components/_shared/svg/SettingsSvg'; -import { useBoardToolbar } from '$app/components/board/BoardToolbar.hooks'; -import { BoardSettingsPopup } from '$app/components/board/BoardSettingsPopup'; -import { BoardFieldsPopup } from '$app/components/board/BoardFieldsPopup'; -import { BoardGroupFieldsPopup } from '$app/components/board/BoardGroupFieldsPopup'; - -export const BoardToolbar = ({ title }: { title: string }) => { - const { showSettings, showAllFields, showGroupFields, onSettingsClick, onFieldsClick, onGroupClick, hidePopup } = - useBoardToolbar(); - - return ( - <div className={'relative flex items-center gap-2'}> - <div className={'text-xl font-semibold'}>{title}</div> - <button onClick={() => onSettingsClick()} className={'h-5 w-5'}> - <SettingsSvg></SettingsSvg> - </button> - {showSettings && ( - <BoardSettingsPopup - hidePopup={hidePopup} - onFieldsClick={onFieldsClick} - onGroupClick={onGroupClick} - ></BoardSettingsPopup> - )} - {showAllFields && <BoardFieldsPopup hidePopup={hidePopup}></BoardFieldsPopup>} - {showGroupFields && <BoardGroupFieldsPopup hidePopup={hidePopup}></BoardGroupFieldsPopup>} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardUrlCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardUrlCell.tsx deleted file mode 100644 index 8c5188dca8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/board/BoardUrlCell.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; -import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; -import { FieldController } from '$app/stores/effects/database/field/field_controller'; -import { useCell } from '$app/components/_shared/database-hooks/useCell'; -import { URLCellDataPB } from '@/services/backend'; - -export const BoardUrlCell = ({ - cellIdentifier, - cellCache, - fieldController, -}: { - cellIdentifier: CellIdentifier; - cellCache: CellCache; - fieldController: FieldController; -}) => { - const { data } = useCell(cellIdentifier, cellCache, fieldController); - - return ( - <> - <a className={'text-fill-hover hover:underline'} href={(data as URLCellDataPB)?.url ?? ''} target={'_blank'}> - {(data as URLCellDataPB)?.content ?? ''} - </a> - </> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts index 36ab3a385a..1e83853530 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.hooks.ts @@ -13,7 +13,7 @@ import { rowListeners, sortListeners, filterListeners, -} from './application'; +} from '$app/application/database'; export function useSelectDatabaseView({ viewId }: { viewId?: string }) { const key = 'v'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx index 416277c76f..4e59b16b2f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/Database.tsx @@ -1,18 +1,18 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useViewId } from '$app/hooks/ViewId.hooks'; -import { databaseViewService } from './application'; +import { databaseViewService } from '$app/application/database'; import { DatabaseTabBar } from './components'; import { DatabaseLoader } from './DatabaseLoader'; import { DatabaseView } from './DatabaseView'; import { DatabaseCollection } from './components/database_settings'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; import SwipeableViews from 'react-swipeable-views'; import { TabPanel } from '$app/components/database/components/tab_bar/ViewTabs'; import DatabaseSettings from '$app/components/database/components/database_settings/DatabaseSettings'; import { Portal } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { ErrorCode } from '@/services/backend'; +import { ErrorCode, FolderNotification } from '@/services/backend'; import ExpandRecordModal from '$app/components/database/components/edit_record/ExpandRecordModal'; +import { subscribeNotifications } from '$app/application/notification'; interface Props { selectedViewId?: string; @@ -44,15 +44,18 @@ export const Database = ({ selectedViewId, setSelectedViewId }: Props) => { onPageChanged(); - const pageController = new PageController(viewId); + const unsubscribePromise = subscribeNotifications( + { + [FolderNotification.DidUpdateView]: () => { + onPageChanged(); + }, + }, + { + id: viewId, + } + ); - void pageController.subscribe({ - onPageChanged, - }); - - return () => { - void pageController.unsubscribe(); - }; + return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); }, [viewId]); const value = useMemo(() => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts index 74be8bea8c..76bba7b152 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.hooks.ts @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { DatabaseNotification } from '@/services/backend'; import { useNotification, useViewId } from '$app/hooks'; -import { cellService, Cell, Field } from '../../application'; +import { cellService, Cell, Field } from '$app/application/database'; import { useDispatchCell, useSelectorCell } from '$app/components/database'; export const useCell = (rowId: string, field: Field) => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx index 67175131e1..a092a1d75a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/Cell.tsx @@ -1,7 +1,7 @@ import React, { FC, HTMLAttributes } from 'react'; import { FieldType } from '@/services/backend'; -import { Cell as CellType, Field } from '../../application'; +import { Cell as CellType, Field } from '$app/application/database'; import { useCell } from './Cell.hooks'; import { TextCell } from './TextCell'; import { SelectCell } from './SelectCell'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx index cff353e31a..f74f038e09 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/CheckboxCell.tsx @@ -2,7 +2,7 @@ import React, { FC, useCallback } from 'react'; import { ReactComponent as CheckboxCheckSvg } from '$app/assets/database/checkbox-check.svg'; import { ReactComponent as CheckboxUncheckSvg } from '$app/assets/database/checkbox-uncheck.svg'; import { useViewId } from '$app/hooks'; -import { cellService, CheckboxCell as CheckboxCellType, Field } from '../../application'; +import { cellService, CheckboxCell as CheckboxCellType, Field } from '$app/application/database'; export const CheckboxCell: FC<{ field: Field; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx index a72b16b376..ab591855ee 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/ChecklistCell.tsx @@ -1,5 +1,5 @@ import React, { useState, Suspense, useMemo } from 'react'; -import { ChecklistCell as ChecklistCellType, ChecklistField } from '$app/components/database/application'; +import { ChecklistCell as ChecklistCellType, ChecklistField } from '$app/application/database'; import Typography from '@mui/material/Typography'; import ChecklistCellActions from '$app/components/database/components/field_types/checklist/ChecklistCellActions'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx index 31f35afd5b..bf74fd44c5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/DateTimeCell.tsx @@ -1,5 +1,5 @@ import React, { Suspense, useRef, useState, useMemo } from 'react'; -import { DateTimeCell as DateTimeCellType, DateTimeField } from '$app/components/database/application'; +import { DateTimeCell as DateTimeCellType, DateTimeField } from '$app/application/database'; import DateTimeCellActions from '$app/components/database/components/field_types/date/DateTimeCellActions'; interface Props { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx index af13b65d07..727e78de3f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/NumberCell.tsx @@ -1,5 +1,5 @@ import React, { Suspense, useCallback, useMemo, useRef } from 'react'; -import { Field, NumberCell as NumberCellType } from '$app/components/database/application'; +import { Field, NumberCell as NumberCellType } from '$app/application/database'; import { CellText } from '$app/components/database/_shared'; import EditNumberCellInput from '$app/components/database/components/field_types/number/EditNumberCellInput'; import { useInputCell } from '$app/components/database/components/cell/Cell.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx index 62bd30f7ab..5abe28cbfe 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/SelectCell.tsx @@ -1,6 +1,6 @@ import { FC, useCallback, useMemo, useState, Suspense, lazy } from 'react'; import { MenuProps, Menu } from '@mui/material'; -import { SelectField, SelectCell as SelectCellType, SelectTypeOption } from '../../application'; +import { SelectField, SelectCell as SelectCellType, SelectTypeOption } from '$app/application/database'; import { Tag } from '../field_types/select/Tag'; import { useTypeOption } from '$app/components/database'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx index 7ad0bd0653..38927d744b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TextCell.tsx @@ -1,5 +1,5 @@ import { FC, FormEventHandler, Suspense, lazy, useCallback, useRef, useMemo } from 'react'; -import { TextCell as TextCellType } from '../../application'; +import { TextCell as TextCellType } from '$app/application/database'; import { CellText } from '../../_shared'; import { useInputCell } from '$app/components/database/components/cell/Cell.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx index f7aeec9f2c..5889a13915 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/TimestampCell.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { CreatedTimeField, LastEditedTimeField, TimeStampCell } from '$app/components/database/application'; +import { CreatedTimeField, LastEditedTimeField, TimeStampCell } from '$app/application/database'; interface Props { field: LastEditedTimeField | CreatedTimeField; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx index d1c2c90db0..e07e81c1af 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/cell/URLCell.tsx @@ -1,6 +1,6 @@ import React, { FormEventHandler, lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useInputCell } from '$app/components/database/components/cell/Cell.hooks'; -import { Field, UrlCell as URLCellType } from '$app/components/database/application'; +import { Field, UrlCell as URLCellType } from '$app/application/database'; import { CellText } from '$app/components/database/_shared'; const EditTextCellInput = lazy(() => import('$app/components/database/components/field_types/text/EditTextCellInput')); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx index c150caa36c..a89b533bca 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/Properties.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { useDatabase } from '$app/components/database'; -import { Field as FieldType, fieldService } from '$app/components/database/application'; +import { Field as FieldType, fieldService } from '$app/application/database'; import { Property } from '$app/components/database/components/property'; import { FieldVisibility } from '@/services/backend'; import { ReactComponent as EyeOpen } from '$app/assets/eye_open.svg'; @@ -41,7 +41,7 @@ function Properties({ onItemClick }: PropertiesProps) { setState(newProperties); - await fieldService.moveField(viewId, draggableId, newId ?? ""); + await fieldService.moveField(viewId, draggableId, newId ?? ''); }; return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx index 4dd2f2b3c3..2b740e6e0b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/database_settings/SettingsMenu.tsx @@ -2,9 +2,9 @@ import React, { useState } from 'react'; import { Menu, MenuItem, MenuProps, Popover } from '@mui/material'; import { useTranslation } from 'react-i18next'; import Properties from '$app/components/database/components/database_settings/Properties'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { FieldVisibility } from '@/services/backend'; -import { updateFieldSetting } from '$app/components/database/application/field/field_service'; +import { updateFieldSetting } from '$app/application/database/field/field_service'; import { useViewId } from '$app/hooks'; type SettingsMenuProps = MenuProps; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx index 3d60ad71b2..b14a3a9783 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/EditRecord.tsx @@ -2,10 +2,10 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import RecordDocument from '$app/components/database/components/edit_record/RecordDocument'; import RecordHeader from '$app/components/database/components/edit_record/RecordHeader'; import { Page } from '$app_reducers/pages/slice'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; import { ErrorCode, ViewLayoutPB } from '@/services/backend'; import { Log } from '$app/utils/log'; import { useDatabase } from '$app/components/database'; +import { createOrphanPage, getPage } from '$app/application/folder/page.service'; interface Props { rowId: string; @@ -21,10 +21,9 @@ function EditRecord({ rowId }: Props) { const loadPage = useCallback(async () => { if (!id) return; - const controller = new PageController(id); try { - const page = await controller.getPage(); + const page = await getPage(id); setPage(page); } catch (e) { @@ -33,7 +32,8 @@ function EditRecord({ rowId }: Props) { // @ts-ignore if (e.code === ErrorCode.RecordNotFound) { try { - const page = await controller.createOrphanPage({ + const page = await createOrphanPage({ + view_id: id, name: '', layout: ViewLayoutPB.Document, }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx index 362e616cf7..34d05d0988 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/ExpandRecordModal.tsx @@ -34,7 +34,7 @@ function ExpandRecordModal({ open, onClose, rowId }: Props) { > <DetailsIcon /> </IconButton> - <DialogContent className={'p-0'}> + <DialogContent className={'relative p-0'}> <EditRecord rowId={rowId} /> </DialogContent> </Dialog> diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx index 5d9f769aec..81fc0ed146 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordActions.tsx @@ -3,7 +3,7 @@ import { Icon, Menu, MenuProps } from '@mui/material'; import { ReactComponent as DelSvg } from '$app/assets/delete.svg'; import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; import { useTranslation } from 'react-i18next'; -import { rowService } from '$app/components/database/application'; +import { rowService } from '$app/application/database'; import { useViewId } from '$app/hooks'; import MenuItem from '@mui/material/MenuItem'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx index 6210ed5f70..f3f1820a49 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordHeader.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react'; import RecordTitle from '$app/components/database/components/edit_record/RecordTitle'; import RecordProperties from '$app/components/database/components/edit_record/record_properties/RecordProperties'; import { Divider } from '@mui/material'; -import { RowMeta } from '$app/components/database/application'; +import { RowMeta } from '$app/application/database'; import { Page } from '$app_reducers/pages/slice'; interface Props { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx index 8908b0de5e..c2f195aee2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/RecordTitle.tsx @@ -1,10 +1,10 @@ import React, { useCallback, useMemo } from 'react'; import { Page, PageIcon } from '$app_reducers/pages/slice'; -import ViewTitle from '$app/components/_shared/ViewTitle'; +import ViewTitle from '$app/components/_shared/view_title/ViewTitle'; import { ViewIconTypePB } from '@/services/backend'; import { useViewId } from '$app/hooks'; -import { updateRowMeta } from '$app/components/database/application/row/row_service'; -import { cellService, Field, RowMeta, TextCell } from '$app/components/database/application'; +import { updateRowMeta } from '$app/application/database/row/row_service'; +import { cellService, Field, RowMeta, TextCell } from '$app/application/database'; import { useDatabase } from '$app/components/database'; import { useCell } from '$app/components/database/components/cell/Cell.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx index 023194e811..279ee13f68 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/Property.tsx @@ -1,7 +1,7 @@ import React, { HTMLAttributes } from 'react'; import PropertyName from '$app/components/database/components/edit_record/record_properties/PropertyName'; import PropertyValue from '$app/components/database/components/edit_record/record_properties/PropertyValue'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { IconButton, Tooltip } from '@mui/material'; import { ReactComponent as DragSvg } from '$app/assets/drag.svg'; import { useTranslation } from 'react-i18next'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx index 0443545d49..138c7543fd 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyList.tsx @@ -1,5 +1,5 @@ import React, { HTMLAttributes, useState } from 'react'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import Property from '$app/components/database/components/edit_record/record_properties/Property'; import { Draggable } from 'react-beautiful-dnd'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx index b2c2bb16de..e7de3f1fb0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyName.tsx @@ -1,6 +1,6 @@ import React, { useRef } from 'react'; import { Property } from '$app/components/database/components/property'; -import { Field as FieldType } from '$app/components/database/application'; +import { Field as FieldType } from '$app/application/database'; interface Props { field: FieldType; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx index 90f8147f9f..4bb33e7f05 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/PropertyValue.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useRef, useState } from 'react'; import { Cell } from '$app/components/database/components'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { useTranslation } from 'react-i18next'; function PropertyValue(props: { rowId: string; field: Field }) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx index 3f9de43e44..16fc122615 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/edit_record/record_properties/RecordProperties.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { Field, fieldService, RowMeta } from '$app/components/database/application'; +import { Field, fieldService, RowMeta } from '$app/application/database'; import { useDatabase } from '$app/components/database'; import { FieldVisibility } from '@/services/backend'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx index ff3c5db172..fc3f72e224 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/AddNewOption.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { updateChecklistCell } from '$app/components/database/application/cell/cell_service'; +import { updateChecklistCell } from '$app/application/database/cell/cell_service'; import { useViewId } from '$app/hooks'; import { ReactComponent as AddIcon } from '$app/assets/add.svg'; import { IconButton } from '@mui/material'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx index ba63902dc6..4905765481 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistCellActions.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Popover, { PopoverProps } from '@mui/material/Popover'; import { LinearProgressWithLabel } from '$app/components/database/components/field_types/checklist/LinearProgressWithLabel'; import { Divider } from '@mui/material'; -import { ChecklistCell as ChecklistCellType } from '$app/components/database/application'; +import { ChecklistCell as ChecklistCellType } from '$app/application/database'; import ChecklistItem from '$app/components/database/components/field_types/checklist/ChecklistItem'; import AddNewOption from '$app/components/database/components/field_types/checklist/AddNewOption'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx index 076dc854ea..9a33c8b9a3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/checklist/ChecklistItem.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; -import { SelectOption } from '$app/components/database/application'; +import { SelectOption } from '$app/application/database'; import { Checkbox, IconButton } from '@mui/material'; -import { updateChecklistCell } from '$app/components/database/application/cell/cell_service'; +import { updateChecklistCell } from '$app/application/database/cell/cell_service'; import { useViewId } from '$app/hooks'; import { ReactComponent as DeleteIcon } from '$app/assets/delete.svg'; import { ReactComponent as CheckboxCheckSvg } from '$app/assets/database/checkbox-check.svg'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx index 631ad8bb8c..f0c6a84250 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeCellActions.tsx @@ -1,9 +1,9 @@ import React, { useCallback, useMemo } from 'react'; import Popover, { PopoverProps } from '@mui/material/Popover'; -import { DateTimeCell, DateTimeField, DateTimeTypeOption } from '$app/components/database/application'; +import { DateTimeCell, DateTimeField, DateTimeTypeOption } from '$app/application/database'; import { useViewId } from '$app/hooks'; import { useTranslation } from 'react-i18next'; -import { updateDateCell } from '$app/components/database/application/cell/cell_service'; +import { updateDateCell } from '$app/application/database/cell/cell_service'; import { Divider, MenuItem, MenuList } from '@mui/material'; import dayjs from 'dayjs'; import RangeSwitch from '$app/components/database/components/field_types/date/RangeSwitch'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx index 8225413a71..6c4b41a494 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFieldActions.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UndeterminedDateField } from '$app/components/database/application'; +import { UndeterminedDateField } from '$app/application/database'; import DateTimeFormat from '$app/components/database/components/field_types/date/DateTimeFormat'; import { Divider } from '@mui/material'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx index 7033077a00..0107997c24 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormat.tsx @@ -1,7 +1,7 @@ import React, { useCallback } from 'react'; import DateFormat from '$app/components/database/components/field_types/date/DateFormat'; import TimeFormat from '$app/components/database/components/field_types/date/TimeFormat'; -import { TimeStampTypeOption, UndeterminedDateField, updateTypeOption } from '$app/components/database/application'; +import { TimeStampTypeOption, UndeterminedDateField, updateTypeOption } from '$app/application/database'; import { DateFormatPB, FieldType, TimeFormatPB } from '@/services/backend'; import { useViewId } from '$app/hooks'; import Typography from '@mui/material/Typography'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx index c65da97817..81ea28ed47 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/date/DateTimeFormatSelect.tsx @@ -1,7 +1,7 @@ import React, { useState, useRef } from 'react'; import { Menu, MenuItem } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import { DateTimeField } from '$app/components/database/application'; +import { DateTimeField } from '$app/application/database'; import DateTimeFormat from '$app/components/database/components/field_types/date/DateTimeFormat'; import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx index 7bc654918b..c91717093b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/number/NumberFieldActions.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react'; -import { NumberField, NumberTypeOption, updateTypeOption } from '$app/components/database/application'; +import { NumberField, NumberTypeOption, updateTypeOption } from '$app/application/database'; import { Divider } from '@mui/material'; import { useTranslation } from 'react-i18next'; import NumberFormatSelect from '$app/components/database/components/field_types/number/NumberFormatSelect'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionMenu.tsx index cdd14ca25e..7b31d203b4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/SelectOptionMenu.tsx @@ -4,13 +4,13 @@ import { Divider, ListSubheader, MenuItem, MenuList, MenuProps, OutlinedInput } import { SelectOptionColorPB } from '@/services/backend'; import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; -import { SelectOption } from '../../../application'; +import { SelectOption } from '$app/application/database'; import { SelectOptionColorMap, SelectOptionColorTextMap } from './constants'; import Button from '@mui/material/Button'; import { deleteSelectOption, insertOrUpdateSelectOption, -} from '$app/components/database/application/field/select_option/select_option_service'; +} from '$app/application/database/field/select_option/select_option_service'; import { useViewId } from '$app/hooks'; import Popover from '@mui/material/Popover'; @@ -111,7 +111,7 @@ export const SelectOptionMenu: FC<SelectOptionMenuProps> = ({ fieldId, option, M <Divider /> <MenuItem disabled>{t('grid.selectOption.colorPanelTitle')}</MenuItem> - <MenuList className={'max-h-[300px] overflow-y-auto overflow-x-hidden'}> + <MenuList className={'max-h-[300px] overflow-y-auto overflow-x-hidden px-2'}> {Colors.map((color) => ( <MenuItem onClick={() => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx index 357dceab12..fd658fbc5d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectCellActions.tsx @@ -3,17 +3,12 @@ import { MenuItem } from '@mui/material'; import { t } from 'i18next'; import { CreateOption } from '$app/components/database/components/field_types/select/select_cell_actions/CreateOption'; import { SelectOptionItem } from '$app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem'; -import { - cellService, - SelectCell as SelectCellType, - SelectField, - SelectTypeOption, -} from '$app/components/database/application'; +import { cellService, SelectCell as SelectCellType, SelectField, SelectTypeOption } from '$app/application/database'; import { useViewId } from '$app/hooks'; import { createSelectOption, insertOrUpdateSelectOption, -} from '$app/components/database/application/field/select_option/select_option_service'; +} from '$app/application/database/field/select_option/select_option_service'; import { FieldType } from '@/services/backend'; import { useTypeOption } from '$app/components/database'; import SearchInput from './SearchInput'; @@ -130,7 +125,7 @@ function SelectCellActions({ {shouldCreateOption ? ( <CreateOption label={newOptionName} onClick={handleNewTagClick} /> ) : ( - <div className={'max-h-[300px] overflow-y-auto overflow-x-hidden'}> + <div className={'max-h-[300px] overflow-y-auto overflow-x-hidden px-2'}> {filteredOptions.map((option) => ( <MenuItem className={'px-2'} key={option.id} value={option.id}> <SelectOptionItem diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx index 3313d5e676..508d1e4aaa 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_cell_actions/SelectOptionItem.tsx @@ -1,7 +1,7 @@ import { FC, MouseEventHandler, useCallback, useRef, useState } from 'react'; import { IconButton } from '@mui/material'; import { ReactComponent as DetailsSvg } from '$app/assets/details.svg'; -import { SelectOption } from '../../../../application'; +import { SelectOption } from '$app/application/database'; import { SelectOptionMenu } from '../SelectOptionMenu'; import { Tag } from '../Tag'; import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx index 25a1e3eee0..b9caebd0c8 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/AddAnOption.tsx @@ -6,7 +6,7 @@ import { OutlinedInput } from '@mui/material'; import { createSelectOption, insertOrUpdateSelectOption, -} from '$app/components/database/application/field/select_option/select_option_service'; +} from '$app/application/database/field/select_option/select_option_service'; import { useViewId } from '$app/hooks'; function AddAnOption({ fieldId }: { fieldId: string }) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx index 71693276c6..f0190b5c92 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Option.tsx @@ -1,6 +1,6 @@ import React, { useRef, useState } from 'react'; import { ReactComponent as MoreIcon } from '$app/assets/more.svg'; -import { SelectOption } from '$app/components/database/application'; +import { SelectOption } from '$app/application/database'; // import { ReactComponent as DragIcon } from '$app/assets/drag.svg'; import { SelectOptionMenu } from '$app/components/database/components/field_types/select/SelectOptionMenu'; @@ -14,6 +14,7 @@ function Option({ option, fieldId }: { option: SelectOption; fieldId: string }) return ( <> <Button + size={'small'} onClick={() => setExpanded(!expanded)} color={'inherit'} // startIcon={<DragIcon />} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx index fea4b31475..f3701517c0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/Options.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { SelectOption } from '$app/components/database/application'; +import { SelectOption } from '$app/application/database'; import Option from './Option'; interface Props { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx index 80e41f11fa..43e46848b3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/field_types/select/select_field_actions/SelectFieldActions.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import AddAnOption from '$app/components/database/components/field_types/select/select_field_actions/AddAnOption'; import Options from '$app/components/database/components/field_types/select/select_field_actions/Options'; -import { SelectField, SelectTypeOption } from '$app/components/database/application'; +import { SelectField, SelectTypeOption } from '$app/application/database'; import { Divider } from '@mui/material'; import { useTypeOption } from '$app/components/database'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx index 615d03dc04..895f3cb39c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/Filter.tsx @@ -9,14 +9,14 @@ import { CheckboxFilterData, ChecklistFilterData, DateFilterData, -} from '$app/components/database/application'; +} from '$app/application/database'; import { Chip, Popover } from '@mui/material'; import { Property } from '$app/components/database/components/property'; import { ReactComponent as DropDownSvg } from '$app/assets/dropdown.svg'; import TextFilter from './text_filter/TextFilter'; import { FieldType } from '@/services/backend'; import FilterActions from '$app/components/database/components/filter/FilterActions'; -import { updateFilter } from '$app/components/database/application/filter/filter_service'; +import { updateFilter } from '$app/application/database/filter/filter_service'; import { useViewId } from '$app/hooks'; import SelectFilter from './select_filter/SelectFilter'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx index 865f9e1829..090102f34b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterActions.tsx @@ -1,9 +1,9 @@ import React, { useState } from 'react'; import { IconButton, Menu, MenuItem } from '@mui/material'; import { ReactComponent as MoreSvg } from '$app/assets/details.svg'; -import { Filter } from '$app/components/database/application'; +import { Filter } from '$app/application/database'; import { useTranslation } from 'react-i18next'; -import { deleteFilter } from '$app/components/database/application/filter/filter_service'; +import { deleteFilter } from '$app/application/database/filter/filter_service'; import { useViewId } from '$app/hooks'; function FilterActions({ filter }: { filter: Filter }) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx index 092b8cff7b..1b6d87296b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/FilterFieldsMenu.tsx @@ -1,11 +1,11 @@ import React, { MouseEvent, useCallback } from 'react'; import { MenuProps } from '@mui/material'; import PropertiesList from '$app/components/database/components/property/PropertiesList'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { useViewId } from '$app/hooks'; import { useTranslation } from 'react-i18next'; -import { insertFilter } from '$app/components/database/application/filter/filter_service'; -import { getDefaultFilter } from '$app/components/database/application/filter/filter_data'; +import { insertFilter } from '$app/application/database/filter/filter_service'; +import { getDefaultFilter } from '$app/application/database/filter/filter_data'; import Popover from '@mui/material/Popover'; function FilterFieldsMenu({ diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx index dabcb42b19..b603df79e2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/date_filter/DateFilter.tsx @@ -4,7 +4,7 @@ import { DateFilterData, DateTimeField, DateTimeTypeOption, -} from '$app/components/database/application'; +} from '$app/application/database'; import { DateFilterConditionPB } from '@/services/backend'; import CustomCalendar from '$app/components/database/components/field_types/date/CustomCalendar'; import DateTimeSet from '$app/components/database/components/field_types/date/DateTimeSet'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx index d15a296200..02ad8d6f25 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/select_filter/SelectFilter.tsx @@ -4,7 +4,7 @@ import { SelectFilter as SelectFilterType, SelectFilterData, SelectTypeOption, -} from '$app/components/database/application'; +} from '$app/application/database'; import { MenuItem, MenuList } from '@mui/material'; import { Tag } from '$app/components/database/components/field_types/select/Tag'; import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx index 85075d6ea6..ccb3e6dde5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/filter/text_filter/TextFilter.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { TextFilter as TextFilterType, TextFilterData } from '$app/components/database/application'; +import { TextFilter as TextFilterType, TextFilterData } from '$app/application/database'; import { TextField } from '@mui/material'; import { useTranslation } from 'react-i18next'; import { TextFilterConditionPB } from '@/services/backend'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx index ca501ca231..6ff76392b3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/NewProperty.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react'; -import { fieldService } from '$app/components/database/application'; +import { fieldService } from '$app/application/database'; import { FieldType } from '@/services/backend'; import { useTranslation } from 'react-i18next'; import Button from '@mui/material/Button'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx index 89a69141de..e47e7200f8 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertiesList.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo, useState } from 'react'; import { OutlinedInput, MenuItem, MenuList } from '@mui/material'; import { Property } from '$app/components/database/components/property/Property'; -import { Field as FieldType } from '../../application'; +import { Field as FieldType } from '$app/application/database'; import { useDatabase } from '$app/components/database'; interface FieldListProps { @@ -42,7 +42,7 @@ function PropertiesList({ showSearch, onItemClick, searchPlaceholder }: FieldLis <div className={'pt-2'}> {searchInput} {emptyList} - <MenuList className={'max-h-[300px] overflow-y-auto overflow-x-hidden'}> + <MenuList className={'max-h-[300px] overflow-y-auto overflow-x-hidden px-2'}> {fieldsResult.map((field) => ( <MenuItem className={'overflow-hidden text-ellipsis px-1'} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx index ea4de01e03..e3a93cd28f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/Property.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useRef, useState } from 'react'; -import { Field as FieldType } from '../../application'; +import { Field as FieldType } from '$app/application/database'; import { ProppertyTypeSvg } from './property_type/ProppertyTypeSvg'; import { PropertyMenu } from '$app/components/database/components/property/PropertyMenu'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx index 9e447394a0..305676f64a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyActions.tsx @@ -7,10 +7,10 @@ import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; import { ReactComponent as LeftSvg } from '$app/assets/left.svg'; import { ReactComponent as RightSvg } from '$app/assets/right.svg'; import { useViewId } from '$app/hooks'; -import { fieldService } from '$app/components/database/application'; +import { fieldService } from '$app/application/database'; import { OrderObjectPositionTypePB, FieldVisibility } from '@/services/backend'; import { MenuItem } from '@mui/material'; -import ConfirmDialog from '$app/components/_shared/app-dialog/ConfirmDialog'; +import DeleteConfirmDialog from '$app/components/_shared/delete_confirm_dialog/DeleteConfirmDialog'; import { useTranslation } from 'react-i18next'; export enum FieldAction { @@ -83,7 +83,8 @@ function PropertyActions({ fieldId, onMenuItemClick, isPrimary, actions = defaul break; case FieldAction.InsertLeft: case FieldAction.InsertRight: { - const fieldPosition = action === FieldAction.InsertLeft ? OrderObjectPositionTypePB.Before : OrderObjectPositionTypePB.After; + const fieldPosition = + action === FieldAction.InsertLeft ? OrderObjectPositionTypePB.Before : OrderObjectPositionTypePB.After; const field = await fieldService.createField({ viewId, @@ -124,7 +125,7 @@ function PropertyActions({ fieldId, onMenuItemClick, isPrimary, actions = defaul </MenuItem> ); })} - <ConfirmDialog + <DeleteConfirmDialog open={openConfirm} subtitle={''} title={t('grid.field.deleteFieldPromptMessage')} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx index 618a0d82c8..abd26a62ea 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyMenu.tsx @@ -1,7 +1,7 @@ import { Divider, MenuList } from '@mui/material'; import { FC, useCallback } from 'react'; import { useViewId } from '$app/hooks'; -import { Field, fieldService } from '../../application'; +import { Field, fieldService } from '$app/application/database'; import PropertyTypeMenuExtension from '$app/components/database/components/property/property_type/PropertyTypeMenuExtension'; import PropertyTypeSelect from '$app/components/database/components/property/property_type/PropertyTypeSelect'; import { FieldType } from '@/services/backend'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx index 7d0d806fd7..31e7a26456 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertyNameInput.tsx @@ -1,6 +1,6 @@ import React, { ChangeEventHandler, useCallback, useState } from 'react'; import { useViewId } from '$app/hooks'; -import { fieldService } from '$app/components/database/application'; +import { fieldService } from '$app/application/database'; import { Log } from '$app/utils/log'; import TextField from '@mui/material/TextField'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx index 1a798d965b..5c4522904a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/PropertySelect.tsx @@ -1,6 +1,6 @@ import { MenuItem, Select, SelectChangeEvent, SelectProps } from '@mui/material'; import { FC, useCallback } from 'react'; -import { Field as FieldType } from '../../application'; +import { Field as FieldType } from '$app/application/database'; import { useDatabase } from '../../Database.hooks'; import { Property } from './Property'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx index 33a279b6ed..f6bb705fc4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenu.tsx @@ -2,7 +2,7 @@ import { Divider, Menu, MenuItem, MenuProps } from '@mui/material'; import { FC, useMemo } from 'react'; import { FieldType } from '@/services/backend'; import { PropertyTypeText, ProppertyTypeSvg } from '$app/components/database/components/property'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; const FieldTypeGroup = [ diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx index 2a5c488194..b45b670757 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeMenuExtension.tsx @@ -1,6 +1,6 @@ import React, { useMemo } from 'react'; import { FieldType } from '@/services/backend'; -import { DateTimeField, Field, NumberField, SelectField } from '$app/components/database/application'; +import { DateTimeField, Field, NumberField, SelectField } from '$app/application/database'; import SelectFieldActions from '$app/components/database/components/field_types/select/select_field_actions/SelectFieldActions'; import NumberFieldActions from '$app/components/database/components/field_types/number/NumberFieldActions'; import DateTimeFieldActions from '$app/components/database/components/field_types/date/DateTimeFieldActions'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx index 8226b2aad5..27805c0035 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/property/property_type/PropertyTypeSelect.tsx @@ -1,7 +1,7 @@ import React, { useRef, useState } from 'react'; import { ProppertyTypeSvg } from '$app/components/database/components/property/property_type/ProppertyTypeSvg'; import { MenuItem } from '@mui/material'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { ReactComponent as MoreSvg } from '$app/assets/more.svg'; import { PropertyTypeMenu } from '$app/components/database/components/property/property_type/PropertyTypeMenu'; import { FieldType } from '@/services/backend'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx index ae9d801ff1..b0b80b6bb7 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortFieldsMenu.tsx @@ -1,7 +1,7 @@ import React, { FC, MouseEvent, useCallback } from 'react'; import { MenuProps } from '@mui/material'; import PropertiesList from '$app/components/database/components/property/PropertiesList'; -import { Field, sortService } from '$app/components/database/application'; +import { Field, sortService } from '$app/application/database'; import { SortConditionPB } from '@/services/backend'; import { useTranslation } from 'react-i18next'; import { useViewId } from '$app/hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx index f6e216f371..e042e9c9c5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortItem.tsx @@ -1,7 +1,7 @@ import { IconButton, SelectChangeEvent, Stack } from '@mui/material'; import { FC, useCallback } from 'react'; import { ReactComponent as CloseSvg } from '$app/assets/close.svg'; -import { Field, Sort, sortService } from '../../application'; +import { Field, Sort, sortService } from '$app/application/database'; import { PropertySelect } from '../property'; import { SortConditionSelect } from './SortConditionSelect'; import { useViewId } from '@/appflowy_app/hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx index 6e42830b5f..148d98f1cb 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/sort/SortMenu.tsx @@ -1,7 +1,7 @@ import { Menu, MenuProps } from '@mui/material'; import { FC, MouseEventHandler, useCallback, useState } from 'react'; import { useViewId } from '$app/hooks'; -import { sortService } from '../../application'; +import { sortService } from '$app/application/database'; import { useDatabase } from '../../Database.hooks'; import { SortItem } from './SortItem'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx index 6145a487f4..717bf1eb18 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/AddViewBtn.tsx @@ -3,7 +3,7 @@ import { IconButton } from '@mui/material'; import { ReactComponent as AddSvg } from '$app/assets/add.svg'; import { useTranslation } from 'react-i18next'; import { ViewLayoutPB } from '@/services/backend'; -import { createDatabaseView } from '$app/components/database/application/database_view/database_view_service'; +import { createDatabaseView } from '$app/application/database/database_view/database_view_service'; function AddViewBtn({ pageId, onCreated }: { pageId: string; onCreated: (id: string) => void }) { const { t } = useTranslation(); @@ -18,9 +18,11 @@ function AddViewBtn({ pageId, onCreated }: { pageId: string; onCreated: (id: str }; return ( - <IconButton onClick={onClick} size='small'> - <AddSvg /> - </IconButton> + <div className={'ml-1 flex items-center justify-center border-l border-line-divider px-1'}> + <IconButton className={'flex items-center justify-center'} onClick={onClick} size='small'> + <AddSvg /> + </IconButton> + </div> ); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx index 51ccdaaefa..86522b72aa 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/DatabaseTabBar.tsx @@ -47,6 +47,15 @@ export const DatabaseTabBar: FC<DatabaseTabBarProps> = ({ pageId, childViewIds, } }, [selectedViewId, setSelectedViewId, views]); + const openMenu = (view: Page) => { + return (e: React.MouseEvent<HTMLElement>) => { + e.preventDefault(); + e.stopPropagation(); + setContextMenuView(view); + setContextMenuAnchorEl(e.currentTarget); + }; + }; + return ( <div className='-mb-px flex items-center px-16'> <div className='flex flex-1 items-center border-b border-line-divider'> @@ -56,12 +65,8 @@ export const DatabaseTabBar: FC<DatabaseTabBarProps> = ({ pageId, childViewIds, return ( <ViewTab - onContextMenu={(e) => { - e.preventDefault(); - e.stopPropagation(); - setContextMenuView(view); - setContextMenuAnchorEl(e.currentTarget); - }} + onContextMenu={openMenu(view)} + onDoubleClick={openMenu(view)} key={view.id} icon={<Icon />} iconPosition='start' diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx index 1495c43ed8..0d61e62544 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewActions.tsx @@ -2,9 +2,9 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ReactComponent as DeleteSvg } from '$app/assets/delete.svg'; import { ReactComponent as EditSvg } from '$app/assets/edit.svg'; -import { deleteView, updateView } from '$app/components/database/application/database_view/database_view_service'; +import { deleteView, updateView } from '$app/application/database/database_view/database_view_service'; import { MenuItem, MenuProps, Menu } from '@mui/material'; -import RenameDialog from '$app/components/layout/NestedPage/RenameDialog'; +import RenameDialog from '$app/components/layout/nested_page/RenameDialog'; import { Page } from '$app_reducers/pages/slice'; enum ViewAction { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx index ee866ff91b..a88edf7f2a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/components/tab_bar/ViewTabs.tsx @@ -15,6 +15,7 @@ export const ViewTab = styled((props: TabProps) => <Tab disableRipple {...props} fontSize: '12px', lineHeight: '16px', minWidth: 'unset', + margin: '4px 0', '&.Mui-selected': { color: 'inherit', diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid/Grid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx similarity index 67% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid/Grid.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx index 154c0d0444..beb90c66dc 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid/Grid.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid.tsx @@ -1,5 +1,5 @@ import { FC } from 'react'; -import { GridTable, GridTableProps } from '../GridTable'; +import { GridTable, GridTableProps } from './grid_table'; export const Grid: FC<GridTableProps> = (props) => { return <GridTable {...props} />; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid/index.ts deleted file mode 100644 index 237cbef136..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/Grid/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Grid'; \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/index.ts deleted file mode 100644 index 2b6d663ef5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './GridCell'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/index.ts deleted file mode 100644 index 69112da7e2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './GridField'; \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/index.ts deleted file mode 100644 index 8551dd0ccc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './GridTable'; \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts index 91f8b16f3c..eadfadaa89 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/constants.ts @@ -1,4 +1,4 @@ -import { Field, RowMeta } from '../application'; +import { Field, RowMeta } from '$app/application/database'; export const GridCalculateCountHeight = 40; @@ -65,7 +65,7 @@ export const rowMetasToRenderRow = (rowMetas: RowMeta[]): RenderRow[] => { })), { type: RenderRowType.NewRow, - data: { }, + data: {}, }, { type: RenderRowType.CalculateRow, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCalculate/GridCalculate.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx similarity index 87% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCalculate/GridCalculate.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx index c6bcac90f9..da222e3330 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCalculate/GridCalculate.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/GridCalculate.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useDatabaseVisibilityRows } from '$app/components/database'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import { DEFAULT_FIELD_WIDTH, GRID_ACTIONS_WIDTH } from '$app/components/database/grid/constants'; interface Props { @@ -9,7 +9,7 @@ interface Props { getContainerRef?: () => React.RefObject<HTMLDivElement>; } -function GridCalculate({ field, index }: Props) { +export function GridCalculate({ field, index }: Props) { const rowMetas = useDatabaseVisibilityRows(); const count = rowMetas.length; const width = index === 0 ? GRID_ACTIONS_WIDTH : field.width ?? DEFAULT_FIELD_WIDTH; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCalculate/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/index.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCalculate/index.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_calculate/index.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/GridCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx similarity index 82% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/GridCell.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx index a1967de801..042ba1777d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/GridCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/GridCell.tsx @@ -1,10 +1,10 @@ import React, { CSSProperties, memo } from 'react'; import { GridColumn, RenderRow, RenderRowType } from '../constants'; -import GridNewRow from '$app/components/database/grid/GridNewRow/GridNewRow'; -import GridCalculate from '$app/components/database/grid/GridCalculate/GridCalculate'; +import GridNewRow from '$app/components/database/grid/grid_new_row/GridNewRow'; +import { GridCalculate } from '$app/components/database/grid/grid_calculate'; import { areEqual } from 'react-window'; import { Cell } from '$app/components/database/components'; -import PrimaryCell from '$app/components/database/grid/GridCell/PrimaryCell'; +import { PrimaryCell } from '$app/components/database/grid/grid_cell'; const getRenderRowKey = (row: RenderRow) => { if (row.type === RenderRowType.Row) { @@ -51,11 +51,7 @@ export const GridCell = memo(({ row, column, columnIndex, style, onEditRecord, g case RenderRowType.NewRow: return ( <div style={style} className={'flex border-b border-line-divider'}> - <GridNewRow - getContainerRef={getContainerRef} - index={columnIndex} - groupId={row.data.groupId} - /> + <GridNewRow getContainerRef={getContainerRef} index={columnIndex} groupId={row.data.groupId} /> </div> ); case RenderRowType.CalculateRow: diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/PrimaryCell.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx similarity index 95% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/PrimaryCell.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx index 1b2a7f1606..b9a734de7b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridCell/PrimaryCell.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/PrimaryCell.tsx @@ -2,9 +2,9 @@ import React, { Suspense, useMemo, useRef } from 'react'; import { ReactComponent as OpenIcon } from '$app/assets/open.svg'; import { IconButton } from '@mui/material'; -import { useGridTableHoverState } from '$app/components/database/grid/GridRowActions/GridRowActions.hooks'; +import { useGridTableHoverState } from '$app/components/database/grid/grid_row_actions'; -function PrimaryCell({ +export function PrimaryCell({ onEditRecord, icon, getContainerRef, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts new file mode 100644 index 0000000000..949d5054bf --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_cell/index.ts @@ -0,0 +1,2 @@ +export * from './GridCell'; +export * from './PrimaryCell'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridField.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx similarity index 94% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridField.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx index 691ab28a6f..d14df4f813 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridField.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridField.tsx @@ -2,12 +2,11 @@ import { Button, Tooltip } from '@mui/material'; import { DragEventHandler, FC, HTMLAttributes, memo, useCallback, useEffect, useMemo, useState } from 'react'; import { useViewId } from '$app/hooks'; import { DragItem, DropPosition, DragType, useDraggable, useDroppable, ScrollDirection } from '../../_shared'; -import { fieldService, Field } from '../../application'; +import { fieldService, Field } from '$app/application/database'; import { Property } from '$app/components/database/components/property'; -import GridResizer from '$app/components/database/grid/GridField/GridResizer'; -import GridFieldMenu from '$app/components/database/grid/GridField/GridFieldMenu'; +import { GridResizer, GridFieldMenu } from '$app/components/database/grid/grid_field'; import { areEqual } from 'react-window'; -import { useOpenMenu } from '$app/components/database/grid/GridStickyHeader/GridStickyHeader.hooks'; +import { useOpenMenu } from '$app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks'; import throttle from 'lodash-es/throttle'; export interface GridFieldProps extends HTMLAttributes<HTMLDivElement> { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridFieldMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx similarity index 90% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridFieldMenu.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx index 1509c93e3b..ce351240e1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridFieldMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridFieldMenu.tsx @@ -1,6 +1,6 @@ import React from 'react'; import Popover, { PopoverProps } from '@mui/material/Popover'; -import { Field } from '$app/components/database/application'; +import { Field } from '$app/application/database'; import PropertyNameInput from '$app/components/database/components/property/PropertyNameInput'; import { MenuList, Portal } from '@mui/material'; import PropertyActions, { FieldAction } from '$app/components/database/components/property/PropertyActions'; @@ -11,7 +11,7 @@ interface Props extends PopoverProps { onOpenMenu?: (fieldId: string) => void; } -function GridFieldMenu({ field, onOpenPropertyMenu, onOpenMenu, ...props }: Props) { +export function GridFieldMenu({ field, onOpenPropertyMenu, onOpenMenu, ...props }: Props) { return ( <Portal> <Popover diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridNewField.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx similarity index 85% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridNewField.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx index 10611fc446..d0b739298a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridNewField.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridNewField.tsx @@ -1,12 +1,12 @@ import React, { useCallback } from 'react'; import { useViewId } from '$app/hooks'; import { useTranslation } from 'react-i18next'; -import { fieldService } from '$app/components/database/application'; +import { fieldService } from '$app/application/database'; import { FieldType } from '@/services/backend'; import Button from '@mui/material/Button'; import { ReactComponent as AddSvg } from '$app/assets/add.svg'; -function GridNewField({ onInserted }: { onInserted?: (id: string) => void }) { +export function GridNewField({ onInserted }: { onInserted?: (id: string) => void }) { const viewId = useViewId(); const { t } = useTranslation(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridResizer.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx similarity index 93% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridResizer.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx index bac219ee08..e673458318 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridField/GridResizer.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/GridResizer.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { Field, fieldService } from '$app/components/database/application'; +import { Field, fieldService } from '$app/application/database'; import { useViewId } from '$app/hooks'; interface GridResizerProps { @@ -9,7 +9,7 @@ interface GridResizerProps { const minWidth = 100; -function GridResizer({ field, onWidthChange }: GridResizerProps) { +export function GridResizer({ field, onWidthChange }: GridResizerProps) { const viewId = useViewId(); const fieldId = field.id; const width = field.width || 0; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts new file mode 100644 index 0000000000..384ee2af3b --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_field/index.ts @@ -0,0 +1,4 @@ +export * from './GridField'; +export * from './GridFieldMenu'; +export * from './GridNewField'; +export * from './GridResizer'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridNewRow/GridNewRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx similarity index 95% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridNewRow/GridNewRow.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx index 23d490cf95..713430eb51 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridNewRow/GridNewRow.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_new_row/GridNewRow.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react'; -import { rowService } from '$app/components/database/application'; +import { rowService } from '$app/application/database'; import { useViewId } from '$app/hooks'; import { t } from 'i18next'; import { ReactComponent as AddSvg } from '$app/assets/add.svg'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridOverlay/GridTableOverlay.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx similarity index 77% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridOverlay/GridTableOverlay.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx index 6c1ecc95df..3443535258 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridOverlay/GridTableOverlay.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_overlay/GridTableOverlay.tsx @@ -1,8 +1,9 @@ import React, { useEffect, useState } from 'react'; -import GridRowContextMenu from '$app/components/database/grid/GridRowActions/GridRowContextMenu'; -import GridRowActions from '$app/components/database/grid/GridRowActions/GridRowActions'; - -import { useGridTableHoverState } from '$app/components/database/grid/GridRowActions/GridRowActions.hooks'; +import { + GridRowContextMenu, + GridRowActions, + useGridTableHoverState, +} from '$app/components/database/grid/grid_row_actions'; function GridTableOverlay({ containerRef, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowActions.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts similarity index 98% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowActions.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts index d49ab6addc..9237bb3c03 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowActions.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.hooks.ts @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { useViewId } from '$app/hooks'; -import { rowService } from '$app/components/database/application'; +import { rowService } from '$app/application/database'; import { autoScrollOnEdge, ScrollDirection } from '$app/components/database/_shared/dnd/utils'; export function getCellsWithRowId(rowId: string, container: HTMLDivElement) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowActions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx similarity index 90% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowActions.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx index 020bbc5387..2813c05f86 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowActions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowActions.tsx @@ -3,13 +3,12 @@ import { IconButton, Tooltip } from '@mui/material'; import { t } from 'i18next'; import { ReactComponent as AddSvg } from '$app/assets/add.svg'; import { GRID_ACTIONS_WIDTH } from '$app/components/database/grid/constants'; -import { rowService } from '$app/components/database/application'; +import { rowService } from '$app/application/database'; import { useViewId } from '$app/hooks'; -import GridRowDragButton from '$app/components/database/grid/GridRowActions/GridRowDragButton'; -import GridRowMenu from '$app/components/database/grid/GridRowActions/GridRowMenu'; +import { GridRowDragButton, GridRowMenu } from '$app/components/database/grid/grid_row_actions'; import { OrderObjectPositionTypePB } from '@/services/backend'; -function GridRowActions({ +export function GridRowActions({ rowId, rowTop, containerRef, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowContextMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx similarity index 97% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowContextMenu.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx index 0734b345c1..5e5aeeb619 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowContextMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowContextMenu.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import GridRowMenu from './GridRowMenu'; import { toggleProperty } from './GridRowActions.hooks'; -function GridRowContextMenu({ +export function GridRowContextMenu({ containerRef, hoverRowId, }: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowDragButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx similarity index 96% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowDragButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx index 1ebb018da9..6d271270a9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowDragButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowDragButton.tsx @@ -4,7 +4,7 @@ import { IconButton, Tooltip } from '@mui/material'; import { ReactComponent as DragSvg } from '$app/assets/drag.svg'; import { useTranslation } from 'react-i18next'; -function GridRowDragButton({ +export function GridRowDragButton({ rowId, containerRef, onClick, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx similarity index 95% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowMenu.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx index c8ab6bc7cf..3daa5041e3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridRowActions/GridRowMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/GridRowMenu.tsx @@ -6,7 +6,7 @@ import { ReactComponent as CopySvg } from '$app/assets/copy.svg'; import Popover, { PopoverProps } from '@mui/material/Popover'; import { useViewId } from '$app/hooks'; import { useTranslation } from 'react-i18next'; -import { rowService } from '$app/components/database/application'; +import { rowService } from '$app/application/database'; import { Icon, MenuItem, MenuList } from '@mui/material'; import { OrderObjectPositionTypePB } from '@/services/backend'; @@ -21,7 +21,7 @@ interface Props extends PopoverProps { rowId: string; } -function GridRowMenu({ rowId, ...props }: Props) { +export function GridRowMenu({ rowId, ...props }: Props) { const viewId = useViewId(); const { t } = useTranslation(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts new file mode 100644 index 0000000000..fb50b6248c --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_row_actions/index.ts @@ -0,0 +1,5 @@ +export * from './GridRowActions.hooks'; +export * from './GridRowActions'; +export * from './GridRowContextMenu'; +export * from './GridRowDragButton'; +export * from './GridRowMenu'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridStickyHeader/GridStickyHeader.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridStickyHeader/GridStickyHeader.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridStickyHeader/GridStickyHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx similarity index 91% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridStickyHeader/GridStickyHeader.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx index 2b67a4fe6f..333edf89ba 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridStickyHeader/GridStickyHeader.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_sticky_header/GridStickyHeader.tsx @@ -1,11 +1,11 @@ import React, { useCallback, useState } from 'react'; import { GridChildComponentProps, VariableSizeGrid as Grid } from 'react-window'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { useGridColumn } from '$app/components/database/grid/GridTable/GridTable.hooks'; -import { GridField } from '$app/components/database/grid/GridField'; +import { useGridColumn } from '$app/components/database/grid/grid_table'; +import { GridField } from 'src/appflowy_app/components/database/grid/grid_field'; import NewProperty from '$app/components/database/components/property/NewProperty'; import { GridColumn, GridColumnType } from '$app/components/database/grid/constants'; -import { OpenMenuContext } from '$app/components/database/grid/GridStickyHeader/GridStickyHeader.hooks'; +import { OpenMenuContext } from '$app/components/database/grid/grid_sticky_header/GridStickyHeader.hooks'; const GridStickyHeader = React.forwardRef< Grid<HTMLDivElement> | null, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.hooks.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.hooks.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx similarity index 91% rename from frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx index c0bede08ee..3dee0941a9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridTable/GridTable.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/GridTable.tsx @@ -1,14 +1,14 @@ import React, { FC, useCallback, useMemo, useRef } from 'react'; -import { RowMeta } from '../../application'; +import { RowMeta } from '$app/application/database'; import { useDatabaseVisibilityFields, useDatabaseVisibilityRows } from '../../Database.hooks'; import { fieldsToColumns, GridColumn, RenderRow, RenderRowType, rowMetasToRenderRow } from '../constants'; import { CircularProgress } from '@mui/material'; import { GridChildComponentProps, GridOnScrollProps, VariableSizeGrid as Grid } from 'react-window'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { GridCell } from '$app/components/database/grid/GridCell'; -import { useGridColumn, useGridRow } from '$app/components/database/grid/GridTable/GridTable.hooks'; -import GridStickyHeader from '$app/components/database/grid/GridStickyHeader/GridStickyHeader'; -import GridTableOverlay from '$app/components/database/grid/GridOverlay/GridTableOverlay'; +import { GridCell } from 'src/appflowy_app/components/database/grid/grid_cell'; +import { useGridColumn, useGridRow } from './GridTable.hooks'; +import GridStickyHeader from '$app/components/database/grid/grid_sticky_header/GridStickyHeader'; +import GridTableOverlay from '$app/components/database/grid/grid_overlay/GridTableOverlay'; import ReactDOM from 'react-dom'; export interface GridTableProps { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts new file mode 100644 index 0000000000..dfdb9b7949 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/grid_table/index.ts @@ -0,0 +1,2 @@ +export * from './GridTable'; +export * from './GridTable.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts index 9296c1d2fa..762542e7cb 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/index.ts @@ -1 +1 @@ -export { Grid } from './Grid'; \ No newline at end of file +export * from './Grid'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx index 87deb9e147..611aecc84a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/document_header/DocumentHeader.tsx @@ -1,27 +1,19 @@ import React, { memo, useCallback } from 'react'; import { Page, PageIcon } from '$app_reducers/pages/slice'; -import { useAppDispatch } from '$app/stores/store'; -import ViewTitle from '$app/components/_shared/ViewTitle'; -import { updatePageIcon } from '$app_reducers/pages/async_actions'; +import ViewTitle from '$app/components/_shared/view_title/ViewTitle'; +import { updatePageIcon } from '$app/application/folder/page.service'; interface DocumentHeaderProps { page: Page; } export function DocumentHeader({ page }: DocumentHeaderProps) { - const dispatch = useAppDispatch(); - const pageId = page.id; const onUpdateIcon = useCallback( - (icon: PageIcon) => { - void dispatch( - updatePageIcon({ - id: pageId, - icon: icon.value ? icon : undefined, - }) - ); + async (icon: PageIcon) => { + await updatePageIcon(pageId, icon.value ? icon : undefined); }, - [dispatch, pageId] + [pageId] ); if (!page) return null; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx index caed46c256..1d6be5c293 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/Editor.tsx @@ -7,10 +7,12 @@ import { EditorIdProvider } from '$app/components/editor/Editor.hooks'; export function Editor(props: EditorProps) { return ( - <EditorIdProvider value={props.id}> - <CollaborativeEditor {...props} /> - <Toaster /> - </EditorIdProvider> + <div className={'appflowy-editor relative'}> + <EditorIdProvider value={props.id}> + <CollaborativeEditor {...props} /> + <Toaster /> + </EditorIdProvider> + </div> ); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx index 1d4d225a02..9c8e163b45 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/_shared/PlaceholderContent.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties, useMemo } from 'react'; +import React, { CSSProperties, useEffect, useMemo, useState } from 'react'; import { ReactEditor, useSelected, useSlateStatic } from 'slate-react'; import { Editor, Element, Range } from 'slate'; import { EditorNodeType, HeadingNode } from '$app/application/document/document.types'; @@ -8,7 +8,7 @@ function PlaceholderContent({ node, ...attributes }: { node: Element; className? const { t } = useTranslation(); const editor = useSlateStatic(); const selected = useSelected() && !!editor.selection && Range.isCollapsed(editor.selection); - + const [isComposing, setIsComposing] = useState(false); const block = useMemo(() => { const path = ReactEditor.findPath(editor, node); const match = Editor.above(editor, { @@ -88,6 +88,31 @@ function PlaceholderContent({ node, ...attributes }: { node: Element; className? } }, [block?.type, t, unSelectedPlaceholder]); + useEffect(() => { + if (!selected) return; + + const handleCompositionStart = () => { + setIsComposing(true); + }; + + const handleCompositionEnd = () => { + setIsComposing(false); + }; + + const editorDom = ReactEditor.toDOMNode(editor, editor); + + editorDom.addEventListener('compositionstart', handleCompositionStart); + editorDom.addEventListener('compositionend', handleCompositionEnd); + return () => { + editorDom.removeEventListener('compositionstart', handleCompositionStart); + editorDom.removeEventListener('compositionend', handleCompositionEnd); + }; + }, [editor, selected]); + + if (isComposing) { + return null; + } + return ( <span contentEditable={false} {...attributes} className={className}> {selected ? selectedPlaceholder : unSelectedPlaceholder} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx index 3c32e988a1..5a57e57888 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/callout/CalloutIcon.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useRef, useState } from 'react'; import { IconButton } from '@mui/material'; import { CalloutNode } from '$app/application/document/document.types'; -import EmojiPicker from '$app/components/_shared/EmojiPicker'; +import EmojiPicker from '$app/components/_shared/emoji_picker/EmojiPicker'; import Popover from '@mui/material/Popover'; import { PopoverCommonProps } from '$app/components/editor/components/tools/popover'; import { useSlateStatic } from 'slate-react'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts index c56092f175..032502b415 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/database/utils.ts @@ -1,11 +1,11 @@ -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; import { ViewLayoutPB } from '@/services/backend'; +import { createPage } from '$app/application/folder/page.service'; export async function createGrid(pageId: string) { - const pageController = new PageController(pageId); - const newViewId = await pageController.createPage({ + const newViewId = await createPage({ layout: ViewLayoutPB.Grid, name: '', + parent_view_id: pageId, }); return newViewId; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx index f09c9d1052..db13ed49ed 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/blocks/math_equation/MathEquation.tsx @@ -1,6 +1,6 @@ import { forwardRef, memo, useState } from 'react'; import { EditorElementProps, MathEquationNode } from '$app/application/document/document.types'; -import KatexMath from '$app/components/_shared/KatexMath'; +import KatexMath from '$app/components/_shared/katex_math/KatexMath'; import { useTranslation } from 'react-i18next'; import { FunctionsOutlined } from '@mui/icons-material'; import EditPopover from '$app/components/editor/components/blocks/math_equation/EditPopover'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/shortcuts/shortcuts.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/shortcuts/shortcuts.hooks.ts index f48affad4b..c9894b5e8d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/shortcuts/shortcuts.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/editor/shortcuts/shortcuts.hooks.ts @@ -1,6 +1,6 @@ import { ReactEditor } from 'slate-react'; import { useCallback, KeyboardEvent } from 'react'; -import { EditorMarkFormat, EditorNodeType } from '$app/application/document/document.types'; +import { EditorMarkFormat, EditorNodeType, TodoListNode } from '$app/application/document/document.types'; import isHotkey from 'is-hotkey'; import { getBlock } from '$app/components/editor/plugins/utils'; @@ -95,6 +95,12 @@ export function useShortcuts(editor: ReactEditor) { return; } + + if (isHotkey('mod+Enter', e) && node && node.type === EditorNodeType.TodoListBlock) { + e.preventDefault(); + CustomEditor.toggleTodo(editor, node as TodoListNode); + return; + } }, [editor] ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx index f0f5b1a616..5899630867 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/inline_formula/FormulaLeaf.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import KatexMath from '$app/components/_shared/KatexMath'; +import KatexMath from '$app/components/_shared/katex_math/KatexMath'; function FormulaLeaf({ formula, children }: { formula: string; children: React.ReactNode }) { return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx index c8db92c2e1..adf22a53a0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/editor/components/inline_nodes/mention/MentionLeaf.tsx @@ -1,10 +1,10 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Mention, MentionPage } from '$app/application/document/document.types'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; import { ReactComponent as DocumentSvg } from '$app/assets/document.svg'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { pageTypeMap } from '$app_reducers/pages/slice'; +import { getPage } from '$app/application/folder/page.service'; export function MentionLeaf({ children, mention }: { mention: Mention; children: React.ReactNode }) { const { t } = useTranslation(); @@ -12,7 +12,7 @@ export function MentionLeaf({ children, mention }: { mention: Mention; children: const navigate = useNavigate(); const loadPage = useCallback(async () => { if (!mention.page) return; - const page = await new PageController(mention.page).getPage(); + const page = await getPage(mention.page); setPage(page); }, [mention.page]); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx index b41096f6c1..d912c4bf6b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Layout.tsx @@ -1,6 +1,6 @@ import React, { ReactNode, useEffect } from 'react'; -import SideBar from '$app/components/layout/SideBar'; -import TopBar from '$app/components/layout/TopBar'; +import SideBar from '$app/components/layout/side_bar/SideBar'; +import TopBar from '$app/components/layout/top_bar/TopBar'; import { useAppSelector } from '$app/stores/store'; import { FooterPanel } from '$app/components/layout/FooterPanel'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.hooks.ts deleted file mode 100644 index e04bd734a0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.hooks.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { useCallback, useEffect, useMemo } from 'react'; -import { WorkspaceController } from '$app/stores/effects/workspace/workspace_controller'; -import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { workspaceActions, WorkspaceItem } from '$app_reducers/workspace/slice'; -import { WorkspaceManagerController } from '$app/stores/effects/workspace/workspace_manager_controller'; -import { Page, pagesActions } from '$app_reducers/pages/slice'; - -export function useLoadWorkspaces() { - const dispatch = useAppDispatch(); - const { workspaces, currentWorkspace } = useAppSelector((state) => state.workspace); - - const onWorkspacesChanged = useCallback( - (data: { workspaces: WorkspaceItem[]; currentWorkspace: WorkspaceItem }) => { - dispatch(workspaceActions.onWorkspacesChanged(data)); - }, - [dispatch] - ); - - const controller = useMemo(() => { - return new WorkspaceManagerController(); - }, []); - - const initializeWorkspaces = useCallback(async () => { - const workspaces = await controller.getWorkspaces(); - const currentWorkspace = await controller.getCurrentWorkspace(); - - dispatch( - workspaceActions.initWorkspaces({ - workspaces, - currentWorkspace, - }) - ); - }, [controller, dispatch]); - - const subscribeToWorkspaces = useCallback(async () => { - await controller.subscribe({ - onWorkspacesChanged, - }); - }, [controller, onWorkspacesChanged]); - - useEffect(() => { - void (async () => { - await initializeWorkspaces(); - await subscribeToWorkspaces(); - })(); - - return () => { - void controller.dispose(); - }; - }, [controller, initializeWorkspaces, subscribeToWorkspaces]); - - return { - workspaces, - currentWorkspace, - }; -} - -export function useLoadWorkspace(workspace: WorkspaceItem) { - const { id } = workspace; - const dispatch = useAppDispatch(); - - const controller = useMemo(() => { - return new WorkspaceController(id); - }, [id]); - - const openWorkspace = useCallback(async () => { - await controller.open(); - }, [controller]); - - const deleteWorkspace = useCallback(async () => { - await controller.delete(); - }, [controller]); - - const onChildPagesChanged = useCallback( - (childPages: Page[]) => { - dispatch( - pagesActions.addChildPages({ - id, - childPages, - }) - ); - }, - [dispatch, id] - ); - - const initializeWorkspace = useCallback(async () => { - const childPages = await controller.getChildPages(); - - dispatch( - pagesActions.addChildPages({ - id, - childPages, - }) - ); - }, [controller, dispatch, id]); - - const subscribeToWorkspace = useCallback(async () => { - await controller.subscribe({ - onChildPagesChanged, - }); - }, [controller, onChildPagesChanged]); - - useEffect(() => { - void (async () => { - await initializeWorkspace(); - await subscribeToWorkspace(); - })(); - - return () => { - void controller.dispose(); - }; - }, [controller, initializeWorkspace, subscribeToWorkspace]); - - return { - openWorkspace, - controller, - deleteWorkspace, - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx similarity index 93% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx index 0b7a688e38..5ef81d2e17 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/BreadCrumb.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useMemo } from 'react'; -import { useLoadExpandedPages } from '$app/components/layout/Breadcrumb/Breadcrumb.hooks'; +import { useLoadExpandedPages } from '$app/components/layout/bread_crumb/Breadcrumb.hooks'; import Breadcrumbs from '@mui/material/Breadcrumbs'; import Link from '@mui/material/Link'; import Typography from '@mui/material/Typography'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts similarity index 91% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts index 82d7854a3a..ed10c5b7b9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/bread_crumb/Breadcrumb.hooks.ts @@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useParams, useLocation } from 'react-router-dom'; import { Page } from '$app_reducers/pages/slice'; import { useTranslation } from 'react-i18next'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; +import { getPage } from '$app/application/folder/page.service'; export function useLoadExpandedPages() { const { t } = useTranslation(); @@ -26,11 +26,10 @@ export function useLoadExpandedPages() { const loadPagePath = useCallback( async (pageId: string) => { let page = pageMap[pageId]; - const controller = new PageController(pageId); if (!page) { try { - page = await controller.getPage(); + page = await getPage(pageId); } catch (e) { // do nothing } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/CollapseMenuButton/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/collapse_menu_button/CollapseMenuButton.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/CollapseMenuButton/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/collapse_menu_button/CollapseMenuButton.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/AddButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx similarity index 79% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/AddButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx index 80c4bfed07..266775515c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/AddButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/AddButton.tsx @@ -1,11 +1,10 @@ import React, { useMemo } from 'react'; -import ButtonPopoverList from '$app/components/_shared/ButtonPopoverList'; +import ButtonPopoverList from '$app/components/_shared/button_menu/ButtonMenu'; import { IconButton } from '@mui/material'; import { ReactComponent as AddSvg } from '$app/assets/add.svg'; import { useTranslation } from 'react-i18next'; import { ReactComponent as DocumentSvg } from '$app/assets/document.svg'; import { ReactComponent as GridSvg } from '$app/assets/grid.svg'; -import { ReactComponent as BoardSvg } from '$app/assets/board.svg'; import { ViewLayoutPB } from '@/services/backend'; function AddButton({ isVisible, onAddPage }: { isVisible: boolean; onAddPage: (layout: ViewLayoutPB) => void }) { @@ -36,18 +35,6 @@ function AddButton({ isVisible, onAddPage }: { isVisible: boolean; onAddPage: (l onAddPage(ViewLayoutPB.Grid); }, }, - { - key: 'add-board', - label: t('board.menuName'), - icon: ( - <div className={'h-5 w-5'}> - <BoardSvg /> - </div> - ), - onClick: () => { - onAddPage(ViewLayoutPB.Board); - }, - }, ], [onAddPage, t] ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/DeleteDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx similarity index 86% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/DeleteDialog.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx index 85697b4bdf..83ed658ac1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/DeleteDialog.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/DeleteDialog.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { ViewLayoutPB } from '@/services/backend'; -import ConfirmDialog from '$app/components/_shared/app-dialog/ConfirmDialog'; +import DeleteConfirmDialog from '$app/components/_shared/delete_confirm_dialog/DeleteConfirmDialog'; function DeleteDialog({ layout, @@ -24,7 +24,7 @@ function DeleteDialog({ }[layout]; return ( - <ConfirmDialog + <DeleteConfirmDialog open={open} title={t('views.deleteContentTitle', { pageType, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx similarity index 94% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/MoreButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx index c12d1eea36..92bb254bd3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/MoreButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/MoreButton.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { IconButton } from '@mui/material'; -import ButtonPopoverList from '../../_shared/ButtonPopoverList'; +import ButtonPopoverList from '$app/components/_shared/button_menu/ButtonMenu'; import { ReactComponent as DetailsSvg } from '$app/assets/details.svg'; import { ReactComponent as EditSvg } from '$app/assets/edit.svg'; @@ -10,7 +10,7 @@ import { ReactComponent as TrashSvg } from '$app/assets/delete.svg'; import RenameDialog from './RenameDialog'; import { Page } from '$app_reducers/pages/slice'; -import DeleteDialog from '$app/components/layout/NestedPage/DeleteDialog'; +import DeleteDialog from '$app/components/layout/nested_page/DeleteDialog'; function MoreButton({ isVisible, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts similarity index 66% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts index f754fa8416..1081fc2cd1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.hooks.ts @@ -1,10 +1,11 @@ -import { useCallback, useEffect, useMemo } from 'react'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; -import { Page, pagesActions, pageTypeMap } from '$app_reducers/pages/slice'; +import { useCallback, useEffect } from 'react'; +import { Page, pagesActions, pageTypeMap, parserViewPBToPage } from '$app_reducers/pages/slice'; import { useAppDispatch, useAppSelector } from '$app/stores/store'; -import { ViewLayoutPB } from '@/services/backend'; +import { FolderNotification, ViewLayoutPB } from '@/services/backend'; import { useNavigate, useParams } from 'react-router-dom'; import { updatePageName } from '$app_reducers/pages/async_actions'; +import { createPage, deletePage, duplicatePage, getChildPages } from '$app/application/folder/page.service'; +import { subscribeNotifications } from '$app/application/notification'; export function useLoadChildPages(pageId: string) { const dispatch = useAppDispatch(); @@ -18,26 +19,16 @@ export function useLoadChildPages(pageId: string) { } }, [dispatch, pageId, collapsed]); - const controller = useMemo(() => { - return new PageController(pageId); - }, [pageId]); - const onPageChanged = useCallback( - (page: Page, children: Page[]) => { + (page: Page) => { dispatch(pagesActions.onPageChanged(page)); - dispatch( - pagesActions.addChildPages({ - id: page.id, - childPages: children, - }) - ); }, [dispatch] ); const loadPageChildren = useCallback( async (pageId: string) => { - const childPages = await controller.getChildPages(); + const childPages = await getChildPages(pageId); dispatch( pagesActions.addChildPages({ @@ -46,7 +37,7 @@ export function useLoadChildPages(pageId: string) { }) ); }, - [controller, dispatch] + [dispatch] ); useEffect(() => { @@ -54,13 +45,24 @@ export function useLoadChildPages(pageId: string) { }, [loadPageChildren, pageId]); useEffect(() => { - void controller.subscribe({ - onPageChanged, - }); - return () => { - void controller.dispose(); - }; - }, [controller, onPageChanged]); + const unsubscribePromise = subscribeNotifications( + { + [FolderNotification.DidUpdateView]: async (payload) => { + const page = parserViewPBToPage(payload); + + onPageChanged(page); + }, + [FolderNotification.DidUpdateChildViews]: async () => { + void loadPageChildren(pageId); + }, + }, + { + id: pageId, + } + ); + + return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); + }, [pageId, onPageChanged, loadPageChildren]); return { toggleCollapsed, @@ -73,9 +75,6 @@ export function usePageActions(pageId: string) { const page = useAppSelector((state) => state.pages.pageMap[pageId]); const dispatch = useAppDispatch(); const navigate = useNavigate(); - const controller = useMemo(() => { - return new PageController(pageId); - }, [pageId]); const onPageClick = useCallback(() => { const pageType = pageTypeMap[page.layout]; @@ -85,9 +84,10 @@ export function usePageActions(pageId: string) { const onAddPage = useCallback( async (layout: ViewLayoutPB) => { - const newViewId = await controller.createPage({ + const newViewId = await createPage({ layout, name: '', + parent_view_id: pageId, }); dispatch(pagesActions.expandPage(pageId)); @@ -95,16 +95,16 @@ export function usePageActions(pageId: string) { navigate(`/page/${pageType}/${newViewId}`); }, - [controller, dispatch, navigate, pageId] + [dispatch, navigate, pageId] ); const onDeletePage = useCallback(async () => { - await controller.deletePage(); - }, [controller]); + await deletePage(pageId); + }, [pageId]); const onDuplicatePage = useCallback(async () => { - await controller.duplicatePage(); - }, [controller]); + await duplicatePage(pageId); + }, [pageId]); const onRenamePage = useCallback( async (name: string) => { @@ -113,12 +113,6 @@ export function usePageActions(pageId: string) { [dispatch, pageId] ); - useEffect(() => { - return () => { - void controller.dispose(); - }; - }, [controller]); - return { onAddPage, onPageClick, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx similarity index 93% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx index dc7906d63c..54e283e878 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPage.tsx @@ -1,9 +1,9 @@ import React, { useCallback, useMemo } from 'react'; import Collapse from '@mui/material/Collapse'; import { TransitionGroup } from 'react-transition-group'; -import NestedPageTitle from '$app/components/layout/NestedPage/NestedPageTitle'; -import { useLoadChildPages, usePageActions } from '$app/components/layout/NestedPage/NestedPage.hooks'; -import { useDrag } from '$app/components/_shared/drag-block'; +import NestedPageTitle from '$app/components/layout/nested_page/NestedPageTitle'; +import { useLoadChildPages, usePageActions } from '$app/components/layout/nested_page/NestedPage.hooks'; +import { useDrag } from 'src/appflowy_app/components/_shared/drag_block'; import { useAppDispatch } from '$app/stores/store'; import { movePageThunk } from '$app_reducers/pages/async_actions'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPageTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx similarity index 96% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPageTitle.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx index 98de2e167d..5b33d8a157 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPageTitle.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/NestedPageTitle.tsx @@ -4,7 +4,7 @@ import { useAppSelector } from '$app/stores/store'; import AddButton from './AddButton'; import MoreButton from './MoreButton'; import { ViewLayoutPB } from '@/services/backend'; -import { useSelectedPage } from '$app/components/layout/NestedPage/NestedPage.hooks'; +import { useSelectedPage } from '$app/components/layout/nested_page/NestedPage.hooks'; import { useTranslation } from 'react-i18next'; function NestedPageTitle({ diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/RenameDialog.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/RenameDialog.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/RenameDialog.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/nested_page/RenameDialog.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Share/Share.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.hooks.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/Share/Share.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.hooks.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Share/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx similarity index 85% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/Share/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx index eb97cc96b0..1a10cb08e6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Share/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/share/Share.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import Button from '@mui/material/Button'; -import { useShareConfig } from '$app/components/layout/Share/Share.hooks'; +import { useShareConfig } from '$app/components/layout/share/Share.hooks'; function ShareButton() { const { showShareButton } = useShareConfig(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/Resizer.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/Resizer.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/Resizer.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/Resizer.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx similarity index 81% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx index 4b525da21d..cfde67bde6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/SideBar.tsx @@ -3,10 +3,10 @@ import { useAppSelector } from '$app/stores/store'; import { ThemeMode } from '$app/stores/reducers/current-user/slice'; import { AppflowyLogoDark } from '$app/components/_shared/svg/AppflowyLogoDark'; import { AppflowyLogoLight } from '$app/components/_shared/svg/AppflowyLogoLight'; -import CollapseMenuButton from '$app/components/layout/CollapseMenuButton'; -import Resizer from '$app/components/layout/SideBar/Resizer'; -import UserInfo from '$app/components/layout/SideBar/UserInfo'; -import WorkspaceManager from '$app/components/layout/WorkspaceManager'; +import CollapseMenuButton from '$app/components/layout/collapse_menu_button/CollapseMenuButton'; +import Resizer from '$app/components/layout/side_bar/Resizer'; +import UserInfo from '$app/components/layout/side_bar/UserInfo'; +import WorkspaceManager from '$app/components/layout/workspace_manager/WorkspaceManager'; function SideBar() { const { isCollapsed, width, isResizing } = useAppSelector((state) => state.sidebar); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/UserInfo.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx similarity index 95% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/UserInfo.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx index a4aaca150a..bde742f57a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/SideBar/UserInfo.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/side_bar/UserInfo.tsx @@ -3,7 +3,7 @@ import { useAppSelector } from '$app/stores/store'; import { Avatar } from '@mui/material'; import PersonOutline from '@mui/icons-material/PersonOutline'; import ArrowDropDown from '@mui/icons-material/ArrowDropDown'; -import UserSetting from '../UserSetting'; +import UserSetting from '../user_setting/UserSetting'; function UserInfo() { const currentUser = useAppSelector((state) => state.currentUser); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/FontSizeConfig.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/FontSizeConfig.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/FontSizeConfig.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/FontSizeConfig.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx similarity index 85% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx index ca104218ab..1ffae1eb85 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreButton.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next'; import { Drawer, IconButton } from '@mui/material'; import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; import Tooltip from '@mui/material/Tooltip'; -import MoreOptions from '$app/components/layout/TopBar/MoreOptions'; -import { useMoreOptionsConfig } from '$app/components/layout/TopBar/MoreOptions.hooks'; +import MoreOptions from '$app/components/layout/top_bar/MoreOptions'; +import { useMoreOptionsConfig } from '$app/components/layout/top_bar/MoreOptions.hooks'; function MoreButton() { const { t } = useTranslation(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.hooks.ts similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.hooks.ts rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.hooks.ts diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx similarity index 59% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx index 28f2b46759..7f77259212 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/MoreOptions.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import FontSizeConfig from '$app/components/layout/TopBar/FontSizeConfig'; -import { useMoreOptionsConfig } from '$app/components/layout/TopBar/MoreOptions.hooks'; +import FontSizeConfig from '$app/components/layout/top_bar/FontSizeConfig'; +import { useMoreOptionsConfig } from '$app/components/layout/top_bar/MoreOptions.hooks'; function MoreOptions() { const { showStyleOptions } = useMoreOptionsConfig(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx similarity index 72% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx index d3d99a2fd5..178c596f6b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/top_bar/TopBar.tsx @@ -1,9 +1,9 @@ import React from 'react'; -import CollapseMenuButton from '$app/components/layout/CollapseMenuButton'; +import CollapseMenuButton from '$app/components/layout/collapse_menu_button/CollapseMenuButton'; import { useAppSelector } from '$app/stores/store'; -import Breadcrumb from '$app/components/layout/Breadcrumb'; -import ShareButton from '$app/components/layout/Share'; -import MoreButton from '$app/components/layout/TopBar/MoreButton'; +import Breadcrumb from '$app/components/layout/bread_crumb/BreadCrumb'; +import ShareButton from '$app/components/layout/share/Share'; +import MoreButton from '$app/components/layout/top_bar/MoreButton'; function TopBar() { const sidebarIsCollapsed = useAppSelector((state) => state.sidebar.isCollapsed); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/AppearanceSetting.tsx similarity index 82% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/AppearanceSetting.tsx index cf69f1c426..e2d0367a51 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/AppearanceSetting.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/AppearanceSetting.tsx @@ -36,24 +36,6 @@ function AppearanceSetting({ [t] ); - const themeOptions = useMemo( - () => [ - { - value: Theme.Default, - content: 'Default', - }, - { - value: Theme.Dandelion, - content: 'Dandelion', - }, - { - value: Theme.Lavender, - content: 'Lavender', - }, - ], - [] - ); - const renderSelect = useCallback( ( items: { @@ -107,16 +89,6 @@ function AppearanceSetting({ }); }, }, - { - options: themeOptions, - label: t('settings.appearance.theme'), - value: theme, - onChange: (newValue) => { - onChange({ - theme: newValue as Theme, - }); - }, - }, ])} </div> ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/LanguageSetting.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/LanguageSetting.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/Menu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/Menu.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/Menu.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/Menu.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/SettingPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/SettingPanel.tsx similarity index 100% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/SettingPanel.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/SettingPanel.tsx diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/UserSetting.tsx similarity index 74% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/UserSetting.tsx index 256f4e96ac..7fd52de56a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/user_setting/UserSetting.tsx @@ -8,9 +8,9 @@ import UserSettingPanel from './SettingPanel'; import { Theme, UserSetting } from '$app/stores/reducers/current-user/slice'; import { useAppDispatch, useAppSelector } from '$app/stores/store'; import { currentUserActions } from '$app_reducers/current-user/slice'; -import { useUserSettingControllerContext } from '$app/components/_shared/app-hooks/useUserSettingControllerContext'; import { ThemeModePB } from '@/services/backend'; import { useTranslation } from 'react-i18next'; +import { UserService } from '$app/application/user/user.service'; const SlideTransition = React.forwardRef((props: SlideProps, ref) => { return <Slide {...props} direction='up' ref={ref} />; @@ -19,7 +19,6 @@ const SlideTransition = React.forwardRef((props: SlideProps, ref) => { function UserSettings({ open, onClose }: { open: boolean; onClose: () => void }) { const userSettingState = useAppSelector((state) => state.currentUser.userSetting); const dispatch = useAppDispatch(); - const userSettingController = useUserSettingControllerContext(); const { t } = useTranslation(); const [selected, setSelected] = useState<MenuItem>(MenuItem.Appearance); const handleChange = useCallback( @@ -27,20 +26,18 @@ function UserSettings({ open, onClose }: { open: boolean; onClose: () => void }) const newSetting = { ...userSettingState, ...setting }; dispatch(currentUserActions.setUserSetting(newSetting)); - if (userSettingController) { - const language = newSetting.language || 'en'; + const language = newSetting.language || 'en'; - void userSettingController.setAppearanceSetting({ - theme: newSetting.theme || Theme.Default, - theme_mode: newSetting.themeMode || ThemeModePB.Light, - locale: { - language_code: language.split('-')[0], - country_code: language.split('-')[1], - }, - }); - } + void UserService.setAppearanceSetting({ + theme: newSetting.theme || Theme.Default, + theme_mode: newSetting.themeMode || ThemeModePB.Light, + locale: { + language_code: language.split('-')[0], + country_code: language.split('-')[1], + }, + }); }, - [dispatch, userSettingController, userSettingState] + [dispatch, userSettingState] ); return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/MoreButton.tsx similarity index 94% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/MoreButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/MoreButton.tsx index e22818eeb2..efdd8d1144 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/MoreButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/MoreButton.tsx @@ -5,7 +5,7 @@ import MoreIcon from '@mui/icons-material/MoreHoriz'; import SettingsIcon from '@mui/icons-material/Settings'; import { useTranslation } from 'react-i18next'; import { DeleteOutline } from '@mui/icons-material'; -import ButtonPopoverList from '$app/components/_shared/ButtonPopoverList'; +import ButtonPopoverList from '$app/components/_shared/button_menu/ButtonMenu'; function MoreButton({ workspace, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NestedPages.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx similarity index 86% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NestedPages.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx index dea679c0f6..80d61440b7 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NestedPages.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NestedPages.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useAppSelector } from '$app/stores/store'; -import NestedPage from '$app/components/layout/NestedPage'; +import NestedPage from '$app/components/layout/nested_page/NestedPage'; function WorkspaceNestedPages({ workspaceId }: { workspaceId: string }) { const pageIds = useAppSelector((state) => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx similarity index 72% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx index ad405024a1..f82cb2208e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/NewPageButton.tsx @@ -1,26 +1,19 @@ -import React, { useEffect, useMemo } from 'react'; +import React from 'react'; import AddSvg from '$app/components/_shared/svg/AddSvg'; import { useTranslation } from 'react-i18next'; -import { WorkspaceController } from '$app/stores/effects/workspace/workspace_controller'; import { ViewLayoutPB } from '@/services/backend'; import { useNavigate } from 'react-router-dom'; +import { createCurrentWorkspaceChildView } from '$app/application/folder/workspace.service'; function NewPageButton({ workspaceId }: { workspaceId: string }) { const { t } = useTranslation(); - const controller = useMemo(() => new WorkspaceController(workspaceId), [workspaceId]); const navigate = useNavigate(); - useEffect(() => { - return () => { - void controller.dispose(); - }; - }, [controller]); - return ( <div className={'flex h-[60px] w-full items-center border-t border-line-divider px-6 py-5'}> <button onClick={async () => { - const { id } = await controller.createView({ + const { id } = await createCurrentWorkspaceChildView({ name: '', layout: ViewLayoutPB.Document, parent_view_id: workspaceId, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/TrashButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx similarity index 83% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/TrashButton.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx index 11d085fb53..944c23df01 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/TrashButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/TrashButton.tsx @@ -2,8 +2,8 @@ import React, { useCallback } from 'react'; import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; -import { useDrag } from '$app/components/_shared/drag-block'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; +import { useDrag } from 'src/appflowy_app/components/_shared/drag_block'; +import { deletePage } from '$app/application/folder/page.service'; function TrashButton() { const { t } = useTranslation(); @@ -16,9 +16,7 @@ function TrashButton() { const selected = currentPathType === 'trash'; const onEnd = useCallback((result: { dragId: string; position: 'before' | 'after' | 'inside' }) => { - const controller = new PageController(result.dragId); - - void controller.deletePage(); + void deletePage(result.dragId); }, []); const { onDrop, onDragOver, onDragLeave, isDraggingOver } = useDrag({ diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts new file mode 100644 index 0000000000..2b5ac678e8 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.hooks.ts @@ -0,0 +1,97 @@ +import { useCallback, useEffect } from 'react'; +import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { workspaceActions, WorkspaceItem } from '$app_reducers/workspace/slice'; +import { Page, pagesActions, parserViewPBToPage } from '$app_reducers/pages/slice'; +import { subscribeNotifications } from '$app/application/notification'; +import { FolderNotification } from '@/services/backend'; +import * as workspaceService from '$app/application/folder/workspace.service'; + +export function useLoadWorkspaces() { + const dispatch = useAppDispatch(); + const { workspaces, currentWorkspace } = useAppSelector((state) => state.workspace); + + const initializeWorkspaces = useCallback(async () => { + const workspaces = await workspaceService.getWorkspaces(); + const currentWorkspace = await workspaceService.getCurrentWorkspace(); + + dispatch( + workspaceActions.initWorkspaces({ + workspaces, + currentWorkspace, + }) + ); + }, [dispatch]); + + useEffect(() => { + void (async () => { + await initializeWorkspaces(); + })(); + }, [initializeWorkspaces]); + + return { + workspaces, + currentWorkspace, + }; +} + +export function useLoadWorkspace(workspace: WorkspaceItem) { + const { id } = workspace; + const dispatch = useAppDispatch(); + + const openWorkspace = useCallback(async () => { + await workspaceService.openWorkspace(id); + }, [id]); + + const deleteWorkspace = useCallback(async () => { + await workspaceService.deleteWorkspace(id); + }, [id]); + + const onChildPagesChanged = useCallback( + (childPages: Page[]) => { + dispatch( + pagesActions.addChildPages({ + id, + childPages, + }) + ); + }, + [dispatch, id] + ); + + const initializeWorkspace = useCallback(async () => { + const childPages = await workspaceService.getWorkspaceChildViews(id); + + dispatch( + pagesActions.addChildPages({ + id, + childPages, + }) + ); + }, [dispatch, id]); + + useEffect(() => { + void (async () => { + await initializeWorkspace(); + })(); + }, [initializeWorkspace]); + + useEffect(() => { + const unsubscribePromise = subscribeNotifications( + { + [FolderNotification.DidUpdateWorkspaceViews]: async (changeset) => { + const res = changeset.items; + + onChildPagesChanged(res.map(parserViewPBToPage)); + }, + }, + { id } + ); + + return () => void unsubscribePromise.then((unsubscribe) => unsubscribe()); + }, [id, onChildPagesChanged]); + + return { + openWorkspace, + deleteWorkspace, + }; +} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx similarity index 74% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx index 97453c4ccc..db9ce8f4b1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/Workspace.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { WorkspaceItem } from '$app_reducers/workspace/slice'; -import NestedViews from '$app/components/layout/WorkspaceManager/NestedPages'; -import { useLoadWorkspace } from '$app/components/layout/WorkspaceManager/Workspace.hooks'; +import NestedViews from '$app/components/layout/workspace_manager/NestedPages'; +import { useLoadWorkspace } from '$app/components/layout/workspace_manager/Workspace.hooks'; function Workspace({ workspace, opened }: { workspace: WorkspaceItem; opened: boolean }) { useLoadWorkspace(workspace); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx similarity index 75% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/index.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx index 945c3d2d40..a7a90e785f 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceManager.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import NewPageButton from '$app/components/layout/WorkspaceManager/NewPageButton'; -import { useLoadWorkspaces } from '$app/components/layout/WorkspaceManager/Workspace.hooks'; +import NewPageButton from '$app/components/layout/workspace_manager/NewPageButton'; +import { useLoadWorkspaces } from '$app/components/layout/workspace_manager/Workspace.hooks'; import Workspace from './Workspace'; -import TrashButton from '$app/components/layout/WorkspaceManager/TrashButton'; +import TrashButton from '$app/components/layout/workspace_manager/TrashButton'; function WorkspaceManager() { const { workspaces, currentWorkspace } = useLoadWorkspaces(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/WorkspaceTitle.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceTitle.tsx similarity index 92% rename from frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/WorkspaceTitle.tsx rename to frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceTitle.tsx index 9a256035fe..4c9f8988a6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/WorkspaceTitle.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/workspace_manager/WorkspaceTitle.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import MoreButton from '$app/components/layout/WorkspaceManager/MoreButton'; +import MoreButton from '$app/components/layout/workspace_manager/MoreButton'; import MenuItem from '@mui/material/MenuItem'; import { WorkspaceItem } from '$app_reducers/workspace/slice'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/AllIcons.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/AllIcons.tsx deleted file mode 100644 index 3f3acfbb35..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/AllIcons.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import AddSvg from '$app/components/_shared/svg/AddSvg'; -import { ArrowLeftSvg } from '$app/components/_shared/svg/ArrowLeftSvg'; -import { ArrowRightSvg } from '$app/components/_shared/svg/ArrowRightSvg'; -import { BoardSvg } from '$app/components/_shared/svg/BoardSvg'; -import { CheckboxSvg } from '$app/components/_shared/svg/CheckboxSvg'; -import { ChecklistTypeSvg } from '$app/components/_shared/svg/ChecklistTypeSvg'; -import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; -import { ClockSvg } from '$app/components/_shared/svg/ClockSvg'; -import { CloseSvg } from '$app/components/_shared/svg/CloseSvg'; -import { CopySvg } from '$app/components/_shared/svg/CopySvg'; -import { DateTypeSvg } from '$app/components/_shared/svg/DateTypeSvg'; -import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; -import { DocumentSvg } from '$app/components/_shared/svg/DocumentSvg'; -import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; -import { EarthSvg } from '$app/components/_shared/svg/EarthSvg'; -import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; -import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; -import { EditSvg } from '$app/components/_shared/svg/EditSvg'; -import { EyeClosedSvg } from '$app/components/_shared/svg/EyeClosedSvg'; -import { EyeOpenSvg } from '$app/components/_shared/svg/EyeOpenSvg'; -import { FilterSvg } from '$app/components/_shared/svg/FilterSvg'; -import { GridSvg } from '$app/components/_shared/svg/GridSvg'; -import { GroupByFieldSvg } from '$app/components/_shared/svg/GroupByFieldSvg'; -import { HideMenuSvg } from '$app/components/_shared/svg/HideMenuSvg'; -import { InformationSvg } from '$app/components/_shared/svg/InformationSvg'; -import { LogoutSvg } from '$app/components/_shared/svg/LogoutSvg'; -import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; -import { MultiSelectTypeSvg } from '$app/components/_shared/svg/MultiSelectTypeSvg'; -import { NumberTypeSvg } from '$app/components/_shared/svg/NumberTypeSvg'; -import { PropertiesSvg } from '$app/components/_shared/svg/PropertiesSvg'; -import { SearchSvg } from '$app/components/_shared/svg/SearchSvg'; -import { ShowMenuSvg } from '$app/components/_shared/svg/ShowMenuSvg'; -import { SingleSelectTypeSvg } from '$app/components/_shared/svg/SingleSelectTypeSvg'; -import { SkipLeftSvg } from '$app/components/_shared/svg/SkipLeftSvg'; -import { SkipRightSvg } from '$app/components/_shared/svg/SkipRightSvg'; -import { SortSvg } from '$app/components/_shared/svg/SortSvg'; -import { TextTypeSvg } from '$app/components/_shared/svg/TextTypeSvg'; -import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { UrlTypeSvg } from '$app/components/_shared/svg/UrlTypeSvg'; -import { DragElementSvg } from '$app/components/_shared/svg/DragElementSvg'; -import { ImageSvg } from '$app/components/_shared/svg/ImageSvg'; -import { DragSvg } from '$app/components/_shared/svg/DragSvg'; -import { FullView } from '$app/components/_shared/svg/FullView'; -import { GroupBySvg } from '$app/components/_shared/svg/GroupBySvg'; -import { SettingsSvg } from '$app/components/_shared/svg/SettingsSvg'; -import { ShareSvg } from '$app/components/_shared/svg/ShareSvg'; -import { SortAscSvg } from '$app/components/_shared/svg/SortAscSvg'; -import { SortDescSvg } from '$app/components/_shared/svg/SortDescSvg'; - -export const AllIcons = () => { - return ( - <div className={'p-8'}> - <h1 className={'mb-12 text-2xl'}>Icons</h1> - <div className={'mb-8'}> - <div className={'flex flex-wrap items-center gap-8'}> - <i className={'h-5 w-5'} title={'AddSvg'}> - <AddSvg></AddSvg> - </i> - <i className={'h-5 w-5'} title={'ArrowLeftSvg'}> - <ArrowLeftSvg></ArrowLeftSvg> - </i> - <i className={'h-5 w-5'} title={'ArrowRightSvg'}> - <ArrowRightSvg></ArrowRightSvg> - </i> - <i className={'h-5 w-5'} title={'BoardSvg'}> - <BoardSvg></BoardSvg> - </i> - <i className={'h-5 w-5'} title={'CheckboxSvg'}> - <CheckboxSvg></CheckboxSvg> - </i> - <i className={'h-5 w-5'} title={'ChecklistTypeSvg'}> - <ChecklistTypeSvg></ChecklistTypeSvg> - </i> - <i className={'h-5 w-5'} title={'CheckmarkSvg'}> - <CheckmarkSvg></CheckmarkSvg> - </i> - <i className={'h-5 w-5'} title={'ClockSvg'}> - <ClockSvg></ClockSvg> - </i> - <i className={'h-5 w-5'} title={'CloseSvg'}> - <CloseSvg></CloseSvg> - </i> - <i className={'h-5 w-5'} title={'CopySvg'}> - <CopySvg></CopySvg> - </i> - <i className={'h-5 w-5'} title={'DateTypeSvg'}> - <DateTypeSvg></DateTypeSvg> - </i> - <i className={'h-5 w-5'} title={'Details2Svg'}> - <Details2Svg></Details2Svg> - </i> - <i className={'h-5 w-5'} title={'DocumentSvg'}> - <DocumentSvg></DocumentSvg> - </i> - <i className={'h-5 w-5'} title={'DragElementSvg'}> - <DragElementSvg></DragElementSvg> - </i> - <i className={'h-5 w-5'} title={'DragSvg'}> - <DragSvg></DragSvg> - </i> - <i className={'h-5 w-5'} title={'DropDownShowSvg'}> - <DropDownShowSvg></DropDownShowSvg> - </i> - <i className={'h-5 w-5'} title={'EarthSvg'}> - <EarthSvg></EarthSvg> - </i> - <i className={'h-5 w-5'} title={'EditorCheckSvg'}> - <EditorCheckSvg></EditorCheckSvg> - </i> - <i className={'h-5 w-5'} title={'EditorUncheckSvg'}> - <EditorUncheckSvg></EditorUncheckSvg> - </i> - <i className={'h-5 w-5'} title={'EditSvg'}> - <EditSvg></EditSvg> - </i> - <i className={'h-5 w-5'} title={'EyeClosedSvg'}> - <EyeClosedSvg></EyeClosedSvg> - </i> - <i className={'h-5 w-5'} title={'EyeOpenSvg'}> - <EyeOpenSvg></EyeOpenSvg> - </i> - <i className={'h-5 w-5'} title={'FilterSvg'}> - <FilterSvg></FilterSvg> - </i> - <i className={'h-5 w-5'} title={'FullView'}> - <FullView></FullView> - </i> - <i className={'h-5 w-5'} title={'GridSvg'}> - <GridSvg></GridSvg> - </i> - <i className={'h-5 w-5'} title={'GroupByFieldSvg'}> - <GroupByFieldSvg></GroupByFieldSvg> - </i> - <i className={'h-5 w-5'} title={'GroupBySvg'}> - <GroupBySvg></GroupBySvg> - </i> - <i className={'h-5 w-5'} title={'HideMenuSvg'}> - <HideMenuSvg></HideMenuSvg> - </i> - <i className={'h-5 w-5'} title={'ImageSvg'}> - <ImageSvg></ImageSvg> - </i> - <i className={'h-5 w-5'} title={'InformationSvg'}> - <InformationSvg></InformationSvg> - </i> - <i className={'h-5 w-5'} title={'LogoutSvg'}> - <LogoutSvg></LogoutSvg> - </i> - <i className={'h-5 w-5'} title={'MoreSvg'}> - <MoreSvg></MoreSvg> - </i> - <i className={'h-5 w-5'} title={'MultiSelectTypeSvg'}> - <MultiSelectTypeSvg></MultiSelectTypeSvg> - </i> - <i className={'h-5 w-5'} title={'NumberTypeSvg'}> - <NumberTypeSvg></NumberTypeSvg> - </i> - <i className={'h-5 w-5'} title={'PropertiesSvg'}> - <PropertiesSvg></PropertiesSvg> - </i> - <i className={'h-5 w-5'} title={'SearchSvg'}> - <SearchSvg></SearchSvg> - </i> - <i className={'h-5 w-5'} title={'SettingsSvg'}> - <SettingsSvg></SettingsSvg> - </i> - <i className={'h-5 w-5'} title={'ShareSvg'}> - <ShareSvg></ShareSvg> - </i> - <i className={'h-5 w-5'} title={'ShowMenuSvg'}> - <ShowMenuSvg></ShowMenuSvg> - </i> - <i className={'h-5 w-5'} title={'SingleSelectTypeSvg'}> - <SingleSelectTypeSvg></SingleSelectTypeSvg> - </i> - <i className={'h-5 w-5'} title={'SkipLeftSvg'}> - <SkipLeftSvg></SkipLeftSvg> - </i> - <i className={'h-5 w-5'} title={'SkipRightSvg'}> - <SkipRightSvg></SkipRightSvg> - </i> - <i className={'h-5 w-5'} title={'SortAscSvg'}> - <SortAscSvg></SortAscSvg> - </i> - <i className={'h-5 w-5'} title={'SortDescSvg'}> - <SortDescSvg></SortDescSvg> - </i> - <i className={'h-5 w-5'} title={'SortSvg'}> - <SortSvg></SortSvg> - </i> - <i className={'h-5 w-5'} title={'TextTypeSvg'}> - <TextTypeSvg></TextTypeSvg> - </i> - <i className={'h-5 w-5'} title={'TrashSvg'}> - <TrashSvg></TrashSvg> - </i> - <i className={'h-5 w-5'} title={'UrlTypeSvg'}> - <UrlTypeSvg></UrlTypeSvg> - </i> - </div> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx deleted file mode 100644 index 1a8e5dee95..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/ColorPalette.tsx +++ /dev/null @@ -1,45 +0,0 @@ -export const ColorPalette = () => { - return ( - <div className={'p-8'}> - <h1 className={'mb-4 text-2xl'}>Colors</h1> - <h2 className={'mb-4'}>Main</h2> - <div className={'mb-8 flex flex-wrap items-center'}> - <div title={'fill-hover'} className={'m-2 h-[100px] w-[100px] bg-fill-default'}></div> - <div title={'main-hovered'} className={'m-2 h-[100px] w-[100px] bg-fill-list-hover'}></div> - <div title={'fill-list-hover'} className={'m-2 h-[100px] w-[100px] bg-fill-list-hover'}></div> - <div title={'main-selector'} className={'m-2 h-[100px] w-[100px] bg-fill-selector'}></div> - <div title={'main-alert'} className={'m-2 h-[100px] w-[100px] bg-function-info'}></div> - <div title={'main-warning'} className={'m-2 h-[100px] w-[100px] bg-function-warning'}></div> - <div title={'main-success'} className={'m-2 h-[100px] w-[100px] bg-function-success'}></div> - </div> - <h2 className={'mb-4'}>Tint</h2> - <div className={'mb-8 flex flex-wrap items-center text-text-title'}> - <div title={'tint-1'} className={'m-2 h-[100px] w-[100px] bg-tint-pink'}></div> - <div title={'tint-2'} className={'m-2 h-[100px] w-[100px] bg-tint-purple'}></div> - <div title={'tint-3'} className={'m-2 h-[100px] w-[100px] bg-tint-red'}></div> - <div title={'tint-4'} className={'m-2 h-[100px] w-[100px] bg-tint-green'}></div> - <div title={'tint-5'} className={'m-2 h-[100px] w-[100px] bg-tint-blue'}></div> - <div title={'tint-6'} className={'m-2 h-[100px] w-[100px] bg-tint-yellow'}></div> - <div title={'tint-7'} className={'m-2 h-[100px] w-[100px] bg-tint-aqua'}></div> - <div title={'tint-8'} className={'m-2 h-[100px] w-[100px] bg-tint-lime'}></div> - <div title={'tint-9'} className={'m-2 h-[100px] w-[100px] bg-tint-pink'}></div> - </div> - <h2 className={'mb-4'}>Shades</h2> - <div className={'mb-8 flex flex-wrap items-center'}> - <div title={'shade-1'} className={'bg-shade-1 m-2 h-[100px] w-[100px]'}></div> - <div title={'shade-2'} className={'bg-shade-2 m-2 h-[100px] w-[100px]'}></div> - <div title={'shade-3'} className={'bg-shade-3 m-2 h-[100px] w-[100px]'}></div> - <div title={'shade-4'} className={'bg-shade-4 m-2 h-[100px] w-[100px]'}></div> - <div title={'shade-5'} className={'bg-shade-5 m-2 h-[100px] w-[100px]'}></div> - <div title={'line-divider'} className={'m-2 h-[100px] w-[100px] bg-line-divider'}></div> - </div> - <h2 className={'mb-4'}>Surface</h2> - <div className={'mb-8 flex flex-wrap items-center'}> - <div title={'surface-1'} className={'bg-surface-1 m-2 h-[100px] w-[100px]'}></div> - <div title={'surface-2'} className={'bg-surface-2 m-2 h-[100px] w-[100px]'}></div> - <div title={'surface-3'} className={'bg-surface-3 m-2 h-[100px] w-[100px]'}></div> - <div title={'surface-4'} className={'bg-surface-4 m-2 h-[100px] w-[100px]'}></div> - </div> - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts deleted file mode 100644 index 4060051597..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts +++ /dev/null @@ -1,237 +0,0 @@ -import { FieldType, SingleSelectTypeOptionPB, ViewLayoutPB, ViewPB, WorkspaceSettingPB } from '@/services/backend'; -import { DatabaseController } from '$app/stores/effects/database/database_controller'; -import { RowInfo } from '$app/stores/effects/database/row/row_cache'; -import { RowController } from '$app/stores/effects/database/row/row_controller'; -import { - CellControllerBuilder, - CheckboxCellController, - DateCellController, - SelectOptionCellController, - TextCellController, - URLCellController, -} from '$app/stores/effects/database/cell/controller_builder'; -import { None, Option, Some } from 'ts-results'; -import { DatabaseBackendService } from '$app/stores/effects/database/database_bd_svc'; -import { FieldInfo } from '$app/stores/effects/database/field/field_controller'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { makeSingleSelectTypeOptionContext } from '$app/stores/effects/database/field/type_option/type_option_context'; -import { SelectOptionBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { WorkspaceController } from '$app/stores/effects/workspace/workspace_controller'; -import { FolderEventGetCurrentWorkspaceSetting } from '@/services/backend/events/flowy-folder'; - -// Create a database page for specific layout type -// Do not use it production code. Just for testing -export async function createTestDatabaseView(layout: ViewLayoutPB): Promise<ViewPB> { - const workspaceSetting: WorkspaceSettingPB = await FolderEventGetCurrentWorkspaceSetting().then((result) => - result.unwrap() - ); - const wsSvc = new WorkspaceController(workspaceSetting.workspace_id); - - return await wsSvc.createView({ name: 'New Grid', layout }); -} - -export async function openTestDatabase(viewId: string): Promise<DatabaseController> { - return new DatabaseController(viewId); -} - -export async function assertTextCell( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController, - expectedContent: string -) { - const cellController = await makeTextCellController(fieldId, rowInfo, databaseController).then((result) => - result.unwrap() - ); - - cellController.subscribeChanged({ - onCellChanged: (value) => { - const cellContent = value.unwrap(); - - if (cellContent !== expectedContent) { - throw Error('Text cell content is not match'); - } - }, - }); - await cellController.getCellData(); -} - -export async function editTextCell( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController, - content: string -) { - const cellController = await makeTextCellController(fieldId, rowInfo, databaseController).then((result) => - result.unwrap() - ); - - await cellController.saveCellData(content); -} - -export async function makeTextCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<TextCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.RichText, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as TextCellController); -} - -export async function makeSingleSelectCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<SelectOptionCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.SingleSelect, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as SelectOptionCellController); -} - -export async function makeMultiSelectCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<SelectOptionCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.MultiSelect, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as SelectOptionCellController); -} - -export async function makeDateCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<DateCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.DateTime, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as DateCellController); -} - -export async function makeCheckboxCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<CheckboxCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.Checkbox, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as CheckboxCellController); -} - -export async function makeURLCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<URLCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.DateTime, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as URLCellController); -} - -export async function makeCellControllerBuilder( - fieldId: string, - rowInfo: RowInfo, - _fieldType: FieldType, - databaseController: DatabaseController -): Promise<Option<CellControllerBuilder>> { - const rowCache = databaseController.databaseViewCache.getRowCache(); - const cellCache = rowCache.getCellCache(); - const fieldController = databaseController.fieldController; - const rowController = new RowController(rowInfo, fieldController, rowCache); - const cellByFieldId = await rowController.loadCells(); - - for (const cellIdentifier of cellByFieldId.values()) { - const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController); - - if (cellIdentifier.fieldId === fieldId) { - return Some(builder); - } - } - - return None; -} - -export function findFirstFieldInfoWithFieldType(rowInfo: RowInfo, fieldType: FieldType) { - const fieldInfo = rowInfo.fieldInfos.find((element) => element.field.field_type === fieldType); - - if (fieldInfo === undefined) { - return None; - } else { - return Some(fieldInfo); - } -} - -export async function assertFieldName(controller: TypeOptionController, expected: string) { - const fieldInfo = controller.getFieldInfo(); - - if (fieldInfo.field.name !== expected) { - throw Error('Expect field name:' + expected + 'but receive:' + fieldInfo.field.name); - } -} - -export async function assertNumberOfFields(viewId: string, expected: number) { - const svc = new DatabaseBackendService(viewId); - const databasePB = await svc.openDatabase().then((result) => result.unwrap()); - - if (databasePB.fields.length !== expected) { - throw Error('Expect number of fields:' + expected + 'but receive:' + databasePB.fields.length); - } -} - -export async function assertNumberOfRows(viewId: string, expected: number) { - const svc = new DatabaseBackendService(viewId); - const databasePB = await svc.openDatabase().then((result) => result.unwrap()); - - if (databasePB.rows.length !== expected) { - throw Error('Expect number of rows:' + expected + 'but receive:' + databasePB.rows.length); - } -} - -export async function assertNumberOfRowsInGroup(viewId: string, groupId: string, expected: number) { - const svc = new DatabaseBackendService(viewId); - - await svc.openDatabase(); - - const group = await svc.getGroup(groupId).then((result) => result.unwrap()); - - if (group.rows.length !== expected) { - throw Error('Expect number of rows in group:' + expected + 'but receive:' + group.rows.length); - } -} - -export async function createSingleSelectOptions(viewId: string, fieldInfo: FieldInfo, optionNames: string[]) { - assert(fieldInfo.field.field_type === FieldType.SingleSelect, 'Only work on single select'); - const typeOptionController = new TypeOptionController(viewId, Some(fieldInfo)); - const singleSelectTypeOptionContext = makeSingleSelectTypeOptionContext(typeOptionController); - const singleSelectTypeOptionPB: SingleSelectTypeOptionPB = singleSelectTypeOptionContext.getTypeOption(); - - const backendSvc = new SelectOptionBackendService(viewId, fieldInfo.field.id); - - for (const optionName of optionNames) { - const option = await backendSvc.createOption({ name: optionName }).then((result) => result.unwrap()); - - singleSelectTypeOptionPB.options.splice(0, 0, option); - } - - await singleSelectTypeOptionContext.setTypeOption(singleSelectTypeOptionPB); - return singleSelectTypeOptionContext; -} - -export function assert(condition: boolean, msg?: string) { - if (!condition) { - throw Error(msg); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts deleted file mode 100644 index 895e28eee7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DocumentTestHelper.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ViewLayoutPB, WorkspaceSettingPB } from '@/services/backend'; -import { WorkspaceController } from '../../stores/effects/workspace/workspace_controller'; -import { FolderEventGetCurrentWorkspaceSetting } from '@/services/backend/events/flowy-folder'; - -export async function createTestDocument() { - const workspaceSetting: WorkspaceSettingPB = await FolderEventGetCurrentWorkspaceSetting().then((result) => - result.unwrap() - ); - const appService = new WorkspaceController(workspaceSetting.workspace_id); - const result = await appService.createView({ name: 'New Document', layout: ViewLayoutPB.Document }); - - return result; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx deleted file mode 100644 index 46c41a65e9..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import { - RunAllGridTests, - TestCreateGrid, - TestCreateNewField, - TestCreateRow, - TestCreateSelectOptionInCell, - TestDeleteField, - TestDeleteRow, - TestEditCell, - TestEditCheckboxCell, - TestEditDateCell, - TestEditDateFormat, - TestEditField, - TestEditNumberFormat, - TestEditTextCell, - TestEditURLCell, - TestGetSingleSelectFieldData, - TestMoveField, - TestSwitchFromMultiSelectToText, - TestSwitchFromSingleSelectToNumber, -} from './TestGrid'; -import { - TestCreateKanbanBoard, - TestCreateKanbanBoardColumn, - TestCreateKanbanBoardRowInNoStatusGroup, - TestAllKanbanTests, - TestMoveKanbanBoardColumn, - TestMoveKanbanBoardRow, -} from './TestGroup'; -import { TestCreateViews } from '$app/components/tests/TestFolder'; - -export const TestAPI = () => { - return ( - <React.Fragment> - <ul className='m-6, space-y-2'> - {/*<tests></tests>*/} - <RunAllGridTests></RunAllGridTests> - <TestCreateGrid></TestCreateGrid> - <TestCreateRow></TestCreateRow> - <TestDeleteRow></TestDeleteRow> - <TestEditCell></TestEditCell> - <TestEditTextCell></TestEditTextCell> - <TestEditURLCell></TestEditURLCell> - <TestEditDateCell></TestEditDateCell> - <TestEditCheckboxCell></TestEditCheckboxCell> - <TestCreateSelectOptionInCell></TestCreateSelectOptionInCell> - <TestGetSingleSelectFieldData></TestGetSingleSelectFieldData> - <TestEditField></TestEditField> - <TestMoveField></TestMoveField> - <TestCreateNewField></TestCreateNewField> - <TestDeleteField></TestDeleteField> - <TestEditDateFormat></TestEditDateFormat> - <TestEditNumberFormat></TestEditNumberFormat> - <TestSwitchFromSingleSelectToNumber></TestSwitchFromSingleSelectToNumber> - <TestSwitchFromMultiSelectToText></TestSwitchFromMultiSelectToText> - {/*kanban board */} - <TestAllKanbanTests></TestAllKanbanTests> - <TestCreateKanbanBoard></TestCreateKanbanBoard> - <TestCreateKanbanBoardRowInNoStatusGroup></TestCreateKanbanBoardRowInNoStatusGroup> - <TestMoveKanbanBoardRow></TestMoveKanbanBoardRow> - <TestMoveKanbanBoardColumn></TestMoveKanbanBoardColumn> - <TestCreateKanbanBoardColumn></TestCreateKanbanBoardColumn> - {/*Folders*/} - <TestCreateViews></TestCreateViews> - </ul> - </React.Fragment> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx deleted file mode 100644 index 6e24aae8a1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react'; -import { UserBackendService } from '$app/stores/effects/user/user_bd_svc'; -import { useAppSelector } from '$app/stores/store'; -import { WorkspaceController } from '../../stores/effects/workspace/workspace_controller'; -import { ViewLayoutPB, ViewPB } from '@/services/backend'; - -const testCreateFolder = async (userId?: number) => { - if (!userId) { - console.log('user is not logged in'); - return; - } - - console.log('test create views'); - const userBackendService: UserBackendService = new UserBackendService(userId); - const workspaces = await userBackendService.getWorkspaces(); - - if (workspaces.ok) { - console.log('workspaces: ', workspaces.val.toObject()); - } - - const currentWorkspaceSetting = await userBackendService.getCurrentWorkspaceSetting(); - const workspaceService = new WorkspaceController(currentWorkspaceSetting.workspace_id); - const rootViews: ViewPB[] = []; - - for (let i = 1; i <= 3; i++) { - const result = await workspaceService.createView({ - name: `test board ${i}`, - desc: 'test description', - layout: ViewLayoutPB.Board, - }); - - rootViews.push(result); - } - - for (let i = 1; i <= 3; i++) { - await workspaceService.createView({ - name: `test board 1 ${i}`, - desc: 'test description', - layout: ViewLayoutPB.Board, - parent_view_id: rootViews[0].id, - }); - } - - const allApps = await workspaceService.getChildPages(); - - console.log(allApps); -}; - -export const TestCreateViews = () => { - const currentUser = useAppSelector((state) => state.currentUser); - - return TestButton('Test create views', testCreateFolder, currentUser.id); -}; - -const TestButton = (title: string, onClick: (userId?: number) => void, userId?: number) => { - return ( - <React.Fragment> - <div> - <button className='rounded-md bg-pink-200 p-4' type='button' onClick={() => onClick(userId)}> - {title} - </button> - </div> - </React.Fragment> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx deleted file mode 100644 index 04e4c566a6..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { ChangeEvent, useState } from 'react'; - -const TestFonts = () => { - const [sampleText, setSampleText] = useState('Sample Text'); - - const onInputChange = (e: ChangeEvent<HTMLInputElement>) => { - setSampleText(e.target.value); - }; - - return ( - <div className={'flex h-full w-full flex-col items-center justify-center'}> - <div className={'py-2'}> - <input className={'rounded border border-gray-500 px-2 py-1'} value={sampleText} onChange={onInputChange} /> - </div> - <div className={'flex flex-1 flex-col items-center justify-center overflow-auto text-2xl'}> - <div className={'mb-4 font-thin'}>{sampleText} 100 Thin</div> - <div className={'mb-4 font-extralight'}>{sampleText} 200 Extra Light</div> - <div className={'mb-4 font-light'}>{sampleText} 300 Light</div> - <div className={'mb-4 font-normal'}>{sampleText} 400 Regular</div> - <div className={'mb-4 font-medium'}>{sampleText} 500 Medium</div> - <div className={'mb-4 font-semibold'}>{sampleText} 600 Semi Bold</div> - <div className={'mb-4 font-bold'}>{sampleText} 700 Bold</div> - <div className={'mb-4 font-extrabold'}>{sampleText} 800 Extra Bold</div> - <div className={'mb-4 font-black'}>{sampleText} 900 Black</div> - </div> - </div> - ); -}; - -export default TestFonts; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx deleted file mode 100644 index 9aaaaf1453..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx +++ /dev/null @@ -1,608 +0,0 @@ -import React from 'react'; -import { - DateFormatPB, - FieldType, - NumberFormatPB, - NumberTypeOptionPB, - SelectOptionCellDataPB, - TimeFormatPB, - ViewLayoutPB, -} from '@/services/backend'; -import { Log } from '$app/utils/log'; -import { - assert, - assertFieldName, - assertNumberOfFields, - assertNumberOfRows, - assertTextCell, - createSingleSelectOptions, - createTestDatabaseView, - editTextCell, - findFirstFieldInfoWithFieldType, - makeCheckboxCellController, - makeDateCellController, - makeMultiSelectCellController, - makeSingleSelectCellController, - makeTextCellController, - makeURLCellController, - openTestDatabase, -} from './DatabaseTestHelper'; -import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { None, Some } from 'ts-results'; -import { - makeDateTypeOptionContext, - makeNumberTypeOptionContext, -} from '$app/stores/effects/database/field/type_option/type_option_context'; -import { CalendarData } from '$app/stores/effects/database/cell/controller_builder'; - -export const RunAllGridTests = () => { - async function run() { - await createBuildInGrid(); - await testEditGridCell(); - await testCreateRow(); - await testDeleteRow(); - await testCreateOptionInCell(); - await testGetSingleSelectFieldData(); - await testSwitchFromSingleSelectToNumber(); - await testSwitchFromMultiSelectToRichText(); - await testEditField(); - await testCreateNewField(); - await testDeleteField(); - } - - return ( - <React.Fragment> - <div> - <button className='rounded-md bg-red-400 p-4' type='button' onClick={() => run()}> - Run all grid tests - </button> - </div> - </React.Fragment> - ); -}; - -async function createBuildInGrid() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - databaseController.subscribe({ - onViewChanged: (databasePB) => { - Log.debug('Did receive database:' + databasePB); - }, - // onRowsChanged: async (rows) => { - // if (rows.length !== 3) { - // throw Error('Expected number of rows is 3, but receive ' + rows.length); - // } - // }, - onFieldsChanged: (fields) => { - if (fields.length !== 3) { - throw Error('Expected number of fields is 3, but receive ' + fields.length); - } - }, - }); - await databaseController.open().then((result) => result.unwrap()); - await databaseController.dispose(); -} - -async function testEditGridCell() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - for (const [index, row] of databaseController.databaseViewCache.rowInfos.entries()) { - const cellContent = index.toString(); - const fieldInfo = findFirstFieldInfoWithFieldType(row, FieldType.RichText).unwrap(); - - await editTextCell(fieldInfo.field.id, row, databaseController, cellContent); - await assertTextCell(fieldInfo.field.id, row, databaseController, cellContent); - } -} - -async function testEditTextCell() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - const row = databaseController.databaseViewCache.rowInfos[0]; - const textField = findFirstFieldInfoWithFieldType(row, FieldType.RichText).unwrap(); - const textCellController = await makeTextCellController(textField.field.id, row, databaseController).then((result) => - result.unwrap() - ); - - textCellController.subscribeChanged({ - onCellChanged: (content) => { - Log.info('Receive text:', content); - }, - }); - - await textCellController.saveCellData('hello react'); - await new Promise((resolve) => setTimeout(resolve, 200)); - await databaseController.dispose(); -} - -async function testEditURLCell() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - const typeOptionController = new TypeOptionController(view.id, None, FieldType.URL); - - await typeOptionController.initialize(); - - const row = databaseController.databaseViewCache.rowInfos[0]; - const urlCellController = await makeURLCellController(typeOptionController.fieldId, row, databaseController).then( - (result) => result.unwrap() - ); - - urlCellController.subscribeChanged({ - onCellChanged: (content) => { - const pb = content.unwrap(); - - Log.info('Receive url data:', pb.url, pb.content); - }, - }); - - await urlCellController.saveCellData('hello react'); - await new Promise((resolve) => setTimeout(resolve, 200)); - - await urlCellController.saveCellData('appflowy.io'); - await new Promise((resolve) => setTimeout(resolve, 200)); -} - -async function testEditDateCell() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - const typeOptionController = new TypeOptionController(view.id, None, FieldType.DateTime); - - await typeOptionController.initialize(); - - const row = databaseController.databaseViewCache.rowInfos[0]; - const dateCellController = await makeDateCellController(typeOptionController.fieldId, row, databaseController).then( - (result) => result.unwrap() - ); - - dateCellController.subscribeChanged({ - onCellChanged: (content) => { - const pb = content.unwrap(); - - Log.info('Receive date data:', pb.date, pb.time); - }, - }); - - const date = new CalendarData(new Date(), true, '13:00'); - - await dateCellController.saveCellData(date); - await new Promise((resolve) => setTimeout(resolve, 200)); -} - -async function testEditDateFormatPB() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Create date field - const typeOptionController = new TypeOptionController(view.id, None, FieldType.DateTime); - - await typeOptionController.initialize(); - - // update date type option - const dateTypeOptionContext = makeDateTypeOptionContext(typeOptionController); - const typeOption = dateTypeOptionContext.getTypeOption(); - - assert(typeOption.date_format === DateFormatPB.Friendly, 'Date format not match'); - assert(typeOption.time_format === TimeFormatPB.TwentyFourHour, 'Time format not match'); - typeOption.date_format = DateFormatPB.Local; - typeOption.time_format = TimeFormatPB.TwelveHour; - await dateTypeOptionContext.setTypeOption(typeOption); - - const typeOption2 = dateTypeOptionContext.getTypeOption(); - - assert(typeOption2.date_format === DateFormatPB.Local, 'Date format not match'); - assert(typeOption2.time_format === TimeFormatPB.TwelveHour, 'Time format not match'); - - await new Promise((resolve) => setTimeout(resolve, 200)); -} - -async function testEditNumberFormatPB() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Create date field - const typeOptionController = new TypeOptionController(view.id, None, FieldType.Number); - - await typeOptionController.initialize(); - - // update date type option - const dateTypeOptionContext = makeNumberTypeOptionContext(typeOptionController); - const typeOption = dateTypeOptionContext.getTypeOption(); - - typeOption.format = NumberFormatPB.EUR; - typeOption.name = 'Money'; - await dateTypeOptionContext.setTypeOption(typeOption); - - const typeOption2 = dateTypeOptionContext.getTypeOption(); - - Log.info(typeOption2); - await new Promise((resolve) => setTimeout(resolve, 200)); -} - -async function testCheckboxCell() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - const typeOptionController = new TypeOptionController(view.id, None, FieldType.Checkbox); - - await typeOptionController.initialize(); - - const row = databaseController.databaseViewCache.rowInfos[0]; - const checkboxCellController = await makeCheckboxCellController( - typeOptionController.fieldId, - row, - databaseController - ).then((result) => result.unwrap()); - - checkboxCellController.subscribeChanged({ - onCellChanged: (content) => { - const pb = content.unwrap(); - - Log.info('Receive checkbox data:', pb); - }, - }); - - await checkboxCellController.saveCellData('true'); - await new Promise((resolve) => setTimeout(resolve, 200)); -} - -async function testCreateRow() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - await assertNumberOfRows(view.id, 3); - - // Create a row from a DatabaseController - await databaseController.createRow(); - await assertNumberOfRows(view.id, 4); - await databaseController.dispose(); -} - -async function testDeleteRow() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - const rows = databaseController.databaseViewCache.rowInfos; - - await databaseController.deleteRow(rows[0].row.id); - await assertNumberOfRows(view.id, 2); - - // Wait the databaseViewCache get the change notification and - // update the rows. - await new Promise((resolve) => setTimeout(resolve, 200)); - if (databaseController.databaseViewCache.rowInfos.length !== 2) { - throw Error('The number of rows is not match'); - } - - await databaseController.dispose(); -} - -async function testCreateOptionInCell() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - for (const [index, row] of databaseController.databaseViewCache.rowInfos.entries()) { - if (index === 0) { - const fieldInfo = findFirstFieldInfoWithFieldType(row, FieldType.SingleSelect).unwrap(); - const cellController = await makeSingleSelectCellController(fieldInfo.field.id, row, databaseController).then( - (result) => result.unwrap() - ); - - // eslint-disable-next-line @typescript-eslint/await-thenable - await cellController.subscribeChanged({ - onCellChanged: (value) => { - if (value.some) { - const option: SelectOptionCellDataPB = value.unwrap(); - - console.log(option); - } - }, - }); - const backendSvc = new SelectOptionCellBackendService(cellController.cellIdentifier); - - await backendSvc.createOption({ name: 'option' + index }); - await cellController.dispose(); - } - } - - await databaseController.dispose(); -} - -async function testMoveField() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - const ids = databaseController.fieldController.fieldInfos.map((value) => value.field.id); - - Log.info('Receive fields data:', ids); - - databaseController.subscribe({ - onFieldsChanged: (values) => { - const new_ids = values.map((value) => value.field.id); - - Log.info('Receive fields data:', new_ids); - }, - }); - - const fieldInfos = [...databaseController.fieldController.fieldInfos]; - const fromFieldId = fieldInfos[0].field.id; - const toFieldId = fieldInfos[1].field.id; - - await databaseController.moveField({ fromFieldId: fromFieldId, toFieldId: toFieldId }); - await new Promise((resolve) => setTimeout(resolve, 200)); - assert(databaseController.fieldController.fieldInfos[1].field.id === fromFieldId); -} - -async function testGetSingleSelectFieldData() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Find the single select column - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const singleSelect = databaseController.fieldController.fieldInfos.find( - (fieldInfo) => fieldInfo.field.field_type === FieldType.SingleSelect - )!; - - // Create options - const singleSelectTypeOptionContext = await createSingleSelectOptions(view.id, singleSelect, [ - 'Task 1', - 'Task 2', - 'Task 3', - ]); - - // Read options - const options = singleSelectTypeOptionContext.getTypeOption(); - - console.log(options); - - await databaseController.dispose(); -} - -async function testSwitchFromSingleSelectToNumber() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Find the single select column - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const singleSelect = databaseController.fieldController.fieldInfos.find( - (fieldInfo) => fieldInfo.field.field_type === FieldType.SingleSelect - )!; - const typeOptionController = new TypeOptionController(view.id, Some(singleSelect)); - - await typeOptionController.switchToField(FieldType.Number); - - // Check the number type option - const numberTypeOptionContext = makeNumberTypeOptionContext(typeOptionController); - const numberTypeOption: NumberTypeOptionPB = numberTypeOptionContext - .getTypeOption(); - const format: NumberFormatPB = numberTypeOption.format; - - if (format !== NumberFormatPB.Num) { - throw Error('The default format should be number'); - } - - await databaseController.dispose(); -} - -async function testSwitchFromMultiSelectToRichText() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Create multi-select field - const typeOptionController = new TypeOptionController(view.id, None, FieldType.MultiSelect); - - await typeOptionController.initialize(); - - // Insert options to first row - const row = databaseController.databaseViewCache.rowInfos[0]; - const multiSelectField = typeOptionController.getFieldInfo(); - // const multiSelectField = findFirstFieldInfoWithFieldType(row, FieldType.MultiSelect).unwrap(); - const selectOptionCellController = await makeMultiSelectCellController( - multiSelectField.field.id, - row, - databaseController - ).then((result) => result.unwrap()); - const backendSvc = new SelectOptionCellBackendService(selectOptionCellController.cellIdentifier); - - await backendSvc.createOption({ name: 'A' }); - await backendSvc.createOption({ name: 'B' }); - await backendSvc.createOption({ name: 'C' }); - - const selectOptionCellData = await selectOptionCellController.getCellData().then((result) => result.unwrap()); - - if (selectOptionCellData.options.length !== 3) { - throw Error('The options should equal to 3'); - } - - if (selectOptionCellData.select_options.length !== 3) { - throw Error('The selected options should equal to 3'); - } - - await selectOptionCellController.dispose(); - - // Switch to RichText field type - await typeOptionController.switchToField(FieldType.RichText); - if (typeOptionController.fieldType !== FieldType.RichText) { - throw Error('The field type should be text'); - } - - const textCellController = await makeTextCellController(multiSelectField.field.id, row, databaseController).then( - (result) => result.unwrap() - ); - const cellContent = await textCellController.getCellData(); - - if (cellContent.unwrap() !== 'A,B,C') { - throw Error('The cell content should be A,B,C, but receive: ' + cellContent.unwrap()); - } - - await databaseController.dispose(); -} - -async function testEditField() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - const fieldInfos = databaseController.fieldController.fieldInfos; - - // Modify the name of the field - const firstFieldInfo = fieldInfos[0]; - const controller = new TypeOptionController(view.id, Some(firstFieldInfo)); - - await controller.initialize(); - const newName = 'hello world'; - - await controller.setFieldName(newName); - - await new Promise((resolve) => setTimeout(resolve, 200)); - await assertFieldName(controller, newName); - await databaseController.dispose(); -} - -async function testCreateNewField() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - await assertNumberOfFields(view.id, 3); - - // Modify the name of the field - const controller = new TypeOptionController(view.id, None); - - await controller.initialize(); - await assertNumberOfFields(view.id, 4); - await databaseController.dispose(); -} - -async function testDeleteField() { - const view = await createTestDatabaseView(ViewLayoutPB.Grid); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Modify the name of the field. - // The fieldInfos[0] is the primary field by default, we can't delete it. - // So let choose the second fieldInfo. - const fieldInfo = databaseController.fieldController.fieldInfos[1]; - const controller = new TypeOptionController(view.id, Some(fieldInfo)); - - await controller.initialize(); - await assertNumberOfFields(view.id, 3); - await controller.deleteField(); - await assertNumberOfFields(view.id, 2); - await databaseController.dispose(); -} - -export const TestCreateGrid = () => { - return TestButton('Test create build-in grid', createBuildInGrid); -}; - -export const TestEditCell = () => { - return TestButton('Test editing cell', testEditGridCell); -}; - -export const TestEditTextCell = () => { - return TestButton('Test editing text cell', testEditTextCell); -}; - -export const TestEditURLCell = () => { - return TestButton('Test editing URL cell', testEditURLCell); -}; - -export const TestEditDateCell = () => { - return TestButton('Test editing date cell', testEditDateCell); -}; - -export const TestEditDateFormat = () => { - return TestButton('Test editing date format', testEditDateFormatPB); -}; - -export const TestEditNumberFormat = () => { - return TestButton('Test editing number format', testEditNumberFormatPB); -}; - -export const TestEditCheckboxCell = () => { - return TestButton('Test editing checkbox cell', testCheckboxCell); -}; - -export const TestCreateRow = () => { - return TestButton('Test create row', testCreateRow); -}; - -export const TestDeleteRow = () => { - return TestButton('Test delete row', testDeleteRow); -}; - -export const TestCreateSelectOptionInCell = () => { - return TestButton('Test create a select option in cell', testCreateOptionInCell); -}; - -export const TestGetSingleSelectFieldData = () => { - return TestButton('Test get single-select column data', testGetSingleSelectFieldData); -}; - -export const TestSwitchFromSingleSelectToNumber = () => { - return TestButton('Test switch from single-select to number column', testSwitchFromSingleSelectToNumber); -}; - -export const TestSwitchFromMultiSelectToText = () => { - return TestButton('Test switch from multi-select to text column', testSwitchFromMultiSelectToRichText); -}; - -export const TestMoveField = () => { - return TestButton('Test move field', testMoveField); -}; - -export const TestEditField = () => { - return TestButton('Test edit the column name', testEditField); -}; - -export const TestCreateNewField = () => { - return TestButton('Test create a new column', testCreateNewField); -}; - -export const TestDeleteField = () => { - return TestButton('Test delete a new column', testDeleteField); -}; - -export const TestButton = (title: string, onClick: () => void) => { - return ( - <React.Fragment> - <div> - <button className='rounded-md bg-blue-400 p-4' type='button' onClick={() => onClick()}> - {title} - </button> - </div> - </React.Fragment> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx deleted file mode 100644 index c161eeeeb1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import { - assert, - assertNumberOfRowsInGroup, - createSingleSelectOptions, - createTestDatabaseView, - openTestDatabase, -} from './DatabaseTestHelper'; -import { FieldType, ViewLayoutPB } from '../../../services/backend'; -import React from 'react'; - -export const TestAllKanbanTests = () => { - async function run() { - await createBuildInBoard(); - await createKanbanBoardRow(); - await moveKanbanBoardRow(); - await createKanbanBoardColumn(); - await createColumnInBoard(); - } - - return ( - <React.Fragment> - <div> - <button className='rounded-md bg-red-400 p-4' type='button' onClick={() => run()}> - Run all kanban board tests - </button> - </div> - </React.Fragment> - ); -}; - -async function createBuildInBoard() { - const view = await createTestDatabaseView(ViewLayoutPB.Board); - const databaseController = await openTestDatabase(view.id); - - databaseController.subscribe({ - onGroupByField: (groups) => { - console.log(groups); - if (groups.length !== 4) { - throw Error('The build-in board should have 4 groups'); - } - - assert(groups[0].rows.length === 0, 'The no status group should have 0 rows'); - assert(groups[1].rows.length === 3, 'The first group should have 3 rows'); - assert(groups[2].rows.length === 0, 'The second group should have 0 rows'); - assert(groups[3].rows.length === 0, 'The third group should have 0 rows'); - }, - }); - await databaseController.open().then((result) => result.unwrap()); - await databaseController.dispose(); -} - -async function createKanbanBoardRow() { - const view = await createTestDatabaseView(ViewLayoutPB.Board); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Create row in no status group - const noStatusGroup = databaseController.groups.getValue()[0]; - - await noStatusGroup.createRow().then((result) => result.unwrap()); - await assertNumberOfRowsInGroup(view.id, noStatusGroup.groupId, 1); - - await databaseController.dispose(); -} - -async function moveKanbanBoardRow() { - const view = await createTestDatabaseView(ViewLayoutPB.Board); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Create row in no status group - const firstGroup = databaseController.groups.getValue()[1]; - const secondGroup = databaseController.groups.getValue()[2]; - - // subscribe the group changes - firstGroup.subscribe({ - onRemoveRow: (groupId, deleteRowId) => { - console.log(groupId + 'did remove:' + deleteRowId); - }, - onInsertRow: (groupId, rowPB) => { - console.log(groupId + 'did insert:' + rowPB.id); - }, - onUpdateRow: (groupId, rowPB) => { - console.log(groupId + 'did update:' + rowPB.id); - }, - onCreateRow: (groupId, rowPB) => { - console.log(groupId + 'did create:' + rowPB.id); - }, - }); - - secondGroup.subscribe({ - onRemoveRow: (groupId, deleteRowId) => { - console.log(groupId + 'did remove:' + deleteRowId); - }, - onInsertRow: (groupId, rowPB) => { - console.log(groupId + 'did insert:' + rowPB.id); - }, - onUpdateRow: (groupId, rowPB) => { - console.log(groupId + 'did update:' + rowPB.id); - }, - onCreateRow: (groupId, rowPB) => { - console.log(groupId + 'did create:' + rowPB.id); - }, - }); - - const row = firstGroup.rowAtIndex(0).unwrap(); - - await databaseController.moveGroupRow(row.id, secondGroup.groupId); - - assert(firstGroup.rows.length === 2); - await assertNumberOfRowsInGroup(view.id, firstGroup.groupId, 2); - - assert(secondGroup.rows.length === 1); - await assertNumberOfRowsInGroup(view.id, secondGroup.groupId, 1); - - await databaseController.dispose(); -} - -async function createKanbanBoardColumn() { - const view = await createTestDatabaseView(ViewLayoutPB.Board); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // Create row in no status group - const firstGroup = databaseController.groups.getValue()[1]; - const secondGroup = databaseController.groups.getValue()[2]; - - await databaseController.moveGroup(firstGroup.groupId, secondGroup.groupId); - - assert(databaseController.groups.getValue()[1].groupId === secondGroup.groupId); - assert(databaseController.groups.getValue()[2].groupId === firstGroup.groupId); - await databaseController.dispose(); -} - -async function createColumnInBoard() { - const view = await createTestDatabaseView(ViewLayoutPB.Board); - const databaseController = await openTestDatabase(view.id); - - await databaseController.open().then((result) => result.unwrap()); - - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const singleSelect = databaseController.fieldController.fieldInfos.find( - (fieldInfo) => fieldInfo.field.field_type === FieldType.SingleSelect - )!; - - // Create a option which will cause creating a new group - const name = 'New column'; - - await createSingleSelectOptions(view.id, singleSelect, [name]); - - // Wait the backend posting the notification to update the groups - await new Promise((resolve) => setTimeout(resolve, 200)); - assert(databaseController.groups.value.length === 5, 'expect number of groups is 5'); - assert(databaseController.groups.value[4].name === name, 'expect the last group name is ' + name); - await databaseController.dispose(); -} - -export const TestCreateKanbanBoard = () => { - return TestButton('Test create build-in board', createBuildInBoard); -}; - -export const TestCreateKanbanBoardRowInNoStatusGroup = () => { - return TestButton('Test create row in build-in kanban board', createKanbanBoardRow); -}; - -export const TestMoveKanbanBoardRow = () => { - return TestButton('Test move row in build-in kanban board', moveKanbanBoardRow); -}; - -export const TestMoveKanbanBoardColumn = () => { - return TestButton('Test move column in build-in kanban board', createKanbanBoardColumn); -}; - -export const TestCreateKanbanBoardColumn = () => { - return TestButton('Test create column in build-in kanban board', createColumnInBoard); -}; - -export const TestButton = (title: string, onClick: () => void) => { - return ( - <React.Fragment> - <div> - <button className='rounded-md bg-yellow-200 p-4' type='button' onClick={() => onClick()}> - {title} - </button> - </div> - </React.Fragment> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts index 9948a2b189..e98a846da0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.hooks.ts @@ -1,41 +1,35 @@ -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { TrashController } from '$app/stores/effects/workspace/trash/controller'; +import { useCallback, useEffect, useState } from 'react'; import { useAppDispatch, useAppSelector } from '@/appflowy_app/stores/store'; import { trashActions, trashPBToTrash } from '$app_reducers/trash/slice'; +import { subscribeNotifications } from '$app/application/notification'; +import { FolderNotification } from '@/services/backend'; +import { deleteTrashItem, getTrash, putback, deleteAll, restoreAll } from '$app/application/folder/trash.service'; export function useLoadTrash() { const trash = useAppSelector((state) => state.trash.list); const dispatch = useAppDispatch(); - const controller = useMemo(() => { - return new TrashController(); - }, []); const initializeTrash = useCallback(async () => { - const trash = await controller.getTrash(); + const trash = await getTrash(); dispatch(trashActions.initTrash(trash.map(trashPBToTrash))); - }, [controller, dispatch]); + }, [dispatch]); - const subscribeToTrash = useCallback(async () => { - await controller.subscribe({ - onTrashChanged: (trash) => { - dispatch(trashActions.onTrashChanged(trash.map(trashPBToTrash))); + useEffect(() => { + void initializeTrash(); + }, [initializeTrash]); + + useEffect(() => { + const unsubscribePromise = subscribeNotifications({ + [FolderNotification.DidUpdateTrash]: async (changeset) => { + dispatch(trashActions.onTrashChanged(changeset.items.map(trashPBToTrash))); }, }); - }, [controller, dispatch]); - useEffect(() => { - void (async () => { - await initializeTrash(); - await subscribeToTrash(); - })(); - }, [initializeTrash, subscribeToTrash]); - - useEffect(() => { return () => { - void controller.dispose(); + void unsubscribePromise.then((fn) => fn()); }; - }, [controller]); + }, [dispatch]); return { trash, @@ -46,16 +40,6 @@ export function useTrashActions() { const [restoreAllDialogOpen, setRestoreAllDialogOpen] = useState(false); const [deleteAllDialogOpen, setDeleteAllDialogOpen] = useState(false); - const controller = useMemo(() => { - return new TrashController(); - }, []); - - useEffect(() => { - return () => { - void controller.dispose(); - }; - }, [controller]); - const onClickRestoreAll = () => { setRestoreAllDialogOpen(true); }; @@ -70,18 +54,10 @@ export function useTrashActions() { }; return { - onPutback: async (id: string) => { - await controller.putback(id); - }, - onDelete: async (ids: string[]) => { - await controller.delete(ids); - }, - onDeleteAll: async () => { - await controller.deleteAll(); - }, - onRestoreAll: async () => { - await controller.restoreAll(); - }, + onPutback: putback, + onDelete: deleteTrashItem, + onDeleteAll: deleteAll, + onRestoreAll: restoreAll, onClickRestoreAll, onClickDeleteAll, restoreAllDialogOpen, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx index 6387437162..6f0a0f94f6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx @@ -5,7 +5,7 @@ import { DeleteOutline, RestoreOutlined } from '@mui/icons-material'; import { useLoadTrash, useTrashActions } from '$app/components/trash/Trash.hooks'; import { Divider, List } from '@mui/material'; import TrashItem from '$app/components/trash/TrashItem'; -import ConfirmDialog from '$app/components/_shared/app-dialog/ConfirmDialog'; +import DeleteConfirmDialog from '$app/components/_shared/delete_confirm_dialog/DeleteConfirmDialog'; function Trash() { const { t } = useTranslation(); @@ -57,14 +57,14 @@ function Trash() { /> ))} </List> - <ConfirmDialog + <DeleteConfirmDialog open={restoreAllDialogOpen} title={t('trash.confirmRestoreAll.title')} subtitle={t('trash.confirmRestoreAll.caption')} onOk={onRestoreAll} onClose={closeDialog} /> - <ConfirmDialog + <DeleteConfirmDialog open={deleteAllDialogOpen} title={t('trash.confirmDeleteAll.title')} subtitle={t('trash.confirmDeleteAll.caption')} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/index.ts deleted file mode 100644 index 7c5757b01c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './user_listener'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/parser.ts b/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/parser.ts deleted file mode 100644 index 866d910e83..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/parser.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { FlowyError, UserNotification } from '@/services/backend'; -import { NotificationParser, OnNotificationError } from '@/services/backend/notifications'; -import { Result } from 'ts-results'; - -declare type UserNotificationCallback = (ty: UserNotification, payload: Result<Uint8Array, FlowyError>) => void; - -export class UserNotificationParser extends NotificationParser<UserNotification> { - constructor(params: { id?: string; callback: UserNotificationCallback; onError?: OnNotificationError }) { - super( - params.callback, - (ty) => { - const notification = UserNotification[ty]; - - if (isUserNotification(notification)) { - return UserNotification[notification]; - } else { - return UserNotification.Unknown; - } - }, - params.id - ); - } -} - -const isUserNotification = (notification: string): notification is keyof typeof UserNotification => { - return Object.values(UserNotification).indexOf(notification) !== -1; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/user_listener.ts b/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/user_listener.ts deleted file mode 100644 index 112ee9b6a2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/user/application/notifications/user_listener.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { FlowyError, UserNotification, UserProfilePB } from '@/services/backend'; -import { AFNotificationObserver, OnNotificationError } from '@/services/backend/notifications'; -import { UserNotificationParser } from './parser'; -import { Ok, Result } from 'ts-results'; - -declare type OnUserProfileUpdate = (result: Result<UserProfilePB, FlowyError>) => void; - -export class UserNotificationListener extends AFNotificationObserver<UserNotification> { - onProfileUpdate?: OnUserProfileUpdate; - - constructor(params: { userId?: string; onProfileUpdate?: OnUserProfileUpdate; onError?: OnNotificationError }) { - const parser = new UserNotificationParser({ - callback: (notification, result) => { - switch (notification) { - case UserNotification.DidUpdateUserProfile: - if (result.ok) { - this.onProfileUpdate?.(Ok(UserProfilePB.deserializeBinary(result.val))); - } else { - this.onProfileUpdate?.(result); - } - - break; - default: - break; - } - }, - id: params.userId, - onError: params.onError, - }); - - super(parser); - this.onProfileUpdate = params.onProfileUpdate; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/user/presentation/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/user/presentation/index.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/user/presentation/index.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_bd_svc.ts deleted file mode 100644 index 30ec4e5812..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_bd_svc.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { DatabaseEventGetCell, DatabaseEventUpdateCell } from '@/services/backend/events/flowy-database2'; -import { CellChangesetPB, CellIdPB, FieldType } from '@/services/backend'; - -class CellIdentifier { - constructor( - public readonly viewId: string, - public readonly rowId: string, - public readonly fieldId: string, - public readonly fieldType: FieldType - ) {} -} - -class CellBackendService { - static updateCell = async (cellId: CellIdentifier, data: string) => { - const payload = CellChangesetPB.fromObject({ - view_id: cellId.viewId, - field_id: cellId.fieldId, - row_id: cellId.rowId, - cell_changeset: data, - }); - - return DatabaseEventUpdateCell(payload); - }; - - getCell = async (cellId: CellIdentifier) => { - const payload = CellIdPB.fromObject({ - view_id: cellId.viewId, - field_id: cellId.fieldId, - row_id: cellId.rowId, - }); - - return DatabaseEventGetCell(payload); - }; -} - -export { CellBackendService, CellIdentifier }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts deleted file mode 100644 index d81415660c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_cache.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { None, Option, Some } from 'ts-results'; - -export class CellCacheKey { - constructor(public readonly fieldId: string, public readonly rowId: string) {} -} - -type CellDataByRowId = Map<string, any>; - -export class CellCache { - private cellDataByFieldId = new Map<string, CellDataByRowId>(); - - constructor(public readonly databaseId: string) {} - - remove = (key: CellCacheKey) => { - const cellDataByRowId = this.cellDataByFieldId.get(key.fieldId); - - if (cellDataByRowId !== undefined) { - cellDataByRowId.delete(key.rowId); - } - }; - - removeWithFieldId = (fieldId: string) => { - this.cellDataByFieldId.delete(fieldId); - }; - - insert = (key: CellCacheKey, value: any) => { - const cellDataByRowId = this.cellDataByFieldId.get(key.fieldId); - - if (cellDataByRowId === undefined) { - const map = new Map(); - - map.set(key.rowId, value); - this.cellDataByFieldId.set(key.fieldId, map); - } else { - cellDataByRowId.set(key.rowId, value); - } - }; - - get<T>(key: CellCacheKey): Option<T> { - const cellDataByRowId = this.cellDataByFieldId.get(key.fieldId); - - if (cellDataByRowId === undefined) { - return None; - } else { - const value = cellDataByRowId.get(key.rowId); - - if (typeof value === typeof undefined) { - return None; - } - - // if (value satisfies T) { - // return Some(value as T); - // } - return Some(value); - } - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts deleted file mode 100644 index 2f1c164313..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { CellIdentifier } from './cell_bd_svc'; -import { CellCache, CellCacheKey } from './cell_cache'; -import { CellDataLoader } from './data_parser'; -import { CellDataPersistence } from './data_persistence'; -import { FieldBackendService } from '../field/field_bd_svc'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { CellObserver } from './cell_observer'; -import { Log } from '$app/utils/log'; -import { None, Option, Some } from 'ts-results'; -import { DatabaseFieldObserver } from '../field/field_observer'; - -type Callbacks<T> = { onCellChanged: (value: Option<T>) => void; onFieldChanged?: () => void }; - -export class CellController<T, D> { - private fieldBackendService: FieldBackendService; - private cellDataNotifier: CellDataNotifier<T>; - private cellObserver: CellObserver; - private readonly cacheKey: CellCacheKey; - private readonly fieldNotifier: DatabaseFieldObserver; - private subscribeCallbacks?: Callbacks<T>; - - constructor( - public readonly cellIdentifier: CellIdentifier, - private readonly cellCache: CellCache, - private readonly cellDataLoader: CellDataLoader<T>, - private readonly cellDataPersistence: CellDataPersistence<D> - ) { - this.fieldBackendService = new FieldBackendService(cellIdentifier.viewId, cellIdentifier.fieldId); - this.cacheKey = new CellCacheKey(cellIdentifier.fieldId, cellIdentifier.rowId); - this.cellDataNotifier = new CellDataNotifier(cellCache.get<T>(this.cacheKey)); - this.cellObserver = new CellObserver(cellIdentifier.rowId, cellIdentifier.fieldId); - this.fieldNotifier = new DatabaseFieldObserver(cellIdentifier.fieldId); - - void this.cellObserver.subscribe({ - /// 1.Listen on user edit event and load the new cell data if needed. - /// For example: - /// user input: 12 - /// cell display: $12 - onCellChanged: async () => { - this.cellCache.remove(this.cacheKey); - try { - await this._loadCellData(); - } catch (e) { - Log.error(e); - } - }, - }); - - /// 2.Listen on the field event and load the cell data if needed. - void this.fieldNotifier.subscribe({ - onFieldChanged: async () => { - /// reloadOnFieldChanged should be true if you need to load the data when the corresponding field is changed - /// For example: - /// ï¿¥12 -> $12 - if (this.cellDataLoader.reloadOnFieldChanged) { - await this._loadCellData(); - } - - this.subscribeCallbacks?.onFieldChanged?.(); - }, - }); - } - - subscribeChanged = (callbacks: Callbacks<T>) => { - this.subscribeCallbacks = callbacks; - this.cellDataNotifier.observer?.subscribe((cellData) => { - if (cellData !== null) { - callbacks.onCellChanged(Some(cellData)); - } - }); - }; - - saveCellData = async (data: D) => { - const result = await this.cellDataPersistence.save(data); - - if (result.err) { - Log.error(result.val); - } - }; - - /// Return the cell data immediately if it exists in the cache - /// Otherwise, it will load the cell data from the backend. The - /// subscribers of the [onCellChanged] will get noticed - getCellData = async (): Promise<Option<T>> => { - const cellData = this.cellCache.get<T>(this.cacheKey); - - if (cellData.none) { - await this._loadCellData(); - return this.cellCache.get<T>(this.cacheKey); - } - - return cellData; - }; - - private _loadCellData = async () => { - const result = await this.cellDataLoader.loadData(); - - if (result.ok) { - const cellData = result.val; - - if (cellData.some) { - this.cellCache.insert(this.cacheKey, cellData.val); - this.cellDataNotifier.cellData = cellData; - } - } else { - this.cellCache.remove(this.cacheKey); - this.cellDataNotifier.cellData = None; - } - }; - - dispose = async () => { - await this.cellObserver.unsubscribe(); - await this.fieldNotifier.unsubscribe(); - this.cellDataNotifier.unsubscribe(); - }; -} - -class CellDataNotifier<T> extends ChangeNotifier<T> { - _cellData: Option<T>; - - constructor(cellData: Option<T>) { - super(); - this._cellData = cellData; - } - - set cellData(data: Option<T>) { - if (this._cellData !== data) { - this._cellData = data; - - if (this._cellData.some) { - this.notify(this._cellData.val); - } - } - } - - get cellData(): Option<T> { - return this._cellData; - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts deleted file mode 100644 index 0cd5ae35c2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_observer.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Ok, Result } from 'ts-results'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { DatabaseNotificationObserver } from '../notifications/observer'; -import { DatabaseNotification, FlowyError } from '@/services/backend'; -import { Subscription } from 'rxjs'; - -export type CellChangedCallback = (value: Result<void, FlowyError>) => void; - -export class CellObserver { - private notifier?: ChangeNotifier<Result<void, FlowyError>>; - private listener?: DatabaseNotificationObserver; - private subscription?: Subscription; - - constructor(public readonly rowId: string, public readonly fieldId: string) {} - - subscribe = async (callbacks: { onCellChanged: CellChangedCallback }) => { - this.notifier = new ChangeNotifier(); - this.subscription = this.notifier?.observer?.subscribe(callbacks.onCellChanged); - this.listener = new DatabaseNotificationObserver({ - // The rowId combine with fieldId can identifier the cell. - // This format rowId:fieldId is also defined in the backend, - // so don't change this. - id: this.rowId + ':' + this.fieldId, - parserHandler: (notification, result) => { - switch (notification) { - case DatabaseNotification.DidUpdateCell: - if (result.ok) { - this.notifier?.notify(Ok.EMPTY); - } else { - this.notifier?.notify(result); - } - - return; - default: - break; - } - }, - }); - await this.listener.start(); - }; - - unsubscribe = async () => { - this.subscription?.unsubscribe(); - await this.listener?.stop(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts deleted file mode 100644 index bc1d5744e0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { DateCellDataPB, FieldType, SelectOptionCellDataPB, URLCellDataPB } from '@/services/backend'; -import { CellIdentifier } from './cell_bd_svc'; -import { CellController } from './cell_controller'; -import { - CellDataLoader, - DateCellDataParser, - SelectOptionCellDataParser, - StringCellDataParser, - URLCellDataParser, -} from './data_parser'; -import { CellCache } from './cell_cache'; -import { FieldController } from '../field/field_controller'; -import { DateCellDataPersistence, TextCellDataPersistence } from './data_persistence'; - -export type TextCellController = CellController<string, string>; - -export type CheckboxCellController = CellController<string, string>; - -export type NumberCellController = CellController<string, string>; - -export type SelectOptionCellController = CellController<SelectOptionCellDataPB, string>; - -export type DateCellController = CellController<DateCellDataPB, CalendarData>; - -export class CalendarData { - constructor(public readonly date: Date, public readonly includeTime: boolean, public readonly time?: string) {} -} - -export type URLCellController = CellController<URLCellDataPB, string>; - -export class CellControllerBuilder { - constructor( - public readonly cellIdentifier: CellIdentifier, - public readonly cellCache: CellCache, - public readonly fieldController: FieldController - ) {} - - /// - build = () => { - switch (this.cellIdentifier.fieldType) { - case FieldType.Checkbox: - return this.makeCheckboxCellController(); - case FieldType.RichText: - return this.makeTextCellController(); - case FieldType.Number: - return this.makeNumberCellController(); - case FieldType.DateTime: - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return this.makeDateCellController(); - case FieldType.URL: - return this.makeURLCellController(); - case FieldType.SingleSelect: - case FieldType.MultiSelect: - case FieldType.Checklist: - return this.makeSelectOptionCellController(); - } - }; - - makeSelectOptionCellController = (): SelectOptionCellController => { - const loader = new CellDataLoader(this.cellIdentifier, new SelectOptionCellDataParser(), true); - const persistence = new TextCellDataPersistence(this.cellIdentifier); - - return new CellController<SelectOptionCellDataPB, string>(this.cellIdentifier, this.cellCache, loader, persistence); - }; - - makeURLCellController = (): URLCellController => { - const loader = new CellDataLoader(this.cellIdentifier, new URLCellDataParser()); - const persistence = new TextCellDataPersistence(this.cellIdentifier); - - return new CellController<URLCellDataPB, string>(this.cellIdentifier, this.cellCache, loader, persistence); - }; - - makeDateCellController = (): DateCellController => { - const loader = new CellDataLoader(this.cellIdentifier, new DateCellDataParser(), true); - const persistence = new DateCellDataPersistence(this.cellIdentifier); - - return new CellController<DateCellDataPB, CalendarData>(this.cellIdentifier, this.cellCache, loader, persistence); - }; - - makeNumberCellController = (): NumberCellController => { - const loader = new CellDataLoader(this.cellIdentifier, new StringCellDataParser(), true); - const persistence = new TextCellDataPersistence(this.cellIdentifier); - - return new CellController<string, string>(this.cellIdentifier, this.cellCache, loader, persistence); - }; - - makeTextCellController = (): TextCellController => { - const loader = new CellDataLoader(this.cellIdentifier, new StringCellDataParser()); - const persistence = new TextCellDataPersistence(this.cellIdentifier); - - return new CellController<string, string>(this.cellIdentifier, this.cellCache, loader, persistence); - }; - - makeCheckboxCellController = (): CheckboxCellController => { - const loader = new CellDataLoader(this.cellIdentifier, new StringCellDataParser()); - const persistence = new TextCellDataPersistence(this.cellIdentifier); - - return new CellController<string, string>(this.cellIdentifier, this.cellCache, loader, persistence); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts deleted file mode 100644 index 80afed8f9f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_parser.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { CellBackendService, CellIdentifier } from './cell_bd_svc'; -import { SelectOptionCellDataPB, URLCellDataPB, DateCellDataPB } from '@/services/backend'; -import { Err, None, Ok, Option, Some } from 'ts-results'; -import { Log } from '$app/utils/log'; - -abstract class CellDataParser<T> { - abstract parserData(data: Uint8Array): Option<T>; -} - -class CellDataLoader<T> { - private service = new CellBackendService(); - - constructor( - readonly cellId: CellIdentifier, - readonly parser: CellDataParser<T>, - public readonly reloadOnFieldChanged: boolean = false - ) {} - - loadData = async () => { - const result = await this.service.getCell(this.cellId); - - if (result.ok) { - return Ok(this.parser.parserData(result.val.data)); - } else { - Log.error(result.err); - return Err(result.err); - } - }; -} - -export const utf8Decoder = new TextDecoder('utf-8'); -export const utf8Encoder = new TextEncoder(); - -class StringCellDataParser extends CellDataParser<string> { - parserData(data: Uint8Array): Option<string> { - return Some(utf8Decoder.decode(data)); - } -} - -class DateCellDataParser extends CellDataParser<DateCellDataPB> { - parserData(data: Uint8Array): Option<DateCellDataPB> { - return Some(DateCellDataPB.deserializeBinary(data)); - } -} - -class SelectOptionCellDataParser extends CellDataParser<SelectOptionCellDataPB> { - parserData(data: Uint8Array): Option<SelectOptionCellDataPB> { - if (data.length === 0) { - return None; - } - - return Some(SelectOptionCellDataPB.deserializeBinary(data)); - } -} - -class URLCellDataParser extends CellDataParser<URLCellDataPB> { - parserData(data: Uint8Array): Option<URLCellDataPB> { - if (data.length === 0) { - return None; - } - - return Some(URLCellDataPB.deserializeBinary(data)); - } -} - -export { - StringCellDataParser, - DateCellDataParser, - SelectOptionCellDataParser, - URLCellDataParser, - CellDataLoader, - CellDataParser, -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts deleted file mode 100644 index ea8e9e2953..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Result } from 'ts-results'; -import { CellBackendService, CellIdentifier } from './cell_bd_svc'; -import { CalendarData } from './controller_builder'; -import { DateChangesetPB, FlowyError, CellIdPB } from '@/services/backend'; -import { DatabaseEventUpdateDateCell } from '@/services/backend/events/flowy-database2'; - -export abstract class CellDataPersistence<D> { - abstract save(data: D): Promise<Result<void, FlowyError>>; -} - -export class TextCellDataPersistence extends CellDataPersistence<string> { - constructor(public readonly cellId: CellIdentifier) { - super(); - } - - save(data: string): Promise<Result<void, FlowyError>> { - return CellBackendService.updateCell(this.cellId, data); - } -} - -export class DateCellDataPersistence extends CellDataPersistence<CalendarData> { - constructor(public readonly cellIdentifier: CellIdentifier) { - super(); - } - - save(data: CalendarData): Promise<Result<void, FlowyError>> { - const payload = DateChangesetPB.fromObject({ cell_id: _makeCellId(this.cellIdentifier) }); - - payload.date = (data.date.getTime() / 1000) | 0; - if (data.time !== undefined) { - payload.time = data.time; - } - - payload.include_time = data.includeTime; - return DatabaseEventUpdateDateCell(payload); - } -} - -function _makeCellId(cellIdentifier: CellIdentifier): CellIdPB { - return CellIdPB.fromObject({ - view_id: cellIdentifier.viewId, - field_id: cellIdentifier.fieldId, - row_id: cellIdentifier.rowId, - }); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts deleted file mode 100644 index 5e3e86fadc..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/select_option_bd_svc.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { CellIdentifier } from './cell_bd_svc'; -import { - CellIdPB, - CreateSelectOptionPayloadPB, - RepeatedSelectOptionPayload, - SelectOptionCellChangesetPB, - SelectOptionPB, -} from '@/services/backend'; -import { - DatabaseEventCreateSelectOption, - DatabaseEventDeleteSelectOption, - DatabaseEventGetSelectOptionCellData, - DatabaseEventInsertOrUpdateSelectOption, - DatabaseEventUpdateSelectOptionCell, -} from '@/services/backend/events/flowy-database2'; - -export class SelectOptionBackendService { - constructor(public readonly viewId: string, public readonly fieldId: string) {} - - createOption = async (params: { name: string }) => { - const payload = CreateSelectOptionPayloadPB.fromObject({ - option_name: params.name, - view_id: this.viewId, - field_id: this.fieldId, - }); - - return DatabaseEventCreateSelectOption(payload); - }; -} - -export class SelectOptionCellBackendService { - constructor(public readonly cellIdentifier: CellIdentifier) {} - - // Creates a new option and insert this option to the cell - createOption = async (params: { name: string; isSelect?: boolean }) => { - const payload = CreateSelectOptionPayloadPB.fromObject({ - option_name: params.name, - view_id: this.cellIdentifier.viewId, - field_id: this.cellIdentifier.fieldId, - }); - - const result = await DatabaseEventCreateSelectOption(payload); - - if (result.ok) { - return await this._insertOption(result.val, params.isSelect || true); - } else { - return result; - } - }; - - private _insertOption = (option: SelectOptionPB, _: boolean) => { - const payload = RepeatedSelectOptionPayload.fromObject({ - view_id: this.cellIdentifier.viewId, - field_id: this.cellIdentifier.fieldId, - items: [option], - }); - - return DatabaseEventInsertOrUpdateSelectOption(payload); - }; - - updateOption = async (option: SelectOptionPB) => { - const payload = RepeatedSelectOptionPayload.fromObject({ - view_id: this.cellIdentifier.viewId, - field_id: this.cellIdentifier.fieldId, - items: [option], - }); - - return DatabaseEventInsertOrUpdateSelectOption(payload); - }; - - deleteOption = (options: SelectOptionPB[]) => { - const payload = RepeatedSelectOptionPayload.fromObject({ - view_id: this.cellIdentifier.viewId, - field_id: this.cellIdentifier.fieldId, - row_id: this.cellIdentifier.rowId, - }); - - payload.items.push(...options); - return DatabaseEventDeleteSelectOption(payload); - }; - - getOptionCellData = () => { - return DatabaseEventGetSelectOptionCellData(this._cellIdentifier()); - }; - - selectOption = (optionIds: string[]) => { - const payload = SelectOptionCellChangesetPB.fromObject({ cell_identifier: this._cellIdentifier() }); - - payload.insert_option_ids.push(...optionIds); - return DatabaseEventUpdateSelectOptionCell(payload); - }; - - unselectOption = (optionIds: string[]) => { - const payload = SelectOptionCellChangesetPB.fromObject({ cell_identifier: this._cellIdentifier() }); - - payload.delete_option_ids.push(...optionIds); - return DatabaseEventUpdateSelectOptionCell(payload); - }; - - private _cellIdentifier = () => { - return CellIdPB.fromObject({ - view_id: this.cellIdentifier.viewId, - field_id: this.cellIdentifier.fieldId, - row_id: this.cellIdentifier.rowId, - }); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts deleted file mode 100644 index 33ece15fb8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { - DatabaseEventCreateRow, - DatabaseEventDeleteRow, - DatabaseEventDuplicateRow, - DatabaseEventGetDatabase, - DatabaseEventGetDatabaseSetting, - DatabaseEventGetFields, - DatabaseEventGetGroup, - DatabaseEventGetGroups, - DatabaseEventMoveField, - DatabaseEventMoveGroup, - DatabaseEventMoveGroupRow, - DatabaseEventMoveRow, - DatabaseEventUpdateField, - DatabaseGroupIdPB, - FieldChangesetPB, - MoveFieldPayloadPB, - MoveGroupPayloadPB, - MoveGroupRowPayloadPB, - MoveRowPayloadPB, - RowIdPB, - DuplicateFieldPayloadPB, - DatabaseEventDuplicateField, -} from '@/services/backend/events/flowy-database2'; -import { - GetFieldPayloadPB, - RepeatedFieldIdPB, - FieldIdPB, - DatabaseViewIdPB, - CreateRowPayloadPB, - ViewIdPB, - OrderObjectPositionTypePB, -} from '@/services/backend'; -import { FolderEventCloseView } from '@/services/backend/events/flowy-folder'; -import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; -import { None } from 'ts-results'; - -/// A service that wraps the backend service -export class DatabaseBackendService { - viewId: string; - - constructor(viewId: string) { - this.viewId = viewId; - } - - /// Open a database - openDatabase = async () => { - const payload = DatabaseViewIdPB.fromObject({ - value: this.viewId, - }); - - return DatabaseEventGetDatabase(payload); - }; - - /// Close a database - closeDatabase = async () => { - const payload = ViewIdPB.fromObject({ value: this.viewId }); - - return FolderEventCloseView(payload); - }; - - /// Create a row in database - /// 1.The row will be the last row in database if the params is undefined - /// 2.The row will be placed after the passed-in rowId - /// 3.The row will be moved to the group with groupId. Currently, grouping is - /// only support in kanban board. - createRow = async (params?: { groupId?: string; position?: OrderObjectPositionTypePB; rowId?: string }) => { - const payload = CreateRowPayloadPB.fromObject({ - view_id: this.viewId, - row_position: { - position: params?.position, - object_id: params?.rowId, - }, - group_id: params?.groupId, - }); - - return DatabaseEventCreateRow(payload); - }; - - duplicateRow = async (rowId: string) => { - const payload = RowIdPB.fromObject({ view_id: this.viewId, row_id: rowId }); - - return DatabaseEventDuplicateRow(payload); - }; - - deleteRow = async (rowId: string) => { - const payload = RowIdPB.fromObject({ view_id: this.viewId, row_id: rowId }); - - return DatabaseEventDeleteRow(payload); - }; - - moveRow = async (fromRowId: string, toRowId: string) => { - const payload = MoveRowPayloadPB.fromObject({ view_id: this.viewId, from_row_id: fromRowId, to_row_id: toRowId }); - - return DatabaseEventMoveRow(payload); - }; - - /// Move the row from one group to another group - /// [toRowId] is used to locate the moving row location. - moveGroupRow = (fromRowId: string, toGroupId: string, toRowId?: string) => { - const payload = MoveGroupRowPayloadPB.fromObject({ - view_id: this.viewId, - from_row_id: fromRowId, - to_group_id: toGroupId, - }); - - if (toRowId !== undefined) { - payload.to_row_id = toRowId; - } - - return DatabaseEventMoveGroupRow(payload); - }; - - moveGroup = (fromGroupId: string, toGroupId: string) => { - const payload = MoveGroupPayloadPB.fromObject({ - view_id: this.viewId, - from_group_id: fromGroupId, - to_group_id: toGroupId, - }); - - return DatabaseEventMoveGroup(payload); - }; - - /// Get all fields in database - getFields = async (fieldIds?: FieldIdPB[]) => { - const payload = GetFieldPayloadPB.fromObject({ view_id: this.viewId }); - - if (!fieldIds) { - payload.field_ids = RepeatedFieldIdPB.fromObject({ items: fieldIds }); - } - - return DatabaseEventGetFields(payload).then((result) => result.map((value) => value.items)); - }; - - /// Get a group by id - getGroup = (groupId: string) => { - const payload = DatabaseGroupIdPB.fromObject({ view_id: this.viewId, group_id: groupId }); - - return DatabaseEventGetGroup(payload); - }; - - moveField = (params: { fromFieldId: string; toFieldId: string }) => { - const payload = MoveFieldPayloadPB.fromObject({ - view_id: this.viewId, - from_field_id: params.fromFieldId, - to_field_id: params.toFieldId, - }); - - return DatabaseEventMoveField(payload); - }; - - changeWidth = (params: { fieldId: string; width: number }) => { - const payload = FieldChangesetPB.fromObject({ - view_id: this.viewId, - field_id: params.fieldId, - width: params.width, - }); - - return DatabaseEventUpdateField(payload); - }; - - duplicateField = (fieldId: string) => { - const payload = DuplicateFieldPayloadPB.fromObject({ view_id: this.viewId, field_id: fieldId }); - - return DatabaseEventDuplicateField(payload); - }; - - createField = async () => { - const fieldController = new TypeOptionController(this.viewId, None); - - await fieldController.initialize(); - }; - - /// Get all groups in database - /// It should only call once after the board open - loadGroups = () => { - const payload = DatabaseViewIdPB.fromObject({ value: this.viewId }); - - return DatabaseEventGetGroups(payload); - }; - - getSettings = () => { - const payload = DatabaseViewIdPB.fromObject({ value: this.viewId }); - - return DatabaseEventGetDatabaseSetting(payload); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts deleted file mode 100644 index 0a33ce2817..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { DatabaseBackendService } from './database_bd_svc'; -import { FieldController, FieldInfo } from './field/field_controller'; -import { DatabaseViewCache } from './view/database_view_cache'; -import { DatabasePB, GroupPB, FlowyError, OrderObjectPositionTypePB } from '@/services/backend'; -import { RowChangedReason, RowInfo } from './row/row_cache'; -import { Err, Ok } from 'ts-results'; -import { DatabaseGroupController } from './group/group_controller'; -import { BehaviorSubject } from 'rxjs'; -import { DatabaseGroupObserver } from './group/group_observer'; -import { Log } from '$app/utils/log'; -import { FilterController } from '$app/stores/effects/database/filter/filter_controller'; -import { FilterParsed } from '$app/stores/effects/database/filter/filter_bd_svc'; -import { SortController } from '$app/stores/effects/database/sort/sort_controller'; -import { IDatabaseSort } from '$app_reducers/database/slice'; - -export type DatabaseSubscriberCallbacks = { - onViewChanged?: (data: DatabasePB) => void; - onRowsChanged?: (rowInfos: readonly RowInfo[], reason: RowChangedReason) => void; - onFieldsChanged?: (fieldInfos: readonly FieldInfo[]) => void; - onFiltersChanged?: (filters: readonly FilterParsed[]) => void; - onSortChanged?: (sorts: readonly IDatabaseSort[]) => void; - onGroupByField?: (groups: GroupPB[]) => void; - - onNumOfGroupChanged?: { - onUpdateGroup: (value: GroupPB[]) => void; - onDeleteGroup: (value: GroupPB[]) => void; - onInsertGroup: (value: GroupPB[]) => void; - }; -}; - -export class DatabaseController { - private readonly backendService: DatabaseBackendService; - fieldController: FieldController; - sortController: SortController; - filterController: FilterController; - databaseViewCache: DatabaseViewCache; - private _callback?: DatabaseSubscriberCallbacks; - public groups: BehaviorSubject<DatabaseGroupController[]>; - private groupsObserver: DatabaseGroupObserver; - - constructor(public readonly viewId: string) { - this.backendService = new DatabaseBackendService(viewId); - this.fieldController = new FieldController(viewId); - this.filterController = new FilterController(viewId); - this.sortController = new SortController(viewId); - this.databaseViewCache = new DatabaseViewCache(viewId, this.fieldController); - this.groups = new BehaviorSubject<DatabaseGroupController[]>([]); - this.groupsObserver = new DatabaseGroupObserver(viewId); - } - - subscribe = (callbacks: DatabaseSubscriberCallbacks) => { - this._callback = callbacks; - this.fieldController.subscribe({ onNumOfFieldsChanged: callbacks.onFieldsChanged }); - this.filterController.subscribe({ onFiltersChanged: callbacks.onFiltersChanged }); - this.sortController.subscribe({ onSortChanged: callbacks.onSortChanged }); - this.databaseViewCache.getRowCache().subscribe({ - onRowsChanged: (reason) => { - this._callback?.onRowsChanged?.(this.databaseViewCache.rowInfos, reason); - }, - }); - }; - - open = async () => { - const openDatabaseResult = await this.backendService.openDatabase(); - - if (openDatabaseResult.ok) { - const database: DatabasePB = openDatabaseResult.val; - - await this.databaseViewCache.initialize(); - await this.fieldController.initialize(); - await this.filterController.initialize(); - await this.sortController.initialize(); - - // subscriptions - await this.subscribeOnGroupsChanged(); - - // load database initial data - await this.fieldController.loadFields(database.fields); - - this.databaseViewCache.initializeWithRows(database.rows); - - this._callback?.onViewChanged?.(database); - return Ok(database.rows); - } else { - return Err(openDatabaseResult.val); - } - }; - - getGroupByFieldId = async () => { - const settingsResult = await this.backendService.getSettings(); - - if (settingsResult.ok) { - const settings = settingsResult.val; - const groupConfig = settings.group_settings.items; - - if (groupConfig.length === 0) { - return Err(new FlowyError({ msg: 'this database has no groups' })); - } - - return Ok(settings.group_settings.items[0].field_id); - } else { - return Err(settingsResult.val); - } - }; - - createRow = () => { - return this.backendService.createRow(); - }; - - createRowAfter = (rowId: string) => { - return this.backendService.createRow({ rowId, position: OrderObjectPositionTypePB.After }); - }; - - duplicateRow = async (rowId: string) => { - return this.backendService.duplicateRow(rowId); - }; - - deleteRow = async (rowId: string) => { - return this.backendService.deleteRow(rowId); - }; - - moveRow = (fromRowId: string, toRowId: string) => { - return this.backendService.moveRow(fromRowId, toRowId); - }; - - moveGroupRow = (rowId: string, groupId: string) => { - return this.backendService.moveGroupRow(rowId, groupId); - }; - - exchangeGroupRow = async (fromRowId: string, toGroupId: string, toRowId?: string) => { - await this.backendService.moveGroupRow(fromRowId, toGroupId, toRowId); - await this.loadGroup(); - }; - - moveGroup = (fromGroupId: string, toGroupId: string) => { - return this.backendService.moveGroup(fromGroupId, toGroupId); - }; - - moveField = (params: { fromFieldId: string; toFieldId: string }) => { - return this.backendService.moveField(params); - }; - - changeWidth = (params: { fieldId: string; width: number }) => { - return this.backendService.changeWidth(params); - }; - - duplicateField = (fieldId: string) => { - return this.backendService.duplicateField(fieldId); - }; - - addFieldToLeft = async (fieldId: string) => { - await this.backendService.createField(); - - const newFieldId = this.fieldController.fieldInfos[this.fieldController.fieldInfos.length - 1].field.id; - - await this.moveField({ - fromFieldId: newFieldId, - toFieldId: fieldId, - }); - }; - - addFieldToRight = async (fieldId: string) => { - await this.backendService.createField(); - - const newFieldId = this.fieldController.fieldInfos[this.fieldController.fieldInfos.length - 1].field.id; - - const index = this.fieldController.fieldInfos.findIndex((fieldInfo) => fieldInfo.field.id === fieldId); - - const toFieldId = this.fieldController.fieldInfos[index + 1].field.id; - - await this.moveField({ - fromFieldId: newFieldId, - toFieldId: toFieldId, - }); - }; - - private loadGroup = async () => { - const result = await this.backendService.loadGroups(); - - if (result.ok) { - const groups = result.val.items; - - await this.initialGroups(groups); - } - - return result; - }; - - private initialGroups = async (groups: GroupPB[]) => { - this.groups.getValue().forEach((controller) => { - void controller.dispose(); - }); - - const controllers: DatabaseGroupController[] = []; - - for (const groupPB of groups) { - const controller = new DatabaseGroupController(groupPB, this.backendService); - - await controller.initialize(); - controllers.push(controller); - } - - this.groups.next(controllers); - this.groups.value; - }; - - private subscribeOnGroupsChanged = async () => { - await this.groupsObserver.subscribe({ - onGroupBy: async (result) => { - if (result.ok) { - await this.initialGroups(result.val); - } - }, - onGroupChangeset: (result) => { - if (result.err) { - Log.error(result.val); - return; - } - - const changeset = result.val; - let existControllers = [...this.groups.getValue()]; - - for (const deleteId of changeset.deleted_groups) { - existControllers = existControllers.filter((c) => c.groupId !== deleteId); - } - - for (const update of changeset.update_groups) { - const index = existControllers.findIndex((c) => c.groupId === update.group_id); - - if (index !== -1) { - existControllers[index].updateGroup(update); - } - } - - for (const insert of changeset.inserted_groups) { - const controller = new DatabaseGroupController(insert.group, this.backendService); - - if (insert.index > existControllers.length) { - existControllers.push(controller); - } else { - existControllers.splice(insert.index, 0, controller); - } - } - - this.groups.next(existControllers); - }, - }); - }; - - dispose = async () => { - this.groups.value.forEach((group) => { - void group.dispose(); - }); - await this.groupsObserver.unsubscribe(); - await this.backendService.closeDatabase(); - await this.fieldController.dispose(); - this.filterController.dispose(); - await this.databaseViewCache.dispose(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_bd_svc.ts deleted file mode 100644 index 86f4314693..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_bd_svc.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { - DeleteFieldPayloadPB, - DuplicateFieldPayloadPB, - FieldChangesetPB, - TypeOptionChangesetPB, -} from '@/services/backend'; -import { - DatabaseEventDeleteField, - DatabaseEventDuplicateField, - DatabaseEventUpdateField, - DatabaseEventUpdateFieldTypeOption, -} from '@/services/backend/events/flowy-database2'; - -export abstract class TypeOptionParser<T> { - abstract fromBuffer(buffer: Uint8Array): T; -} - -export class FieldBackendService { - constructor(public readonly viewId: string, public readonly fieldId: string) {} - - updateField = (data: { name?: string; frozen?: boolean; visibility?: boolean; width?: number }) => { - const payload = FieldChangesetPB.fromObject({ view_id: this.viewId, field_id: this.fieldId }); - - if (data.name !== undefined) { - payload.name = data.name; - } - - if (data.frozen !== undefined) { - payload.frozen = data.frozen; - } - - if (data.visibility !== undefined) { - payload.visibility = data.visibility; - } - - if (data.width !== undefined) { - payload.width = data.width; - } - - return DatabaseEventUpdateField(payload); - }; - - updateTypeOption = (typeOptionData: Uint8Array) => { - const payload = TypeOptionChangesetPB.fromObject({ - view_id: this.viewId, - field_id: this.fieldId, - type_option_data: typeOptionData, - }); - - return DatabaseEventUpdateFieldTypeOption(payload); - }; - - deleteField = () => { - const payload = DeleteFieldPayloadPB.fromObject({ view_id: this.viewId, field_id: this.fieldId }); - - return DatabaseEventDeleteField(payload); - }; - - duplicateField = () => { - const payload = DuplicateFieldPayloadPB.fromObject({ view_id: this.viewId, field_id: this.fieldId }); - - return DatabaseEventDuplicateField(payload); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts deleted file mode 100644 index 2234c06dbd..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Log } from '$app/utils/log'; -import { DatabaseBackendService } from '../database_bd_svc'; -import { DatabaseFieldChangesetObserver } from './field_observer'; -import { FieldIdPB, FieldPB, IndexFieldPB } from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; - -export class FieldController { - private backendService: DatabaseBackendService; - private fieldChangesetObserver: DatabaseFieldChangesetObserver; - private numOfFieldsNotifier = new NumOfFieldsNotifier([]); - - constructor(public readonly viewId: string) { - this.backendService = new DatabaseBackendService(viewId); - this.fieldChangesetObserver = new DatabaseFieldChangesetObserver(viewId); - } - - dispose = async () => { - this.numOfFieldsNotifier.unsubscribe(); - await this.fieldChangesetObserver.unsubscribe(); - }; - - get fieldInfos(): readonly FieldInfo[] { - return this.numOfFieldsNotifier.fieldInfos; - } - - getField = (fieldId: string): FieldInfo | undefined => { - return this.numOfFieldsNotifier.fieldInfos.find((element) => element.field.id === fieldId); - }; - - loadFields = async (fieldIds: FieldIdPB[]) => { - const result = await this.backendService.getFields(fieldIds); - - if (result.ok) { - this.numOfFieldsNotifier.fieldInfos = result.val.map((field) => new FieldInfo(field)); - } else { - Log.error(result.val); - } - }; - - subscribe = (callbacks: { onNumOfFieldsChanged?: (fieldInfos: readonly FieldInfo[]) => void }) => { - this.numOfFieldsNotifier.observer?.subscribe((fieldInfos) => { - callbacks.onNumOfFieldsChanged?.(fieldInfos); - }); - }; - - initialize = async () => { - await this.fieldChangesetObserver.subscribe({ - onFieldsChanged: (result) => { - if (result.ok) { - const changeset = result.val; - - this._deleteFields(changeset.deleted_fields); - this._insertFields(changeset.inserted_fields); - this._updateFields(changeset.updated_fields); - } else { - Log.error(result.val); - } - }, - }); - }; - - private _deleteFields = (deletedFields: FieldIdPB[]) => { - if (deletedFields.length === 0) { - return; - } - - const deletedFieldIds = deletedFields.map((field) => field.field_id); - const predicate = (element: FieldInfo): boolean => { - return !deletedFieldIds.includes(element.field.id); - }; - - this.numOfFieldsNotifier.fieldInfos = [...this.fieldInfos].filter(predicate); - }; - - private _insertFields = (insertedFields: IndexFieldPB[]) => { - if (insertedFields.length === 0) { - return; - } - - const newFieldInfos = [...this.fieldInfos]; - - insertedFields.forEach((insertedField) => { - const fieldInfo = new FieldInfo(insertedField.field); - - if (newFieldInfos.length > insertedField.index) { - newFieldInfos.splice(insertedField.index, 0, fieldInfo); - } else { - newFieldInfos.push(fieldInfo); - } - }); - this.numOfFieldsNotifier.fieldInfos = newFieldInfos; - }; - - private _updateFields = (updatedFields: FieldPB[]) => { - if (updatedFields.length === 0) { - return; - } - - const newFieldInfos = [...this.fieldInfos]; - - updatedFields.forEach((updatedField) => { - const index = newFieldInfos.findIndex((fieldInfo) => { - return fieldInfo.field.id === updatedField.id; - }); - - if (index !== -1) { - newFieldInfos.splice(index, 1, new FieldInfo(updatedField)); - } - }); - this.numOfFieldsNotifier.fieldInfos = newFieldInfos; - }; -} - -class NumOfFieldsNotifier extends ChangeNotifier<FieldInfo[]> { - constructor(private _fieldInfos: FieldInfo[]) { - super(); - } - - set fieldInfos(newFieldInfos: FieldInfo[]) { - if (this._fieldInfos !== newFieldInfos) { - this._fieldInfos = newFieldInfos; - this.notify(this._fieldInfos); - } - } - - /// Return a readonly list - get fieldInfos(): FieldInfo[] { - return this._fieldInfos; - } -} - -export class FieldInfo { - constructor(public readonly field: FieldPB) {} -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts deleted file mode 100644 index 162c5c972a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_observer.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Ok, Result } from "ts-results"; -import { DatabaseNotification, DatabaseFieldChangesetPB, FlowyError, FieldPB } from "@/services/backend"; -import { ChangeNotifier } from "$app/utils/change_notifier"; -import { DatabaseNotificationObserver } from "../notifications/observer"; - -export type FieldChangesetSubscribeCallback = (value: Result<DatabaseFieldChangesetPB, FlowyError>) => void; - -export class DatabaseFieldChangesetObserver { - private notifier?: ChangeNotifier<Result<DatabaseFieldChangesetPB, FlowyError>>; - private listener?: DatabaseNotificationObserver; - - constructor(public readonly viewId: string) { - } - - subscribe = async (callbacks: { onFieldsChanged: FieldChangesetSubscribeCallback }) => { - this.notifier = new ChangeNotifier(); - this.notifier?.observer?.subscribe(callbacks.onFieldsChanged); - - this.listener = new DatabaseNotificationObserver({ - id: this.viewId, - parserHandler: (notification, result) => { - switch (notification) { - case DatabaseNotification.DidUpdateFields: - if (result.ok) { - this.notifier?.notify(Ok(DatabaseFieldChangesetPB.deserializeBinary(result.val))); - } else { - this.notifier?.notify(result); - } - - return; - default: - break; - } - } - }); - await this.listener.start(); - }; - - unsubscribe = async () => { - this.notifier?.unsubscribe(); - await this.listener?.stop(); - }; -} - -export type FieldSubscribeCallback = (value: Result<FieldPB, FlowyError>) => void; - -export class DatabaseFieldObserver { - private _notifier?: ChangeNotifier<Result<FieldPB, FlowyError>>; - private _listener?: DatabaseNotificationObserver; - - constructor(public readonly fieldId: string) { - } - - subscribe = async (callbacks: { onFieldChanged: FieldSubscribeCallback }) => { - this._notifier = new ChangeNotifier(); - this._notifier?.observer?.subscribe(callbacks.onFieldChanged); - - this._listener = new DatabaseNotificationObserver({ - id: this.fieldId, - parserHandler: (notification, result) => { - switch (notification) { - case DatabaseNotification.DidUpdateField: - if (result.ok) { - this._notifier?.notify(Ok(FieldPB.deserializeBinary(result.val))); - } else { - this._notifier?.notify(result); - } - - break; - default: - break; - } - } - }); - await this._listener.start(); - }; - - unsubscribe = async () => { - this._notifier?.unsubscribe(); - await this._listener?.stop(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_bd_svc.ts deleted file mode 100644 index eeca1dc102..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_bd_svc.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CreateFieldPayloadPB, FieldType, UpdateFieldTypePayloadPB } from '@/services/backend'; -import { - DatabaseEventCreateField, - DatabaseEventUpdateFieldType, -} from '@/services/backend/events/flowy-database2'; - -export class TypeOptionBackendService { - constructor(public readonly viewId: string) {} - - createTypeOption = (fieldType: FieldType) => { - const payload = CreateFieldPayloadPB.fromObject({ view_id: this.viewId, field_type: fieldType }); - - return DatabaseEventCreateField(payload); - }; - - updateTypeOptionType = (fieldId: string, fieldType: FieldType) => { - const payload = UpdateFieldTypePayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - }); - - return DatabaseEventUpdateFieldType(payload); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts deleted file mode 100644 index 8f37673be7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_context.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { None, Option, Some } from 'ts-results'; -import { TypeOptionController } from './type_option_controller'; -import { - CheckboxTypeOptionPB, - ChecklistTypeOptionPB, - DateTypeOptionPB, - MultiSelectTypeOptionPB, - NumberTypeOptionPB, - SingleSelectTypeOptionPB, - URLTypeOptionPB, -} from '@/services/backend'; -import { utf8Decoder, utf8Encoder } from '../../cell/data_parser'; -import { DatabaseFieldObserver } from '../field_observer'; - -abstract class TypeOptionSerde<T> { - abstract deserialize(buffer: Uint8Array): T; - - abstract serialize(value: T): Uint8Array; -} - -// RichText -export function makeRichTextTypeOptionContext(controller: TypeOptionController): RichTextTypeOptionContext { - const parser = new RichTextTypeOptionSerde(); - - return new TypeOptionContext<string>(parser, controller); -} - -export type RichTextTypeOptionContext = TypeOptionContext<string>; - -class RichTextTypeOptionSerde extends TypeOptionSerde<string> { - deserialize(buffer: Uint8Array): string { - return utf8Decoder.decode(buffer); - } - - serialize(value: string): Uint8Array { - return utf8Encoder.encode(value); - } -} - -// Number -export function makeNumberTypeOptionContext(controller: TypeOptionController): NumberTypeOptionContext { - const parser = new NumberTypeOptionSerde(); - - return new TypeOptionContext<NumberTypeOptionPB>(parser, controller); -} - -export type NumberTypeOptionContext = TypeOptionContext<NumberTypeOptionPB>; - -class NumberTypeOptionSerde extends TypeOptionSerde<NumberTypeOptionPB> { - deserialize(buffer: Uint8Array): NumberTypeOptionPB { - return NumberTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: NumberTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -// Checkbox -export function makeCheckboxTypeOptionContext(controller: TypeOptionController): CheckboxTypeOptionContext { - const parser = new CheckboxTypeOptionSerde(); - - return new TypeOptionContext<CheckboxTypeOptionPB>(parser, controller); -} - -export type CheckboxTypeOptionContext = TypeOptionContext<CheckboxTypeOptionPB>; - -class CheckboxTypeOptionSerde extends TypeOptionSerde<CheckboxTypeOptionPB> { - deserialize(buffer: Uint8Array): CheckboxTypeOptionPB { - return CheckboxTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: CheckboxTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -// URL -export function makeURLTypeOptionContext(controller: TypeOptionController): URLTypeOptionContext { - const parser = new URLTypeOptionSerde(); - - return new TypeOptionContext<URLTypeOptionPB>(parser, controller); -} - -export type URLTypeOptionContext = TypeOptionContext<URLTypeOptionPB>; - -class URLTypeOptionSerde extends TypeOptionSerde<URLTypeOptionPB> { - deserialize(buffer: Uint8Array): URLTypeOptionPB { - return URLTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: URLTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -// Date -export function makeDateTypeOptionContext(controller: TypeOptionController): DateTypeOptionContext { - const parser = new DateTypeOptionSerde(); - - return new TypeOptionContext<DateTypeOptionPB>(parser, controller); -} - -export type DateTypeOptionContext = TypeOptionContext<DateTypeOptionPB>; - -class DateTypeOptionSerde extends TypeOptionSerde<DateTypeOptionPB> { - deserialize(buffer: Uint8Array): DateTypeOptionPB { - return DateTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: DateTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -// SingleSelect -export function makeSingleSelectTypeOptionContext(controller: TypeOptionController): SingleSelectTypeOptionContext { - const parser = new SingleSelectTypeOptionSerde(); - - return new TypeOptionContext<SingleSelectTypeOptionPB>(parser, controller); -} - -export type SingleSelectTypeOptionContext = TypeOptionContext<SingleSelectTypeOptionPB>; - -class SingleSelectTypeOptionSerde extends TypeOptionSerde<SingleSelectTypeOptionPB> { - deserialize(buffer: Uint8Array): SingleSelectTypeOptionPB { - return SingleSelectTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: SingleSelectTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -// Multi-select -export function makeMultiSelectTypeOptionContext(controller: TypeOptionController): MultiSelectTypeOptionContext { - const parser = new MultiSelectTypeOptionSerde(); - - return new TypeOptionContext<MultiSelectTypeOptionPB>(parser, controller); -} - -export type MultiSelectTypeOptionContext = TypeOptionContext<MultiSelectTypeOptionPB>; - -class MultiSelectTypeOptionSerde extends TypeOptionSerde<MultiSelectTypeOptionPB> { - deserialize(buffer: Uint8Array): MultiSelectTypeOptionPB { - return MultiSelectTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: MultiSelectTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -// Checklist -export function makeChecklistTypeOptionContext(controller: TypeOptionController): ChecklistTypeOptionContext { - const parser = new ChecklistTypeOptionSerde(); - - return new TypeOptionContext<ChecklistTypeOptionPB>(parser, controller); -} - -export type ChecklistTypeOptionContext = TypeOptionContext<ChecklistTypeOptionPB>; - -class ChecklistTypeOptionSerde extends TypeOptionSerde<ChecklistTypeOptionPB> { - deserialize(buffer: Uint8Array): ChecklistTypeOptionPB { - return ChecklistTypeOptionPB.deserializeBinary(buffer); - } - - serialize(value: ChecklistTypeOptionPB): Uint8Array { - return value.serializeBinary(); - } -} - -export class TypeOptionContext<T> { - private typeOption: Option<T>; - private fieldObserver: DatabaseFieldObserver; - - constructor(public readonly parser: TypeOptionSerde<T>, private readonly controller: TypeOptionController) { - this.typeOption = None; - this.fieldObserver = new DatabaseFieldObserver(controller.fieldId); - - void this.fieldObserver.subscribe({ - onFieldChanged: () => { - void this.getTypeOption(); - }, - }); - } - - get viewId(): string { - return this.controller.viewId; - } - - getTypeOption = (): T => { - const type_option_data = this.controller.getTypeOption(); - const typeOption = this.parser.deserialize(type_option_data); - - this.typeOption = Some(typeOption); - return typeOption; - }; - - // Save the typeOption to disk - setTypeOption = async (typeOption: T) => { - await this.controller.saveTypeOption(this.parser.serialize(typeOption)); - this.typeOption = Some(typeOption); - }; - - dispose = async () => { - await this.fieldObserver.unsubscribe(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts deleted file mode 100644 index 342a910206..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/type_option/type_option_controller.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { FieldPB, FieldType } from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { FieldBackendService } from '../field_bd_svc'; -import { Log } from '$app/utils/log'; -import { None, Option, Some } from 'ts-results'; -import { FieldInfo } from '../field_controller'; -import { TypeOptionBackendService } from './type_option_bd_svc'; - -export class TypeOptionController { - private fieldNotifier = new ChangeNotifier<FieldPB>(); - private field: Option<FieldPB>; - private fieldBackendSvc?: FieldBackendService; - private typeOptionBackendSvc: TypeOptionBackendService; - - // Must call [initialize] if the passed-in fieldInfo is None - constructor( - public readonly viewId: string, - private readonly initialFieldInfo: Option<FieldInfo> = None, - private readonly defaultFieldType: FieldType = FieldType.RichText - ) { - if (initialFieldInfo.none) { - this.field = None; - } else { - this.field = Some(initialFieldInfo.val.field); - this.fieldBackendSvc = new FieldBackendService(this.viewId, initialFieldInfo.val.field.id); - } - - this.typeOptionBackendSvc = new TypeOptionBackendService(viewId); - } - - // It will create a new field for the defaultFieldType if the [initialFieldInfo] is None. - // Otherwise, it will get the type option of the [initialFieldInfo] - initialize = async () => { - if (this.initialFieldInfo.none) { - await this.createTypeOption(this.defaultFieldType); - } - }; - - get fieldId(): string { - return this.getFieldInfo().field.id; - } - - get fieldType(): FieldType { - return this.getFieldInfo().field.field_type; - } - - getFieldInfo = (): FieldInfo => { - if (this.field.none) { - if (this.initialFieldInfo.some) { - return this.initialFieldInfo.val; - } else { - throw Error('Unexpected empty type option data. Should call initialize first'); - } - } - - return new FieldInfo(this.field.val); - }; - - switchToField = async (fieldType: FieldType) => { - if (this.field.some) { - this.field.val.field_type = fieldType; - await this.typeOptionBackendSvc.updateTypeOptionType(this.fieldId, fieldType).then((result) => { - if (result.err) { - Log.error(result.val); - } - }); - this.fieldNotifier.notify(this.field.val); - } else { - throw Error('Unexpected empty type option data. Should call initialize first'); - } - }; - - setFieldName = async (name: string) => { - if (this.field.some) { - this.field.val.name = name; - void this.fieldBackendSvc?.updateField({ name: name }); - this.fieldNotifier.notify(this.field.val); - } - }; - - hideField = async () => { - if (this.fieldBackendSvc) { - void this.fieldBackendSvc.updateField({ visibility: false }); - } else { - throw Error('Unexpected empty field backend service'); - } - }; - - showField = async () => { - if (this.fieldBackendSvc) { - void this.fieldBackendSvc.updateField({ visibility: true }); - } else { - throw Error('Unexpected empty field backend service'); - } - }; - - changeWidth = async (width: number) => { - if (this.fieldBackendSvc) { - void this.fieldBackendSvc.updateField({ width: width }); - } else { - throw Error('Unexpected empty field backend service'); - } - }; - - saveTypeOption = async (data: Uint8Array) => { - if (this.field.some) { - this.field.val.type_option_data = data; - await this.fieldBackendSvc?.updateTypeOption(data).then((result) => { - if (result.err) { - Log.error(result.val); - } - }); - } else { - throw Error('Unexpected empty type option data. Should call initialize first'); - } - }; - - deleteField = async () => { - if (this.fieldBackendSvc === undefined) { - Log.error('Unexpected empty field backend service'); - } - - return this.fieldBackendSvc?.deleteField(); - }; - - duplicateField = async () => { - if (this.fieldBackendSvc === undefined) { - Log.error('Unexpected empty field backend service'); - } - - return this.fieldBackendSvc?.duplicateField(); - }; - - // Returns the type option for specific field with specific fieldType - getTypeOption = () => { - if (this.field.some) { - return this.field.val.type_option_data; - } else { - throw Error('Unexpected empty type option data. Should call initialize first'); - } - }; - - private createTypeOption = async (fieldType: FieldType) => { - const result = await this.typeOptionBackendSvc.createTypeOption(fieldType); - - if (result.ok) { - this.updateField(result.val); - } - - return result; - }; - - private updateField = (field: FieldPB) => { - this.field = Some(field); - this.fieldBackendSvc = new FieldBackendService(this.viewId, field.id); - this.fieldNotifier.notify(field); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/filter/filter_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/filter/filter_bd_svc.ts deleted file mode 100644 index 3fa6982933..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/filter/filter_bd_svc.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { - CheckboxFilterPB, - DatabaseSettingChangesetPB, - DatabaseViewIdPB, - DeleteFilterPayloadPB, - FieldType, - FilterPB, - FlowyError, - SelectOptionFilterPB, - TextFilterPB, - UpdateFilterPayloadPB, -} from '@/services/backend'; -import { - DatabaseEventGetAllFilters, - DatabaseEventUpdateDatabaseSetting, -} from '@/services/backend/events/flowy-database2'; -import { Err, Ok, Result } from 'ts-results'; -import { nanoid } from 'nanoid'; - -export class FilterBackendService { - constructor(public readonly viewId: string) {} - - getFilters = async (): Promise<Result<FilterParsed[], FlowyError>> => { - const payload = DatabaseViewIdPB.fromObject({ - value: this.viewId, - }); - - const res = await DatabaseEventGetAllFilters(payload); - - if (res.ok) { - return Ok(res.val.items.map<FilterParsed>((f) => new FilterParsed(this.viewId, f))); - } else { - return Err(res.val); - } - }; - - addFilter = async ( - fieldId: string, - fieldType: FieldType, - filter: TextFilterPB | SelectOptionFilterPB | CheckboxFilterPB - ) => { - const data = filter.serializeBinary(); - const id = nanoid(4); - - await DatabaseEventUpdateDatabaseSetting( - DatabaseSettingChangesetPB.fromObject({ - view_id: this.viewId, - update_filter: UpdateFilterPayloadPB.fromObject({ - filter_id: id, - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - data, - }), - }) - ); - return id; - }; - - updateFilter = ( - filterId: string, - fieldId: string, - fieldType: FieldType, - filter: TextFilterPB | SelectOptionFilterPB | CheckboxFilterPB - ) => { - const data = filter.serializeBinary(); - - return DatabaseEventUpdateDatabaseSetting( - DatabaseSettingChangesetPB.fromObject({ - view_id: this.viewId, - update_filter: UpdateFilterPayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - filter_id: filterId, - data, - }), - }) - ); - }; - - removeFilter = (fieldId: string, fieldType: FieldType, filterId: string) => { - return DatabaseEventUpdateDatabaseSetting( - DatabaseSettingChangesetPB.fromObject({ - view_id: this.viewId, - delete_filter: DeleteFilterPayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - filter_id: filterId, - }), - }) - ); - }; -} - -export class FilterParsed { - view_id: string; - id: string; - field_id: string; - field_type: FieldType; - data: TextFilterPB | SelectOptionFilterPB | CheckboxFilterPB | Uint8Array; - - constructor(view_id: string, filter: FilterPB) { - this.view_id = view_id; - - this.id = filter.id; - this.field_id = filter.field_id; - this.field_type = filter.field_type; - - switch (filter.field_type) { - case FieldType.RichText: - this.data = TextFilterPB.deserializeBinary(filter.data); - break; - case FieldType.SingleSelect: - case FieldType.MultiSelect: - this.data = SelectOptionFilterPB.deserializeBinary(filter.data); - break; - case FieldType.Checkbox: - this.data = CheckboxFilterPB.deserializeBinary(filter.data); - break; - default: - this.data = filter.data; - break; - } - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/filter/filter_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/filter/filter_controller.ts deleted file mode 100644 index b56d1e8a58..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/filter/filter_controller.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { FilterBackendService, FilterParsed } from '$app/stores/effects/database/filter/filter_bd_svc'; -import { CheckboxFilterPB, FieldType, SelectOptionFilterPB, TextFilterPB } from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; - -export class FilterController { - filterService: FilterBackendService; - notifier: FilterNotifier; - - constructor(public readonly viewId: string) { - this.filterService = new FilterBackendService(viewId); - this.notifier = new FilterNotifier(); - } - - initialize = async () => { - await this.readFilters(); - }; - - readFilters = async () => { - const result = await this.filterService.getFilters(); - - if (result.ok) { - this.notifier.filters = result.val; - } - }; - - addFilter = async ( - fieldId: string, - fieldType: FieldType, - filter: TextFilterPB | SelectOptionFilterPB | CheckboxFilterPB - ) => { - const id = await this.filterService.addFilter(fieldId, fieldType, filter); - - await this.readFilters(); - return id; - }; - - updateFilter = async ( - filterId: string, - fieldId: string, - fieldType: FieldType, - filter: TextFilterPB | SelectOptionFilterPB | CheckboxFilterPB - ) => { - const result = await this.filterService.updateFilter(filterId, fieldId, fieldType, filter); - - if (result.ok) { - await this.readFilters(); - } - }; - - removeFilter = async (fieldId: string, fieldType: FieldType, filterId: string) => { - const result = await this.filterService.removeFilter(fieldId, fieldType, filterId); - - if (result.ok) { - await this.readFilters(); - } - }; - - subscribe = (callbacks: { onFiltersChanged?: (filters: FilterParsed[]) => void }) => { - if (callbacks.onFiltersChanged) { - this.notifier.observer?.subscribe(callbacks.onFiltersChanged); - } - }; - - dispose = () => { - this.notifier.unsubscribe(); - }; -} - -class FilterNotifier extends ChangeNotifier<FilterParsed[]> { - private _filters: FilterParsed[] = []; - - get filters(): FilterParsed[] { - return this._filters; - } - - set filters(value: FilterParsed[]) { - this._filters = value; - this.notify(value); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_controller.ts deleted file mode 100644 index 0a5c8de4af..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_controller.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { DatabaseNotification, FlowyError, GroupPB, GroupRowsNotificationPB, RowMetaPB } from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { None, Ok, Option, Result, Some } from 'ts-results'; -import { DatabaseNotificationObserver } from '../notifications/observer'; -import { Log } from '$app/utils/log'; -import { DatabaseBackendService } from '../database_bd_svc'; - -export type GroupDataCallbacks = { - onRemoveRow: (groupId: string, rowId: string) => void; - onInsertRow: (groupId: string, row: RowMetaPB, index?: number) => void; - onUpdateRow: (groupId: string, row: RowMetaPB) => void; - - onCreateRow: (groupId: string, row: RowMetaPB) => void; -}; - -export class DatabaseGroupController { - private dataObserver: GroupDataObserver; - private callbacks?: GroupDataCallbacks; - - constructor(private group: GroupPB, private databaseBackendSvc: DatabaseBackendService) { - this.dataObserver = new GroupDataObserver(group.group_id); - } - - get groupId() { - return this.group.group_id; - } - - get rows() { - return this.group.rows; - } - - get name() { - return this.group.group_name; - } - - updateGroup = (group: GroupPB) => { - this.group = group; - }; - - rowAtIndex = (index: number): Option<RowMetaPB> => { - if (this.group.rows.length < index) { - return None; - } - - return Some(this.group.rows[index]); - }; - - initialize = async () => { - await this.dataObserver.subscribe({ - onRowsChanged: (result) => { - if (result.ok) { - const changeset = result.val; - - // Delete - changeset.deleted_rows.forEach((deletedRowId) => { - this.group.rows = this.group.rows.filter((row) => row.id !== deletedRowId); - this.callbacks?.onRemoveRow(this.group.group_id, deletedRowId); - }); - - // Insert - changeset.inserted_rows.forEach((insertedRow) => { - let index: number | undefined = insertedRow.index; - - if (insertedRow.has_index && this.group.rows.length > insertedRow.index) { - this.group.rows.splice(index, 0, insertedRow.row_meta); - } else { - index = undefined; - this.group.rows.push(insertedRow.row_meta); - } - - if (insertedRow.is_new) { - this.callbacks?.onCreateRow(this.group.group_id, insertedRow.row_meta); - } else { - this.callbacks?.onInsertRow(this.group.group_id, insertedRow.row_meta, index); - } - }); - - // Update - changeset.updated_rows.forEach((updatedRow) => { - const index = this.group.rows.findIndex((row) => row.id === updatedRow.id); - - if (index !== -1) { - this.group.rows[index] = updatedRow; - this.callbacks?.onUpdateRow(this.group.group_id, updatedRow); - } - }); - } else { - Log.error(result.val); - } - }, - }); - }; - - createRow = async () => { - return this.databaseBackendSvc.createRow({ groupId: this.group.group_id }); - }; - - subscribe = (callbacks: GroupDataCallbacks) => { - this.callbacks = callbacks; - }; - - unsubscribe = () => { - this.callbacks = undefined; - }; - - dispose = async () => { - await this.dataObserver.unsubscribe(); - this.callbacks = undefined; - }; -} - -type GroupRowsSubscribeCallback = (value: Result<GroupRowsNotificationPB, FlowyError>) => void; - -class GroupDataObserver { - private notifier?: ChangeNotifier<Result<GroupRowsNotificationPB, FlowyError>>; - private listener?: DatabaseNotificationObserver; - - constructor(public readonly groupId: string) {} - - subscribe = async (callbacks: { onRowsChanged: GroupRowsSubscribeCallback }) => { - this.notifier = new ChangeNotifier(); - this.notifier?.observer?.subscribe(callbacks.onRowsChanged); - - this.listener = new DatabaseNotificationObserver({ - id: this.groupId, - parserHandler: (notification, result) => { - switch (notification) { - case DatabaseNotification.DidUpdateGroupRow: - if (result.ok) { - this.notifier?.notify(Ok(GroupRowsNotificationPB.deserializeBinary(result.val))); - } else { - this.notifier?.notify(result); - } - - return; - default: - break; - } - }, - }); - await this.listener.start(); - }; - - unsubscribe = async () => { - await this.listener?.stop(); - this.notifier?.unsubscribe(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_observer.ts deleted file mode 100644 index 7967351145..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/group/group_observer.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { Ok, Result } from 'ts-results'; -import { DatabaseNotification, FlowyError, GroupChangesPB, GroupPB } from '@/services/backend'; -import { DatabaseNotificationObserver } from '../notifications/observer'; - -export type GroupByFieldCallback = (value: Result<GroupPB[], FlowyError>) => void; -export type GroupChangesetSubscribeCallback = (value: Result<GroupChangesPB, FlowyError>) => void; - -export class DatabaseGroupObserver { - private groupByNotifier?: ChangeNotifier<Result<GroupPB[], FlowyError>>; - private groupChangesetNotifier?: ChangeNotifier<Result<GroupChangesPB, FlowyError>>; - private listener?: DatabaseNotificationObserver; - - constructor(public readonly viewId: string) {} - - subscribe = async (callbacks: { - onGroupBy: GroupByFieldCallback; - onGroupChangeset: GroupChangesetSubscribeCallback; - }) => { - this.groupByNotifier = new ChangeNotifier(); - this.groupByNotifier?.observer?.subscribe(callbacks.onGroupBy); - - this.groupChangesetNotifier = new ChangeNotifier(); - this.groupChangesetNotifier?.observer?.subscribe(callbacks.onGroupChangeset); - - this.listener = new DatabaseNotificationObserver({ - id: this.viewId, - parserHandler: (notification, result) => { - switch (notification) { - case DatabaseNotification.DidGroupByField: - if (result.ok) { - this.groupByNotifier?.notify(Ok(GroupChangesPB.deserializeBinary(result.val).initial_groups)); - } else { - this.groupByNotifier?.notify(result); - } - - break; - case DatabaseNotification.DidUpdateNumOfGroups: - if (result.ok) { - this.groupChangesetNotifier?.notify(Ok(GroupChangesPB.deserializeBinary(result.val))); - } else { - this.groupChangesetNotifier?.notify(result); - } - - break; - default: - break; - } - }, - }); - - await this.listener.start(); - }; - - unsubscribe = async () => { - this.groupByNotifier?.unsubscribe(); - this.groupChangesetNotifier?.unsubscribe(); - await this.listener?.stop(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts deleted file mode 100644 index 210258cde8..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/observer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { DatabaseNotification, FlowyError } from '@/services/backend'; -import { AFNotificationObserver } from '@/services/backend/notifications'; -import { DatabaseNotificationParser } from './parser'; -import { Result } from 'ts-results'; - -export type ParserHandler = (notification: DatabaseNotification, result: Result<Uint8Array, FlowyError>) => void; - -export class DatabaseNotificationObserver extends AFNotificationObserver<DatabaseNotification> { - constructor(params: { id?: string; parserHandler: ParserHandler }) { - const parser = new DatabaseNotificationParser({ - callback: params.parserHandler, - id: params.id, - }); - - super(parser); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/parser.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/parser.ts deleted file mode 100644 index caac06d1ba..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/notifications/parser.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { DatabaseNotification, FlowyError } from '@/services/backend'; -import { NotificationParser } from '@/services/backend/notifications'; -import { Result } from 'ts-results'; - -declare type DatabaseNotificationCallback = (ty: DatabaseNotification, payload: Result<Uint8Array, FlowyError>) => void; - -export class DatabaseNotificationParser extends NotificationParser<DatabaseNotification> { - constructor(params: { id?: string; callback: DatabaseNotificationCallback }) { - super( - params.callback, - (ty) => { - const notification = DatabaseNotification[ty]; - - if (isDatabaseNotification(notification)) { - return DatabaseNotification[notification]; - } else { - return DatabaseNotification.Unknown; - } - }, - params.id - ); - } -} - -const isDatabaseNotification = (notification: string): notification is keyof typeof DatabaseNotification => { - return Object.values(DatabaseNotification).indexOf(notification) !== -1; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts deleted file mode 100644 index a353fa6198..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_cache.ts +++ /dev/null @@ -1,414 +0,0 @@ -import { - InsertedRowPB, - UpdatedRowPB, - RowIdPB, - RowsChangePB, - RowsVisibilityChangePB, - ReorderSingleRowPB, - RowMetaPB, -} from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { FieldInfo } from '../field/field_controller'; -import { CellCache, CellCacheKey } from '../cell/cell_cache'; -import { CellIdentifier } from '../cell/cell_bd_svc'; -import { DatabaseEventGetRowMeta } from '@/services/backend/events/flowy-database2'; -import { None, Option, Some } from 'ts-results'; -import { Log } from '$app/utils/log'; - -export type CellByFieldId = Map<string, CellIdentifier>; - -export class RowCache { - private readonly rowList: RowList; - private readonly cellCache: CellCache; - private readonly notifier: RowChangeNotifier; - - constructor(public readonly viewId: string, private readonly getFieldInfos: () => readonly FieldInfo[]) { - this.rowList = new RowList(); - this.cellCache = new CellCache(viewId); - this.notifier = new RowChangeNotifier(); - } - - get rows(): readonly RowInfo[] { - return this.rowList.rows; - } - - getCellCache = () => { - return this.cellCache; - }; - - loadCells = async (rowId: string): Promise<CellByFieldId> => { - const opRow = this.rowList.getRow(rowId); - - if (opRow.some) { - return this._toCellMap(opRow.val.row.id, this.getFieldInfos()); - } else { - const rowResult = await this._loadRow(rowId); - - if (rowResult.ok) { - this._refreshRow(rowResult.val); - return this._toCellMap(rowId, this.getFieldInfos()); - } else { - Log.error(rowResult.val); - return new Map(); - } - } - }; - - subscribe = (callbacks: { - onRowsChanged: (reason: RowChangedReason, cellMap?: Map<string, CellIdentifier>) => void; - }) => { - return this.notifier.observer?.subscribe((change) => { - if (change.rowId !== undefined) { - callbacks.onRowsChanged(change.reason, this._toCellMap(change.rowId, this.getFieldInfos())); - } else { - callbacks.onRowsChanged(change.reason); - } - }); - }; - - onFieldUpdated = (fieldInfo: FieldInfo) => { - // Remove the cell data if the corresponding field was changed - this.cellCache.removeWithFieldId(fieldInfo.field.id); - }; - - onNumberOfFieldsUpdated = (fieldInfos: readonly FieldInfo[]) => { - this.rowList.setFieldInfos(fieldInfos); - this.notifier.withChange(RowChangedReason.FieldDidChanged); - }; - - initializeRows = (rows: RowMetaPB[]) => { - rows.forEach((rowPB) => { - this.rowList.push(this._toRowInfo(rowPB)); - }); - this.notifier.withChange(RowChangedReason.ReorderRows); - }; - - applyRowsChanged = (changeset: RowsChangePB) => { - this._deleteRows(changeset.deleted_rows); - this._insertRows(changeset.inserted_rows); - this._updateRows(changeset.updated_rows); - }; - - applyRowsVisibility = (changeset: RowsVisibilityChangePB) => { - this._hideRows(changeset.invisible_rows); - this._displayRows(changeset.visible_rows); - }; - - applyReorderRows = (rowIds: string[]) => { - this.rowList.reorderByRowIds(rowIds); - this.notifier.withChange(RowChangedReason.ReorderRows); - }; - - applyReorderSingleRow = (reorderRow: ReorderSingleRowPB) => { - const rowInfo = this.rowList.getRow(reorderRow.row_id); - - if (rowInfo !== undefined) { - this.rowList.move({ rowId: reorderRow.row_id, fromIndex: reorderRow.old_index, toIndex: reorderRow.new_index }); - this.notifier.withChange(RowChangedReason.ReorderSingleRow, reorderRow.row_id); - } - }; - - private _refreshRow = (updatedRow: RowMetaPB) => { - const option = this.rowList.getRowWithIndex(updatedRow.id); - - if (option.some) { - const { rowInfo, index } = option.val; - - this.rowList.remove(rowInfo.row.id); - this.rowList.insert(index, rowInfo.copyWith({ row: updatedRow })); - } else { - const newRowInfo = new RowInfo(this.viewId, this.getFieldInfos(), updatedRow); - - this.rowList.push(newRowInfo); - } - }; - - private _loadRow = (rowId: string) => { - const payload = RowIdPB.fromObject({ view_id: this.viewId, row_id: rowId }); - - return DatabaseEventGetRowMeta(payload); - }; - - private _deleteRows = (rowIds: string[]) => { - rowIds.forEach((rowId) => { - const deletedRow = this.rowList.remove(rowId); - - if (deletedRow !== undefined) { - this.notifier.withChange(RowChangedReason.Delete, deletedRow.rowInfo.row.id); - } - }); - }; - - private _insertRows = (rows: InsertedRowPB[]) => { - rows.forEach((insertedRow) => { - const rowInfo = this._toRowInfo(insertedRow.row_meta); - const insertedIndex = this.rowList.insert(insertedRow.index, rowInfo); - - if (insertedIndex !== undefined) { - this.notifier.withChange(RowChangedReason.Insert, insertedIndex.rowId); - } - }); - }; - - private _updateRows = (updatedRows: UpdatedRowPB[]) => { - if (updatedRows.length === 0) { - return; - } - - const rowInfos: RowInfo[] = []; - - updatedRows.forEach((updatedRow) => { - updatedRow.field_ids.forEach((fieldId) => { - const key = new CellCacheKey(fieldId, updatedRow.row_meta.id); - - this.cellCache.remove(key); - }); - - rowInfos.push(this._toRowInfo(updatedRow.row_meta)); - }); - - const updatedIndexs = this.rowList.insertRows(rowInfos); - - updatedIndexs.forEach((row) => { - this.notifier.withChange(RowChangedReason.Update, row.rowId); - }); - }; - - private _hideRows = (rowIds: string[]) => { - rowIds.forEach((rowId) => { - const deletedRow = this.rowList.remove(rowId); - - if (deletedRow !== undefined) { - this.notifier.withChange(RowChangedReason.Delete, deletedRow.rowInfo.row.id); - } - }); - }; - - private _displayRows = (insertedRows: InsertedRowPB[]) => { - insertedRows.forEach((insertedRow) => { - const insertedIndex = this.rowList.insert(insertedRow.index, this._toRowInfo(insertedRow.row_meta)); - - if (insertedIndex !== undefined) { - this.notifier.withChange(RowChangedReason.Insert, insertedIndex.rowId); - } - }); - }; - - dispose = async () => { - this.notifier.dispose(); - }; - - private _toRowInfo = (rowPB: RowMetaPB) => { - return new RowInfo(this.viewId, this.getFieldInfos(), rowPB); - }; - - private _toCellMap = (rowId: string, fieldInfos: readonly FieldInfo[]): CellByFieldId => { - const cellIdentifierByFieldId: Map<string, CellIdentifier> = new Map(); - - fieldInfos.forEach((fieldInfo) => { - const identifier = new CellIdentifier(this.viewId, rowId, fieldInfo.field.id, fieldInfo.field.field_type); - - cellIdentifierByFieldId.set(fieldInfo.field.id, identifier); - }); - - return cellIdentifierByFieldId; - }; -} - -class RowList { - _rowInfos: RowInfo[] = []; - _rowInfoByRowId: Map<string, RowInfo> = new Map(); - - get rows(): readonly RowInfo[] { - return this._rowInfos; - } - - getRow = (rowId: string): Option<RowInfo> => { - const rowInfo = this._rowInfoByRowId.get(rowId); - - if (rowInfo === undefined) { - return None; - } else { - return Some(rowInfo); - } - }; - getRowWithIndex = (rowId: string): Option<{ rowInfo: RowInfo; index: number }> => { - const rowInfo = this._rowInfoByRowId.get(rowId); - - if (rowInfo !== undefined) { - const index = this._rowInfos.indexOf(rowInfo, 0); - - return Some({ rowInfo: rowInfo, index: index }); - } - - return None; - }; - - indexOfRow = (rowId: string): number => { - const rowInfo = this._rowInfoByRowId.get(rowId); - - if (rowInfo !== undefined) { - return this._rowInfos.indexOf(rowInfo, 0); - } - - return -1; - }; - - push = (rowInfo: RowInfo) => { - const index = this.indexOfRow(rowInfo.row.id); - - if (index !== -1) { - this._rowInfos.splice(index, 1, rowInfo); - } else { - this._rowInfos.push(rowInfo); - } - - this._rowInfoByRowId.set(rowInfo.row.id, rowInfo); - }; - - remove = (rowId: string): DeletedRow | undefined => { - const result = this.getRowWithIndex(rowId); - - if (result.some) { - const { rowInfo, index } = result.val; - - this._rowInfoByRowId.delete(rowInfo.row.id); - this._rowInfos.splice(index, 1); - return new DeletedRow(index, rowInfo); - } else { - return undefined; - } - }; - - insert = (insertIndex: number, newRowInfo: RowInfo): InsertedRow | undefined => { - const rowId = newRowInfo.row.id; - // Calibrate where to insert - let insertedIndex = insertIndex; - - if (this._rowInfos.length <= insertedIndex) { - insertedIndex = this._rowInfos.length; - } - - const result = this.getRowWithIndex(rowId); - - if (result.some) { - const { index } = result.val; - - // remove the old row info - this._rowInfos.splice(index, 1); - // insert the new row info to the insertedIndex - this._rowInfos.splice(insertedIndex, 0, newRowInfo); - this._rowInfoByRowId.set(rowId, newRowInfo); - return undefined; - } else { - this._rowInfos.splice(insertedIndex, 0, newRowInfo); - this._rowInfoByRowId.set(rowId, newRowInfo); - return new InsertedRow(insertedIndex, rowId); - } - }; - - insertRows = (rowInfos: RowInfo[]) => { - const map = new Map<string, InsertedRow>(); - - rowInfos.forEach((rowInfo) => { - const index = this.indexOfRow(rowInfo.row.id); - - if (index !== -1) { - this._rowInfos.splice(index, 1, rowInfo); - this._rowInfoByRowId.set(rowInfo.row.id, rowInfo); - - map.set(rowInfo.row.id, new InsertedRow(index, rowInfo.row.id)); - } - }); - return map; - }; - - move = (params: { rowId: string; fromIndex: number; toIndex: number }) => { - const currentIndex = this.indexOfRow(params.rowId); - - if (currentIndex !== -1 && currentIndex !== params.toIndex) { - const rowInfo = this.remove(params.rowId)?.rowInfo; - - if (rowInfo !== undefined) { - this.insert(params.toIndex, rowInfo); - } - } - }; - - reorderByRowIds = (rowIds: string[]) => { - // remove all the elements - this._rowInfos = []; - rowIds.forEach((rowId) => { - const rowInfo = this._rowInfoByRowId.get(rowId); - - if (rowInfo !== undefined) { - this._rowInfos.push(rowInfo); - } - }); - }; - - includes = (rowId: string): boolean => { - return this._rowInfoByRowId.has(rowId); - }; - - setFieldInfos = (fieldInfos: readonly FieldInfo[]) => { - const newRowInfos: RowInfo[] = []; - - this._rowInfos.forEach((rowInfo) => { - newRowInfos.push(rowInfo.copyWith({ fieldInfos: fieldInfos })); - }); - this._rowInfos = newRowInfos; - }; -} - -export class RowInfo { - constructor( - public readonly viewId: string, - public readonly fieldInfos: readonly FieldInfo[], - public readonly row: RowMetaPB - ) {} - - copyWith = (params: { row?: RowMetaPB; fieldInfos?: readonly FieldInfo[] }) => { - return new RowInfo(this.viewId, params.fieldInfos || this.fieldInfos, params.row || this.row); - }; -} - -export class DeletedRow { - constructor(public readonly index: number, public readonly rowInfo: RowInfo) {} -} - -export class InsertedRow { - constructor(public readonly index: number, public readonly rowId: string) {} -} - -export class RowChanged { - constructor(public readonly reason: RowChangedReason, public readonly rowId?: string) {} -} - -// eslint-disable-next-line no-shadow -export enum RowChangedReason { - Insert, - Delete, - Update, - Initial, - FieldDidChanged, - ReorderRows, - ReorderSingleRow, -} - -export class RowChangeNotifier extends ChangeNotifier<RowChanged> { - _currentChanged = new RowChanged(RowChangedReason.Initial); - - withChange = (reason: RowChangedReason, rowId?: string) => { - const newChange = new RowChanged(reason, rowId); - - if (this._currentChanged !== newChange) { - this._currentChanged = newChange; - this.notify(this._currentChanged); - } - }; - - dispose = () => { - this.unsubscribe(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_controller.ts deleted file mode 100644 index 7de6ee0e57..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/row/row_controller.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CellByFieldId, RowCache, RowInfo } from './row_cache'; -import { FieldController } from '../field/field_controller'; - -export class RowController { - constructor( - public readonly rowInfo: RowInfo, - public readonly fieldController: FieldController, - private readonly cache: RowCache - ) { - // - } - - loadCells = async (): Promise<CellByFieldId> => { - return this.cache.loadCells(this.rowInfo.row.id); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/sort/sort_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/sort/sort_bd_svc.ts deleted file mode 100644 index 1eabf080d2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/sort/sort_bd_svc.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - DatabaseSettingChangesetPB, - DatabaseViewIdPB, - DeleteSortPayloadPB, - FieldType, - FlowyError, - SortConditionPB, - UpdateSortPayloadPB, -} from '@/services/backend'; -import { DatabaseEventGetAllSorts, DatabaseEventUpdateDatabaseSetting } from '@/services/backend/events/flowy-database2'; -import { Err, Ok, Result } from 'ts-results'; -import { nanoid } from 'nanoid'; -import type { IDatabaseSort } from '$app_reducers/database/slice'; - -export class SortBackendService { - constructor(public readonly viewId: string) {} - - getSorts = async (): Promise<Result<IDatabaseSort[], FlowyError>> => { - const payload = DatabaseViewIdPB.fromObject({ - value: this.viewId, - }); - - const res = await DatabaseEventGetAllSorts(payload); - - if (res.ok) { - return Ok( - res.val.items.map<IDatabaseSort>((o) => ({ - id: o.id, - fieldId: o.field_id, - fieldType: o.field_type, - order: o.condition, - })) - ); - } else { - return Err(res.val); - } - }; - - addSort = async (fieldId: string, fieldType: FieldType, order: SortConditionPB) => { - const id = nanoid(4); - - await DatabaseEventUpdateDatabaseSetting( - DatabaseSettingChangesetPB.fromObject({ - view_id: this.viewId, - update_sort: UpdateSortPayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - sort_id: id, - condition: order, - }), - }) - ); - return id; - }; - - updateSort = (sortId: string, fieldId: string, fieldType: FieldType, order: SortConditionPB) => { - return DatabaseEventUpdateDatabaseSetting( - DatabaseSettingChangesetPB.fromObject({ - view_id: this.viewId, - update_sort: UpdateSortPayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - sort_id: sortId, - condition: order, - }), - }) - ); - }; - - removeSort = (fieldId: string, fieldType: FieldType, sortId: string) => { - return DatabaseEventUpdateDatabaseSetting( - DatabaseSettingChangesetPB.fromObject({ - view_id: this.viewId, - delete_sort: DeleteSortPayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - field_type: fieldType, - sort_id: sortId, - }), - }) - ); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/sort/sort_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/sort/sort_controller.ts deleted file mode 100644 index 2bf7dcde90..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/sort/sort_controller.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { FieldType, SortConditionPB } from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { IDatabaseSort } from '$app_reducers/database/slice'; -import { SortBackendService } from '$app/stores/effects/database/sort/sort_bd_svc'; - -export class SortController { - sortService: SortBackendService; - notifier: SortNotifier; - - constructor(public readonly viewId: string) { - this.sortService = new SortBackendService(viewId); - this.notifier = new SortNotifier(); - } - - initialize = async () => { - await this.readSorts(); - }; - - readSorts = async () => { - const result = await this.sortService.getSorts(); - - if (result.ok) { - this.notifier.sorts = result.val; - } - }; - - addSort = async (fieldId: string, fieldType: FieldType, sort: SortConditionPB) => { - const id = await this.sortService.addSort(fieldId, fieldType, sort); - - await this.readSorts(); - return id; - }; - - updateSort = async (sortId: string, fieldId: string, fieldType: FieldType, sort: SortConditionPB) => { - const result = await this.sortService.updateSort(sortId, fieldId, fieldType, sort); - - if (result.ok) { - await this.readSorts(); - } - }; - - removeSort = async (fieldId: string, fieldType: FieldType, sortId: string) => { - const result = await this.sortService.removeSort(fieldId, fieldType, sortId); - - if (result.ok) { - await this.readSorts(); - } - }; - - subscribe = (callbacks: { onSortChanged?: (sorts: IDatabaseSort[]) => void }) => { - if (callbacks.onSortChanged) { - this.notifier.observer?.subscribe(callbacks.onSortChanged); - } - }; - - dispose = () => { - this.notifier.unsubscribe(); - }; -} - -class SortNotifier extends ChangeNotifier<IDatabaseSort[]> { - private _sorts: IDatabaseSort[] = []; - - get sorts(): IDatabaseSort[] { - return this._sorts; - } - - set sorts(value: IDatabaseSort[]) { - this._sorts = value; - this.notify(value); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts deleted file mode 100644 index 072dae5189..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/database_view_cache.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { DatabaseViewRowsObserver } from './view_row_observer'; -import { RowCache, RowInfo } from '../row/row_cache'; -import { FieldController } from '../field/field_controller'; -import { RowMetaPB } from '@/services/backend'; - -export class DatabaseViewCache { - private readonly rowsObserver: DatabaseViewRowsObserver; - private readonly rowCache: RowCache; - - constructor(public readonly viewId: string, fieldController: FieldController) { - this.rowsObserver = new DatabaseViewRowsObserver(viewId); - this.rowCache = new RowCache(viewId, () => fieldController.fieldInfos); - fieldController.subscribe({ - onNumOfFieldsChanged: (fieldInfos) => { - fieldInfos.forEach((fieldInfo) => { - this.rowCache.onFieldUpdated(fieldInfo); - }); - this.rowCache.onNumberOfFieldsUpdated(fieldInfos); - }, - }); - } - - initializeWithRows = (rows: RowMetaPB[]) => { - this.rowCache.initializeRows(rows); - }; - - get rowInfos(): readonly RowInfo[] { - return this.rowCache.rows; - } - - getRowCache = () => { - return this.rowCache; - }; - - dispose = async () => { - await this.rowsObserver.unsubscribe(); - await this.rowCache.dispose(); - }; - - initialize = async () => { - await this.rowsObserver.subscribe({ - onRowsVisibilityChanged: (result) => { - if (result.ok) { - this.rowCache.applyRowsVisibility(result.val); - } - }, - onNumberOfRowsChanged: (result) => { - if (result.ok) { - this.rowCache.applyRowsChanged(result.val); - } - }, - onReorderRows: (result) => { - if (result.ok) { - this.rowCache.applyReorderRows(result.val); - } - }, - onReorderSingleRow: (result) => { - if (result.ok) { - this.rowCache.applyReorderSingleRow(result.val); - } - }, - }); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts deleted file mode 100644 index 2b7a67e4f1..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/view/view_row_observer.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Ok, Result } from 'ts-results'; -import { - DatabaseNotification, - FlowyError, - ReorderAllRowsPB, - ReorderSingleRowPB, - RowsChangePB, - RowsVisibilityChangePB, -} from '@/services/backend'; -import { ChangeNotifier } from '$app/utils/change_notifier'; -import { DatabaseNotificationObserver } from '../notifications/observer'; - -export type RowsVisibilityNotifyValue = Result<RowsVisibilityChangePB, FlowyError>; -export type RowsNotifyValue = Result<RowsChangePB, FlowyError>; -export type ReorderRowsNotifyValue = Result<string[], FlowyError>; -export type ReorderSingleRowNotifyValue = Result<ReorderSingleRowPB, FlowyError>; - -export class DatabaseViewRowsObserver { - private rowsVisibilityNotifier = new ChangeNotifier<RowsVisibilityNotifyValue>(); - private rowsNotifier = new ChangeNotifier<RowsNotifyValue>(); - private reorderRowsNotifier = new ChangeNotifier<ReorderRowsNotifyValue>(); - private reorderSingleRowNotifier = new ChangeNotifier<ReorderSingleRowNotifyValue>(); - - private _listener?: DatabaseNotificationObserver; - - constructor(public readonly viewId: string) {} - - subscribe = async (callbacks: { - onRowsVisibilityChanged?: (value: RowsVisibilityNotifyValue) => void; - onNumberOfRowsChanged?: (value: RowsNotifyValue) => void; - onReorderRows?: (value: ReorderRowsNotifyValue) => void; - onReorderSingleRow?: (value: ReorderSingleRowNotifyValue) => void; - }) => { - // - this.rowsVisibilityNotifier.observer?.subscribe(callbacks.onRowsVisibilityChanged); - this.rowsNotifier.observer?.subscribe(callbacks.onNumberOfRowsChanged); - this.reorderRowsNotifier.observer?.subscribe(callbacks.onReorderRows); - this.reorderSingleRowNotifier.observer?.subscribe(callbacks.onReorderSingleRow); - - this._listener = new DatabaseNotificationObserver({ - id: this.viewId, - parserHandler: (notification, result) => { - switch (notification) { - case DatabaseNotification.DidUpdateViewRowsVisibility: - if (result.ok) { - this.rowsVisibilityNotifier.notify(Ok(RowsVisibilityChangePB.deserializeBinary(result.val))); - } else { - this.rowsVisibilityNotifier.notify(result); - } - - break; - case DatabaseNotification.DidUpdateViewRows: - if (result.ok) { - this.rowsNotifier.notify(Ok(RowsChangePB.deserializeBinary(result.val))); - } else { - this.rowsNotifier.notify(result); - } - - break; - case DatabaseNotification.DidReorderRows: - if (result.ok) { - this.reorderRowsNotifier.notify(Ok(ReorderAllRowsPB.deserializeBinary(result.val).row_orders)); - } else { - this.reorderRowsNotifier.notify(result); - } - - break; - case DatabaseNotification.DidReorderSingleRow: - if (result.ok) { - this.reorderSingleRowNotifier.notify(Ok(ReorderSingleRowPB.deserializeBinary(result.val))); - } else { - this.reorderSingleRowNotifier.notify(result); - } - - break; - default: - break; - } - }, - }); - await this._listener.start(); - }; - - unsubscribe = async () => { - this.rowsVisibilityNotifier.unsubscribe(); - this.reorderRowsNotifier.unsubscribe(); - this.rowsNotifier.unsubscribe(); - this.reorderSingleRowNotifier.unsubscribe(); - await this._listener?.stop(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts deleted file mode 100644 index 481c7de359..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { nanoid } from '@reduxjs/toolkit'; -import { - AppearanceSettingsPB, - UserEventGetAppearanceSetting, - UserEventGetUserProfile, - UserEventGetUserSetting, - UserEventSetAppearanceSetting, - UserEventSignInWithEmailPassword, - UserEventSignOut, - UserEventSignUp, - UserEventUpdateUserProfile, -} from '@/services/backend/events/flowy-user'; -import { - CreateWorkspacePayloadPB, - SignInPayloadPB, - SignUpPayloadPB, - UpdateUserProfilePayloadPB, - WorkspacePB, - WorkspaceSettingPB, -} from '@/services/backend'; -import { - FolderEventCreateWorkspace, - FolderEventGetCurrentWorkspaceSetting, - FolderEventReadCurrentWorkspace, -} from '@/services/backend/events/flowy-folder'; - -export class UserBackendService { - constructor(public readonly userId: number) {} - - static getUserProfile = () => { - return UserEventGetUserProfile(); - }; - - updateUserProfile = (params: { name?: string; password?: string; email?: string; openAIKey?: string }) => { - const payload = UpdateUserProfilePayloadPB.fromObject({ id: this.userId }); - - if (params.name !== undefined) { - payload.name = params.name; - } - - if (params.password !== undefined) { - payload.password = params.password; - } - - if (params.email !== undefined) { - payload.email = params.email; - } - - // if (params.openAIKey !== undefined) { - // } - return UserEventUpdateUserProfile(payload); - }; - - getCurrentWorkspaceSetting = async (): Promise<WorkspaceSettingPB> => { - const result = await FolderEventGetCurrentWorkspaceSetting(); - - if (result.ok) { - return result.val; - } else { - throw new Error(result.val.msg); - } - }; - - getWorkspaces = () => { - return FolderEventReadCurrentWorkspace(); - }; - - createWorkspace = async (params: { name: string; desc: string }): Promise<WorkspacePB> => { - const payload = CreateWorkspacePayloadPB.fromObject({ name: params.name, desc: params.desc }); - const result = await FolderEventCreateWorkspace(payload); - - if (result.ok) { - return result.val; - } else { - throw new Error(result.val.msg); - } - }; - - signOut = () => { - return UserEventSignOut(); - }; - - setAppearanceSettings = (params: ReturnType<typeof AppearanceSettingsPB.prototype.toObject>) => { - const payload = AppearanceSettingsPB.fromObject(params); - - return UserEventSetAppearanceSetting(payload); - }; - - getAppearanceSettings = () => { - return UserEventGetAppearanceSetting(); - }; - - getStorageSettings = () => { - return UserEventGetUserSetting(); - }; -} - -export class AuthBackendService { - signIn = (params: { email: string; password: string }) => { - const payload = SignInPayloadPB.fromObject({ email: params.email, password: params.password }); - - return UserEventSignInWithEmailPassword(payload); - }; - - signUp = (params: { name: string; email: string; password: string }) => { - const deviceId = nanoid(8); - const payload = SignUpPayloadPB.fromObject({ - name: params.name, - email: params.email, - password: params.password, - device_id: deviceId, - }); - - return UserEventSignUp(payload); - }; - - signOut = () => { - return UserEventSignOut(); - }; - - autoSignUp = () => { - const password = 'AppFlowy123@'; - const email = nanoid(4) + '@appflowy.io'; - - return this.signUp({ name: 'Me', email: email, password: password }); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts deleted file mode 100644 index ec4ea12c49..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_setting_controller.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { UserBackendService } from '$app/stores/effects/user/user_bd_svc'; -import { AppearanceSettingsPB } from '@/services/backend'; -import { Theme, ThemeMode, UserSetting } from '$app/stores/reducers/current-user/slice'; - -export class UserSettingController { - private readonly backendService: UserBackendService; - constructor(private userId: number) { - this.backendService = new UserBackendService(userId); - } - - getStorageSettings = async () => { - const userSetting = await this.backendService.getStorageSettings(); - - if (userSetting.ok) { - return userSetting.val; - } - - return {}; - }; - - getAppearanceSetting = async (): Promise<Partial<UserSetting> | undefined> => { - const appearanceSetting = await this.backendService.getAppearanceSettings(); - - if (appearanceSetting.ok) { - const res = appearanceSetting.val; - const { locale, theme = Theme.Default, theme_mode = ThemeMode.Light } = res; - let language = 'en'; - - if (locale.language_code && locale.country_code) { - language = `${locale.language_code}-${locale.country_code}`; - } else if (locale.language_code) { - language = locale.language_code; - } - - return { - themeMode: theme_mode, - theme: theme as Theme, - language: language, - }; - } - - return; - }; - - setAppearanceSetting = async (params: ReturnType<typeof AppearanceSettingsPB.prototype.toObject>) => { - const res = await this.backendService.setAppearanceSettings(params); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/notifications/observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/notifications/observer.ts deleted file mode 100644 index 4633328944..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/notifications/observer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { OnNotificationError, AFNotificationObserver } from '@/services/backend/notifications'; -import { FlowyError, FolderNotification } from '@/services/backend'; -import { Result } from 'ts-results'; -import { WorkspaceNotificationParser } from './parser'; - -export type ParserHandler = (notification: FolderNotification, payload: Result<Uint8Array, FlowyError>) => void; - -export class WorkspaceNotificationObserver extends AFNotificationObserver<FolderNotification> { - constructor(params: { id?: string; parserHandler: ParserHandler; onError?: OnNotificationError }) { - const parser = new WorkspaceNotificationParser({ - callback: params.parserHandler, - id: params.id, - }); - - super(parser); - } -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/notifications/parser.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/notifications/parser.ts deleted file mode 100644 index cab08feee3..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/notifications/parser.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { NotificationParser, OnNotificationError } from '@/services/backend/notifications'; -import { FlowyError, FolderNotification } from '@/services/backend'; -import { Result } from 'ts-results'; - -declare type WorkspaceNotificationCallback = (ty: FolderNotification, payload: Result<Uint8Array, FlowyError>) => void; - -export class WorkspaceNotificationParser extends NotificationParser<FolderNotification> { - constructor(params: { id?: string; callback: WorkspaceNotificationCallback; onError?: OnNotificationError }) { - super( - params.callback, - (ty) => { - const notification = FolderNotification[ty]; - - if (isFolderNotification(notification)) { - return FolderNotification[notification]; - } else { - return FolderNotification.Unknown; - } - }, - params.id - ); - } -} - -const isFolderNotification = (notification: string): notification is keyof typeof FolderNotification => { - return Object.values(FolderNotification).indexOf(notification) !== -1; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/page/page_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/page/page_bd_svc.ts deleted file mode 100644 index 6dc5752da0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/page/page_bd_svc.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { - FolderEventCreateView, - FolderEventUpdateView, - FolderEventDeleteView, - FolderEventDuplicateView, - FolderEventCloseView, - FolderEventImportData, - ViewIdPB, - CreateViewPayloadPB, - UpdateViewPayloadPB, - RepeatedViewIdPB, - ViewPB, - ImportPB, - MoveNestedViewPayloadPB, - FolderEventMoveNestedView, - ViewIconPB, - UpdateViewIconPayloadPB, - FolderEventUpdateViewIcon, - FolderEventCreateOrphanView, - CreateOrphanViewPayloadPB, - FolderEventGetView, -} from '@/services/backend/events/flowy-folder'; -import { Page, PageIcon } from '$app_reducers/pages/slice'; - -export class PageBackendService { - constructor() { - // - } - - getPage = async (viewId: string) => { - const payload = new ViewIdPB({ - value: viewId, - }); - - return FolderEventGetView(payload); - }; - - movePage = async (params: { viewId: string; parentId: string; prevId?: string }) => { - const payload = new MoveNestedViewPayloadPB({ - view_id: params.viewId, - new_parent_id: params.parentId, - prev_view_id: params.prevId, - }); - - return FolderEventMoveNestedView(payload); - }; - - createPage = async (params: ReturnType<typeof CreateViewPayloadPB.prototype.toObject>) => { - const payload = CreateViewPayloadPB.fromObject(params); - - return FolderEventCreateView(payload); - }; - - updatePage = async (page: { id: string } & Partial<Page>) => { - const payload = new UpdateViewPayloadPB(); - - payload.view_id = page.id; - if (page.name !== undefined) { - payload.name = page.name; - } - - return FolderEventUpdateView(payload); - }; - - updatePageIcon = async (viewId: string, icon?: PageIcon) => { - const payload = new UpdateViewIconPayloadPB({ - view_id: viewId, - icon: icon - ? new ViewIconPB({ - ty: icon.ty, - value: icon.value, - }) - : undefined, - }); - - return FolderEventUpdateViewIcon(payload); - }; - - deletePage = async (viewId: string) => { - const payload = new RepeatedViewIdPB({ - items: [viewId], - }); - - return FolderEventDeleteView(payload); - }; - - duplicatePage = async (params: ReturnType<typeof ViewPB.prototype.toObject>) => { - const payload = ViewPB.fromObject(params); - - return FolderEventDuplicateView(payload); - }; - - createOrphanPage = async (params: ReturnType<typeof CreateOrphanViewPayloadPB.prototype.toObject>) => { - const payload = CreateOrphanViewPayloadPB.fromObject(params); - - return FolderEventCreateOrphanView(payload); - }; - - closePage = async (viewId: string) => { - const payload = new ViewIdPB({ - value: viewId, - }); - - return FolderEventCloseView(payload); - }; - - importData = async (params: ReturnType<typeof ImportPB.prototype.toObject>) => { - const payload = ImportPB.fromObject(params); - - return FolderEventImportData(payload); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/page/page_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/page/page_controller.ts deleted file mode 100644 index 3ff5ac9530..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/page/page_controller.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { ViewLayoutPB, ViewPB } from '@/services/backend'; -import { PageBackendService } from '$app/stores/effects/workspace/page/page_bd_svc'; -import { WorkspaceObserver } from '$app/stores/effects/workspace/workspace_observer'; -import { Page, PageIcon, parserViewPBToPage } from '$app_reducers/pages/slice'; - -export class PageController { - private readonly backendService: PageBackendService = new PageBackendService(); - - private readonly observer: WorkspaceObserver = new WorkspaceObserver(); - constructor(private readonly id: string) { - // - } - - dispose = async () => { - await this.observer.unsubscribe(); - }; - - createPage = async (params: { name: string; layout: ViewLayoutPB }): Promise<string> => { - const result = await this.backendService.createPage({ - name: params.name, - layout: params.layout, - parent_view_id: this.id, - }); - - if (result.ok) { - return result.val.id; - } - - return Promise.reject(result.err); - }; - - movePage = async (params: { parentId: string; prevId?: string }): Promise<void> => { - const result = await this.backendService.movePage({ - viewId: this.id, - parentId: params.parentId, - prevId: params.prevId, - }); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - getChildPages = async (): Promise<Page[]> => { - const result = await this.backendService.getPage(this.id); - - if (result.ok) { - return result.val.child_views.map(parserViewPBToPage); - } - - return []; - }; - - getPage = async (id?: string) => { - const result = await this.backendService.getPage(id || this.id); - - if (result.ok) { - return parserViewPBToPage(result.val); - } - - return Promise.reject(result.val); - }; - - getParentPage = async (): Promise<Page> => { - const page = await this.getPage(); - const parentPageId = page.parentId; - - return this.getPage(parentPageId); - }; - - subscribe = async (callbacks: { onPageChanged?: (page: Page, children: Page[]) => void }) => { - const didUpdateView = (payload: Uint8Array) => { - const res = ViewPB.deserializeBinary(payload); - const page = parserViewPBToPage(res); - - const childPages = res.child_views.map(parserViewPBToPage); - - callbacks.onPageChanged?.(page, childPages); - }; - - await this.observer.subscribeView(this.id, { - didUpdateView, - }); - }; - - unsubscribe = async () => { - await this.observer.unsubscribe(); - }; - - updatePage = async (page: { id: string } & Partial<Page>) => { - const result = await this.backendService.updatePage(page); - - if (result.ok) { - return result.val.toObject(); - } - - return Promise.reject(result.err); - }; - - updatePageIcon = async (icon?: PageIcon) => { - const result = await this.backendService.updatePageIcon(this.id, icon); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - deletePage = async () => { - const result = await this.backendService.deletePage(this.id); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - duplicatePage = async () => { - const page = await this.getPage(); - const result = await this.backendService.duplicatePage(page); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - createOrphanPage = async (params: { name: string; layout: ViewLayoutPB }): Promise<Page> => { - const result = await this.backendService.createOrphanPage({ - view_id: this.id, - name: params.name, - layout: params.layout, - }); - - if (result.ok) { - return parserViewPBToPage(result.val); - } - - return Promise.reject(result.val); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/trash/bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/trash/bd_svc.ts deleted file mode 100644 index e57d74107b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/trash/bd_svc.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { - TrashIdPB, - RepeatedTrashIdPB, - FolderEventListTrashItems, - FolderEventRestoreTrashItem, - FolderEventPermanentlyDeleteTrashItem, - FolderEventPermanentlyDeleteAllTrashItem, - FolderEventRecoverAllTrashItems, -} from '@/services/backend/events/flowy-folder'; - -export class TrashBackendService { - constructor() { - // - } - - getTrash = async () => { - return FolderEventListTrashItems(); - }; - - putback = async (id: string) => { - const payload = new TrashIdPB({ - id, - }); - - return FolderEventRestoreTrashItem(payload); - }; - - delete = async (ids: string[]) => { - const items = ids.map((id) => new TrashIdPB({ id })); - const payload = new RepeatedTrashIdPB({ - items, - }); - - return FolderEventPermanentlyDeleteTrashItem(payload); - }; - - deleteAll = async () => { - return FolderEventPermanentlyDeleteAllTrashItem(); - }; - - restoreAll = async () => { - return FolderEventRecoverAllTrashItems(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/trash/controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/trash/controller.ts deleted file mode 100644 index 86563a0ab7..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/trash/controller.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { TrashBackendService } from '$app/stores/effects/workspace/trash/bd_svc'; -import { WorkspaceObserver } from '$app/stores/effects/workspace/workspace_observer'; -import { RepeatedTrashPB, TrashPB } from '@/services/backend'; - -export class TrashController { - private readonly observer: WorkspaceObserver = new WorkspaceObserver(); - - private readonly backendService: TrashBackendService = new TrashBackendService(); - - subscribe = async (callbacks: { onTrashChanged?: (trash: TrashPB[]) => void }) => { - const didUpdateTrash = (payload: Uint8Array) => { - const res = RepeatedTrashPB.deserializeBinary(payload); - - callbacks.onTrashChanged?.(res.items); - }; - - await this.observer.subscribeTrash({ - didUpdateTrash, - }); - }; - - dispose = async () => { - await this.observer.unsubscribe(); - }; - getTrash = async () => { - const res = await this.backendService.getTrash(); - - if (res.ok) { - return res.val.items; - } - - return []; - }; - - putback = async (id: string) => { - const res = await this.backendService.putback(id); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }; - - delete = async (ids: string[]) => { - const res = await this.backendService.delete(ids); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }; - - deleteAll = async () => { - const res = await this.backendService.deleteAll(); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }; - - restoreAll = async () => { - const res = await this.backendService.restoreAll(); - - if (res.ok) { - return res.val; - } - - return Promise.reject(res.err); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_bd_svc.ts deleted file mode 100644 index fc4d81f58b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_bd_svc.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - FolderEventCreateWorkspace, - CreateWorkspacePayloadPB, - FolderEventDeleteWorkspace, - WorkspaceIdPB, - FolderEventReadWorkspaceViews, - FolderEventReadCurrentWorkspace, -} from '@/services/backend/events/flowy-folder'; -import { UserEventOpenWorkspace, UserWorkspaceIdPB } from '@/services/backend/events/flowy-user'; - -export class WorkspaceBackendService { - constructor() { - // - } - - createWorkspace = async (params: ReturnType<typeof CreateWorkspacePayloadPB.prototype.toObject>) => { - const { name, desc } = params; - const payload = CreateWorkspacePayloadPB.fromObject({ - name, - desc, - }); - - return FolderEventCreateWorkspace(payload); - }; - - openWorkspace = async (workspaceId: string) => { - const payload = new UserWorkspaceIdPB({ - workspace_id: workspaceId, - }); - - return UserEventOpenWorkspace(payload); - }; - - deleteWorkspace = async (workspaceId: string) => { - const payload = new WorkspaceIdPB({ - value: workspaceId, - }); - - return FolderEventDeleteWorkspace(payload); - }; - - getWorkspaces = async () => { - return FolderEventReadCurrentWorkspace(); - }; - - getCurrentWorkspace = async () => { - return FolderEventReadCurrentWorkspace(); - }; - - getChildPages = async (workspaceId: string) => { - const payload = new WorkspaceIdPB({ - value: workspaceId, - }); - - return FolderEventReadWorkspaceViews(payload); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_controller.ts deleted file mode 100644 index 56b7003c42..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_controller.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { WorkspaceBackendService } from '$app/stores/effects/workspace/workspace_bd_svc'; -import { WorkspaceObserver } from '$app/stores/effects/workspace/workspace_observer'; -import { CreateViewPayloadPB, RepeatedViewPB } from '@/services/backend'; -import { PageBackendService } from '$app/stores/effects/workspace/page/page_bd_svc'; -import { Page, parserViewPBToPage } from '$app_reducers/pages/slice'; - -export class WorkspaceController { - private readonly observer: WorkspaceObserver = new WorkspaceObserver(); - private readonly pageBackendService: PageBackendService; - private readonly backendService: WorkspaceBackendService; - constructor(private readonly workspaceId: string) { - this.pageBackendService = new PageBackendService(); - this.backendService = new WorkspaceBackendService(); - } - - dispose = async () => { - await this.observer.unsubscribe(); - }; - - open = async () => { - const result = await this.backendService.openWorkspace(this.workspaceId); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - delete = async () => { - const result = await this.backendService.deleteWorkspace(this.workspaceId); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - subscribe = async (callbacks: { onChildPagesChanged?: (childPages: Page[]) => void }) => { - const didUpdateWorkspace = (payload: Uint8Array) => { - const res = RepeatedViewPB.deserializeBinary(payload).items; - - callbacks.onChildPagesChanged?.(res.map(parserViewPBToPage)); - }; - - await this.observer.subscribeWorkspace(this.workspaceId, { - didUpdateWorkspace, - }); - }; - - createView = async (params: ReturnType<typeof CreateViewPayloadPB.prototype.toObject>) => { - const result = await this.pageBackendService.createPage(params); - - if (result.ok) { - const view = result.val; - - return view; - } - - return Promise.reject(result.err); - }; - - getChildPages = async (): Promise<Page[]> => { - const result = await this.backendService.getChildPages(this.workspaceId); - - if (result.ok) { - return result.val.items.map(parserViewPBToPage); - } - - return []; - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_manager_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_manager_controller.ts deleted file mode 100644 index 67bfdd92ee..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_manager_controller.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { WorkspaceBackendService } from './workspace_bd_svc'; -import { CreateWorkspacePayloadPB } from '@/services/backend'; -import { WorkspaceItem } from '$app_reducers/workspace/slice'; -import { WorkspaceObserver } from '$app/stores/effects/workspace/workspace_observer'; - -export class WorkspaceManagerController { - private readonly observer: WorkspaceObserver; - private readonly backendService: WorkspaceBackendService = new WorkspaceBackendService(); - private onWorkspacesChanged?: (data: { workspaces: WorkspaceItem[]; currentWorkspace: WorkspaceItem }) => void; - - constructor() { - this.observer = new WorkspaceObserver(); - } - - subscribe = async (callbacks: { - onWorkspacesChanged?: (data: { workspaces: WorkspaceItem[]; currentWorkspace: WorkspaceItem }) => void; - }) => { - // this.observer.subscribeWorkspaces(this.didCreateWorkspace); - this.onWorkspacesChanged = callbacks.onWorkspacesChanged; - }; - - createWorkspace = async (params: ReturnType<typeof CreateWorkspacePayloadPB.prototype.toObject>) => { - const result = await this.backendService.createWorkspace(params); - - if (result.ok) { - return result.val; - } - - return Promise.reject(result.err); - }; - - getWorkspaces = async (): Promise<WorkspaceItem[]> => { - const result = await this.backendService.getWorkspaces(); - - if (result.ok) { - const item = result.val; - - return [ - { - id: item.id, - name: item.name, - }, - ]; - } - - return []; - }; - - getCurrentWorkspace = async (): Promise<WorkspaceItem | null> => { - const result = await this.backendService.getCurrentWorkspace(); - - if (result.ok) { - const workspace = result.val; - - return { - id: workspace.id, - name: workspace.name, - }; - } - - return null; - }; - - dispose = async () => { - await this.observer.unsubscribe(); - }; - - private didCreateWorkspace = () => { - // const data = RepeatedWorkspacePB.deserializeBinary(payload); - // onWorkspacesChanged(data.toObject().items); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_observer.ts deleted file mode 100644 index 97f27b585c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/workspace/workspace_observer.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { FolderNotification } from '@/services/backend'; -import { WorkspaceNotificationObserver } from '$app/stores/effects/workspace/notifications/observer'; - -export class WorkspaceObserver { - private listener?: WorkspaceNotificationObserver; - constructor() { - // - } - - subscribeWorkspaces = async (callback: (payload: Uint8Array) => void) => { - this.listener = new WorkspaceNotificationObserver({ - parserHandler: (notification, result) => { - switch (notification) { - case FolderNotification.DidCreateWorkspace: - if (!result.ok) break; - callback(result.val); - break; - default: - break; - } - }, - }); - await this.listener.start(); - }; - - subscribeWorkspace = async ( - workspaceId: string, - callbacks: { - didUpdateChildViews?: (payload: Uint8Array) => void; - didUpdateWorkspace?: (payload: Uint8Array) => void; - didDeleteWorkspace?: (payload: Uint8Array) => void; - } - ) => { - this.listener = new WorkspaceNotificationObserver({ - id: workspaceId, - parserHandler: (notification, result) => { - switch (notification) { - case FolderNotification.DidUpdateWorkspaceViews: - if (!result.ok) break; - callbacks.didUpdateWorkspace?.(result.val); - break; - case FolderNotification.DidUpdateChildViews: - if (!result.ok) break; - callbacks.didUpdateChildViews?.(result.val); - break; - // case FolderNotification.DidDeleteWorkspace: - // if (!result.ok) break; - // callbacks.didDeleteWorkspace(result.val); - // break; - default: - break; - } - }, - }); - await this.listener.start(); - }; - - subscribeView = async ( - viewId: string, - callbacks: { - didUpdateChildViews?: (payload: Uint8Array) => void; - didUpdateView?: (payload: Uint8Array) => void; - } - ) => { - this.listener = new WorkspaceNotificationObserver({ - id: viewId, - parserHandler: (notification, result) => { - switch (notification) { - case FolderNotification.DidUpdateChildViews: - if (!result.ok) break; - callbacks.didUpdateChildViews?.(result.val); - break; - case FolderNotification.DidUpdateView: - if (!result.ok) break; - callbacks.didUpdateView?.(result.val); - break; - default: - break; - } - }, - }); - await this.listener.start(); - }; - - subscribeTrash = async (callbacks: { didUpdateTrash: (payload: Uint8Array) => void }) => { - this.listener = new WorkspaceNotificationObserver({ - parserHandler: (notification, result) => { - switch (notification) { - case FolderNotification.DidUpdateTrash: - if (!result.ok) break; - callbacks.didUpdateTrash(result.val); - break; - default: - break; - } - }, - }); - await this.listener.start(); - }; - - unsubscribe = async () => { - await this.listener?.stop(); - }; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/board/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/board/slice.ts deleted file mode 100644 index dff96c6a31..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/board/slice.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; - -const initialState = ''; - -export const boardSlice = createSlice({ - name: 'board', - initialState: initialState as string, - reducers: { - setGroupingFieldId: (state, action: PayloadAction<{ fieldId: string }>) => { - return action.payload.fieldId; - }, - }, -}); - -export const boardActions = boardSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/database/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/database/slice.ts deleted file mode 100644 index 26525d23b0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/database/slice.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { FieldType } from '@/services/backend/models/flowy-database2/field_entities'; -import { DateFormatPB, NumberFormatPB, SelectOptionColorPB, SortConditionPB, TimeFormatPB } from '@/services/backend'; - -export interface ISelectOption { - selectOptionId: string; - title: string; - color: SelectOptionColorPB; -} - -export interface ISelectOptionType { - selectOptions: ISelectOption[]; -} - -export interface IDateType { - dateFormat: DateFormatPB; - timeFormat: TimeFormatPB; - // includeTime: boolean; -} - -export interface INumberType { - numberFormat: NumberFormatPB; -} - -export interface IDatabaseField { - fieldId: string; - title: string; - visible: boolean; - width: number; - fieldType: FieldType; - fieldOptions?: ISelectOptionType | IDateType | INumberType; -} - -export interface IDatabaseColumn { - fieldId: string; - sort: 'none' | 'asc' | 'desc'; - visible: boolean; -} - -export interface IDatabaseRow { - rowId: string; -} - -export type DatabaseFieldMap = { [keys: string]: IDatabaseField }; - -export type TDatabaseOperators = - | 'contains' - | 'doesNotContain' - | 'endsWith' - | 'startWith' - | 'is' - | 'isNot' - | 'isEmpty' - | 'isNotEmpty' - | 'isComplete' - | 'isIncomplted'; - -export type TSupportedOperatorsByType = { [keys: number]: TDatabaseOperators[] }; - -export const SupportedOperatorsByType: TSupportedOperatorsByType = { - [FieldType.RichText]: ['contains', 'doesNotContain', 'endsWith', 'startWith', 'is', 'isNot', 'isEmpty', 'isNotEmpty'], - [FieldType.SingleSelect]: ['is', 'isNot', 'isEmpty', 'isNotEmpty'], - [FieldType.MultiSelect]: ['contains', 'doesNotContain', 'isEmpty', 'isNotEmpty'], - [FieldType.Checkbox]: ['is'], - [FieldType.Checklist]: ['isComplete', 'isIncomplted'], -}; - -export interface IDatabaseFilter { - id?: string; - fieldId: string; - fieldType: FieldType; - logicalOperator: 'and' | 'or'; - operator: TDatabaseOperators; - value: string[] | string | boolean; -} - -export interface IDatabaseSort { - id?: string; - fieldId: string; - fieldType: FieldType; - order: SortConditionPB; -} - -export interface IDatabase { - title: string; - fields: DatabaseFieldMap; - rows: IDatabaseRow[]; - columns: IDatabaseColumn[]; - filters: IDatabaseFilter[]; - sort: IDatabaseSort[]; -} - -const initialState: IDatabase = { - title: 'Database One', - columns: [], - fields: {}, - rows: [], - filters: [], - sort: [], -}; - -export const databaseSlice = createSlice({ - name: 'database', - initialState: initialState, - reducers: { - clear: () => { - return initialState; - }, - - updateRows: (state, action: PayloadAction<{ rows: IDatabaseRow[] }>) => { - return { - ...state, - rows: action.payload.rows, - }; - }, - - updateFields: (state, action: PayloadAction<{ fields: DatabaseFieldMap }>) => { - return { - ...state, - fields: action.payload.fields, - }; - }, - - updateColumns: (state, action: PayloadAction<{ columns: IDatabaseColumn[] }>) => { - return { - ...state, - columns: action.payload.columns, - }; - }, - - updateTitle: (state, action: PayloadAction<{ title: string }>) => { - state.title = action.payload.title; - }, - - updateField: (state, action: PayloadAction<{ field: IDatabaseField }>) => { - const { field } = action.payload; - - state.fields[field.fieldId] = field; - }, - - changeWidth: (state, action: PayloadAction<{ fieldId: string; width: number }>) => { - const { fieldId, width } = action.payload; - - state.fields[fieldId].width = width; - }, - - updateFilters: (state, action: PayloadAction<{ filters: IDatabaseFilter[] }>) => { - state.filters = action.payload.filters; - }, - - updateSorts: (state, action: PayloadAction<{ sorts: IDatabaseSort[] }>) => { - state.sort = action.payload.sorts; - }, - }, -}); - -export const databaseActions = databaseSlice.actions; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts index d647bcf934..0e8c9cc9ea 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/async_actions.ts @@ -1,7 +1,7 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { RootState } from '$app/stores/store'; -import { PageController } from '$app/stores/effects/workspace/page/page_controller'; -import { PageIcon, pagesActions } from '$app_reducers/pages/slice'; +import { pagesActions } from '$app_reducers/pages/slice'; +import { movePage, updatePage } from '$app/application/folder/page.service'; export const movePageThunk = createAsyncThunk( 'pages/movePage', @@ -51,16 +51,17 @@ export const movePageThunk = createAsyncThunk( } } - const controller = new PageController(sourceId); - - await controller.movePage({ parentId, prevId }); + await movePage({ + view_id: sourceId, + new_parent_id: parentId, + prev_view_id: prevId, + }); } ); export const updatePageName = createAsyncThunk( 'pages/updateName', async (payload: { id: string; name: string }, thunkAPI) => { - const controller = new PageController(payload.id); const { dispatch, getState } = thunkAPI; const { pageMap } = (getState() as RootState).pages; const { id, name } = payload; @@ -74,15 +75,9 @@ export const updatePageName = createAsyncThunk( name, }) ); - await controller.updatePage({ - id: payload.id, - name: payload.name, + await updatePage({ + id, + name, }); } ); - -export const updatePageIcon = createAsyncThunk('pages/updateIcon', async (payload: { id: string; icon?: PageIcon }) => { - const controller = new PageController(payload.id); - - await controller.updatePageIcon(payload.icon); -}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts index a9e0df8252..090382de70 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/workspace/slice.ts @@ -28,34 +28,6 @@ export const workspaceSlice = createSlice({ ) => { return action.payload; }, - - onWorkspacesChanged: ( - state, - action: PayloadAction<{ - workspaces: WorkspaceItem[]; - currentWorkspace: WorkspaceItem | null; - }> - ) => { - return action.payload; - }, - - onWorkspaceChanged: (state, action: PayloadAction<WorkspaceItem>) => { - const { id } = action.payload; - const index = state.workspaces.findIndex((workspace) => workspace.id === id); - - if (index !== -1) { - state.workspaces[index] = action.payload; - } - }, - - onWorkspaceDeleted: (state, action: PayloadAction<string>) => { - const id = action.payload; - const index = state.workspaces.findIndex((workspace) => workspace.id === id); - - if (index !== -1) { - state.workspaces.splice(index, 1); - } - }, }, }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts index 6ee441ad33..269f46884c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/store.ts @@ -10,8 +10,6 @@ import { import { pagesSlice } from './reducers/pages/slice'; import { currentUserSlice } from './reducers/current-user/slice'; import { workspaceSlice } from './reducers/workspace/slice'; -import { databaseSlice } from './reducers/database/slice'; -import { boardSlice } from './reducers/board/slice'; import { errorSlice } from './reducers/error/slice'; import { sidebarSlice } from '$app_reducers/sidebar/slice'; import { trashSlice } from '$app_reducers/trash/slice'; @@ -24,8 +22,6 @@ const store = configureStore({ reducer: { [pagesSlice.name]: pagesSlice.reducer, [currentUserSlice.name]: currentUserSlice.reducer, - [databaseSlice.name]: databaseSlice.reducer, - [boardSlice.name]: boardSlice.reducer, [workspaceSlice.name]: workspaceSlice.reducer, [errorSlice.name]: errorSlice.reducer, [sidebarSlice.name]: sidebarSlice.reducer, diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts index 41ce9c7294..78a0308a1b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/mui.ts @@ -42,7 +42,7 @@ export const getDesignTokens = (mode: ThemeMode): ThemeOptions => { defaultProps: { sx: { '&.Mui-selected:hover': { - backgroundColor: 'var(--fill-list-active)', + backgroundColor: 'var(--fill-list-hover)', }, }, }, @@ -52,7 +52,7 @@ export const getDesignTokens = (mode: ThemeMode): ThemeOptions => { backgroundColor: 'var(--fill-list-hover)', }, '&:active': { - backgroundColor: 'var(--fill-list-active)', + backgroundColor: 'var(--fill-list-hover)', }, borderRadius: '4px', padding: '2px', diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/BoardPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/BoardPage.tsx deleted file mode 100644 index 6066b00e7a..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/BoardPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { useParams } from 'react-router-dom'; -import { useEffect, useState } from 'react'; -import { Board } from '../components/board/Board'; -import { useAppSelector } from '$app/stores/store'; - -export const BoardPage = () => { - const params = useParams(); - const [viewId, setViewId] = useState(''); - const pagesStore = useAppSelector((state) => state.pages); - const page = useAppSelector((state) => (params.id ? state.pages.pageMap[params.id] : undefined)); - const [title, setTitle] = useState(''); - - useEffect(() => { - if (page) { - setViewId(page.id); - setTitle(page.name); - } - }, [params, pagesStore, page]); - - return ( - <div className='flex h-full flex-col gap-8 px-8 pt-8'> - {viewId?.length && <Board viewId={viewId} title={title} />} - </div> - ); -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/ConfirmAccountPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/ConfirmAccountPage.tsx deleted file mode 100644 index d06645b75c..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/ConfirmAccountPage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { ConfirmAccount } from '../components/auth/ConfirmAccount/ConfirmAccount'; - -export const ConfirmAccountPage = () => { - return <ConfirmAccount />; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/LoginPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/LoginPage.tsx deleted file mode 100644 index 72f38dc5ad..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/LoginPage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Login } from '../components/auth/Login/Login'; - -export const LoginPage = () => { - return <Login />; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/SignUpPage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/SignUpPage.tsx deleted file mode 100644 index e213d57d24..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/views/SignUpPage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { SignUp } from '../components/auth/SignUp/SignUp'; - -export const SignUpPage = () => { - return <SignUp />; -}; diff --git a/frontend/appflowy_tauri/src/main.tsx b/frontend/appflowy_tauri/src/main.tsx index 30b86f2d40..e53dc96c43 100644 --- a/frontend/appflowy_tauri/src/main.tsx +++ b/frontend/appflowy_tauri/src/main.tsx @@ -4,7 +4,5 @@ import App from './appflowy_app/App'; import './styles/tailwind.css'; import './styles/font.css'; import './styles/template.css'; -import './styles/Calendar.css'; -import './styles/switch.css'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(<App />); diff --git a/frontend/appflowy_tauri/src/styles/Calendar.css b/frontend/appflowy_tauri/src/styles/Calendar.css deleted file mode 100644 index 0ac995ed51..0000000000 --- a/frontend/appflowy_tauri/src/styles/Calendar.css +++ /dev/null @@ -1,141 +0,0 @@ -.react-calendar { - width: 250px; - max-width: 100%; - background: white; - line-height: 1.125em; -} - -.react-calendar--doubleView { - width: 700px; -} - -.react-calendar--doubleView .react-calendar__viewContainer { - display: flex; - margin: -0.5em; -} - -.react-calendar--doubleView .react-calendar__viewContainer > * { - width: 50%; - margin: 0.5em; -} - -.react-calendar, -.react-calendar *, -.react-calendar *:before, -.react-calendar *:after { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -.react-calendar button { - margin: 0; - border: 0; - outline: none; -} - -.react-calendar button:enabled:hover { - cursor: pointer; -} - -.react-calendar__navigation { - display: flex; - height: 44px; - margin-bottom: 1em; -} - -.react-calendar__navigation button { - min-width: 44px; - background: none; -} - -.react-calendar__navigation button:disabled { - background-color: #f0f0f0; -} - -.react-calendar__navigation button:enabled:hover, -.react-calendar__navigation button:enabled:focus { - background-color: #e6e6e6; -} - -.react-calendar__month-view__weekdays { - @apply mb-2 text-center text-xs uppercase text-text-caption; -} - -.react-calendar__month-view__weekdays abbr { - @apply no-underline; -} - -.react-calendar__month-view__weekdays__weekday { - padding: 0.5em; -} - -.react-calendar__month-view__weekNumbers .react-calendar__tile { - display: flex; - align-items: center; - justify-content: center; - font-size: 0.75em; - font-weight: 400; -} - -.react-calendar__month-view__days__day--weekend { - @apply text-text-link-default; -} - -.react-calendar__month-view__days__day--neighboringMonth { - @apply text-text-caption; -} - -.react-calendar__year-view .react-calendar__tile, -.react-calendar__decade-view .react-calendar__tile, -.react-calendar__century-view .react-calendar__tile { - padding: 2em 0.5em; -} - -.react-calendar__tile { - max-width: 100%; - background: none; - text-align: center; - line-height: 16px; - @apply rounded py-2; -} - -.react-calendar__tile:disabled { - background-color: #f0f0f0; -} - -.react-calendar__tile:enabled:hover, -.react-calendar__tile:enabled:focus { - background-color: #e6e6e6; -} - -.react-calendar__tile--now { - @apply bg-bg-base; -} - -.react-calendar__tile--now:enabled:hover, -.react-calendar__tile--now:enabled:focus { - @apply bg-bg-base; -} - -.react-calendar__tile--hasActive { - background: #76baff; -} - -.react-calendar__tile--hasActive:enabled:hover, -.react-calendar__tile--hasActive:enabled:focus { - background: #a9d4ff; -} - -.react-calendar__tile--active { - @apply bg-fill-default text-text-title; -} - -.react-calendar__tile--active:enabled:hover, -.react-calendar__tile--active:enabled:focus { - @apply bg-fill-list-hover; -} - -.react-calendar--selectRange .react-calendar__tile--hover { - @apply bg-bg-base; -} diff --git a/frontend/appflowy_tauri/src/styles/switch.css b/frontend/appflowy_tauri/src/styles/switch.css deleted file mode 100644 index 7af7142fe7..0000000000 --- a/frontend/appflowy_tauri/src/styles/switch.css +++ /dev/null @@ -1,58 +0,0 @@ -.form-switch { - display: inline-block; - cursor: pointer; - -webkit-tap-highlight-color: transparent; -} -.form-switch i { - position: relative; - display: inline-block; - margin-right: 0.5rem; - width: 46px; - height: 26px; - @apply bg-bg-base; - border-radius: 23px; - vertical-align: text-bottom; - transition: all 0.3s linear; -} -.form-switch i::before { - content: ''; - position: absolute; - left: 0; - width: 42px; - height: 22px; - background-color: #fff; - border-radius: 11px; - transform: translate3d(2px, 2px, 0) scale3d(1, 1, 1); - transition: all 0.25s linear; -} -.form-switch i::after { - content: ''; - position: absolute; - left: 0; - width: 22px; - height: 22px; - background-color: #fff; - border-radius: 11px; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.24); - transform: translate3d(2px, 2px, 0); - transition: all 0.2s ease-in-out; -} -.form-switch:active i::after { - width: 28px; - transform: translate3d(2px, 2px, 0); -} -.form-switch:active input:checked + i::after { - transform: translate3d(16px, 2px, 0); -} -.form-switch input { - display: none; -} -.form-switch input:checked + i { - @apply bg-fill-default; -} -.form-switch input:checked + i::before { - transform: translate3d(18px, 2px, 0) scale3d(0, 0, 0); -} -.form-switch input:checked + i::after { - transform: translate3d(22px, 2px, 0); -}