diff --git a/frontend/appflowy_tauri/.eslintrc.cjs b/frontend/appflowy_tauri/.eslintrc.cjs index 7223e5cb39..2047d4f962 100644 --- a/frontend/appflowy_tauri/.eslintrc.cjs +++ b/frontend/appflowy_tauri/.eslintrc.cjs @@ -15,20 +15,20 @@ module.exports = { plugins: ['@typescript-eslint', "react-hooks"], rules: { "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", + "react-hooks/exhaustive-deps": "error", '@typescript-eslint/adjacent-overload-signatures': 'error', '@typescript-eslint/no-empty-function': 'error', - '@typescript-eslint/no-empty-interface': 'warn', - '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/await-thenable': 'error', '@typescript-eslint/no-namespace': 'error', '@typescript-eslint/no-unnecessary-type-assertion': 'error', '@typescript-eslint/no-redeclare': 'error', - '@typescript-eslint/prefer-for-of': 'warn', + '@typescript-eslint/prefer-for-of': 'error', '@typescript-eslint/triple-slash-reference': 'error', - '@typescript-eslint/unified-signatures': 'warn', + '@typescript-eslint/unified-signatures': 'error', 'no-shadow': 'off', - '@typescript-eslint/no-shadow': 'warn', + '@typescript-eslint/no-shadow': 'off', 'constructor-super': 'error', eqeqeq: ['error', 'always'], 'no-cond-assign': 'error', @@ -47,18 +47,18 @@ module.exports = { 'no-throw-literal': 'error', 'no-unsafe-finally': 'error', 'no-unused-labels': 'error', - 'no-var': 'warn', + 'no-var': 'error', 'no-void': 'off', - 'prefer-const': 'warn', + 'prefer-const': 'error', 'prefer-spread': 'off', '@typescript-eslint/no-unused-vars': [ - 'warn', + 'error', { argsIgnorePattern: '^_', } ], 'padding-line-between-statements': [ - "warn", + "error", { blankLine: "always", prev: ["const", "let", "var"], next: "*"}, { blankLine: "any", prev: ["const", "let", "var"], next: ["const", "let", "var"]}, { blankLine: "always", prev: "import", next: "*" }, diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index 6c65dae94d..e92fdc163f 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -9,7 +9,7 @@ "preview": "vite preview", "format": "prettier --write .", "test:code": "eslint --max-warnings=0 --ext .js,.ts,.tsx .", - "test:errors": "pnpm sync:i18n && tsc --noEmit && eslint --quiet --ext .js,.ts,.tsx .", + "test:errors": "pnpm sync:i18n && tsc --noEmit && eslint --ext .js,.ts,.tsx .", "test:prettier": "pnpm prettier --list-different src", "tauri:clean": "cargo make --cwd .. tauri_clean", "tauri:dev": "pnpm sync:i18n && tauri dev", diff --git a/frontend/appflowy_tauri/src/appflowy_app/assets/close.svg b/frontend/appflowy_tauri/src/appflowy_app/assets/close.svg new file mode 100644 index 0000000000..b519b419c0 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/assets/close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/BlockDragDropContext.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/BlockDragDropContext.tsx index 2b0a307a37..0d7076b9bc 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/BlockDragDropContext.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/BlockDragDropContext.tsx @@ -66,7 +66,7 @@ function BlockDragDropContext({ children }: { children: React.ReactNode }) { }; const onDragEnd = () => { - dispatch(onDragEndThunk()); + void dispatch(onDragEndThunk()); unlisten(); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/index.tsx index eaf0530c21..9ae0ebc545 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/BlockDraggable/index.tsx @@ -18,7 +18,7 @@ function BlockDraggable( } & HTMLAttributes, ref: React.Ref ) { - const { onDragStart, beforeDropping, afterDropping, childDropping, isDragging } = useDraggableState(id, type); + const { onDragStart, beforeDropping, afterDropping, childDropping } = useDraggableState(id, type); const commonCls = 'pointer-events-none absolute z-10 w-[100%] bg-fill-hover transition-all duration-200'; 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 index 50de845116..9b0955793e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Database.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/Database.hooks.ts @@ -1,11 +1,6 @@ -import { useAppDispatch, useAppSelector } from '../../stores/store'; -import { useEffect, useState } from 'react'; -import { databaseActions, IDatabase } from '../../stores/reducers/database/slice'; -import { nanoid } from 'nanoid'; -import { FieldType } from '../../../services/backend'; +import { useAppSelector } from '$app/stores/store'; export const useDatabase = () => { - const dispatch = useAppDispatch(); const database = useAppSelector((state) => state.database); const newField = () => { @@ -22,7 +17,7 @@ export const useDatabase = () => { console.log('depreciated'); }; - const renameField = (fieldId: string, newTitle: string) => { + const renameField = (_fieldId: string, _newTitle: string) => { /* const field = database.fields[fieldId]; field.title = newTitle; 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 index 87c766c814..b0b7d41322 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseFilter/DatabaseFilterItem.tsx @@ -75,6 +75,7 @@ export const DatabaseFilterItem = ({ value: currentValue, }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentFieldId, currentFieldType, currentOperator, currentValue, textInputActive]); // 1. not all field types support filtering 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 index bc308e574c..5e096dce6e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortItem.tsx @@ -54,6 +54,7 @@ export const DatabaseSortItem = ({ fieldType: fields[currentFieldId].fieldType, }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentFieldId, currentOrder]); const onSelectFieldClick = (id: string) => { 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 index fe2c89772a..98247e3a1a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortPopup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/DatabaseSort/DatabaseSortPopup.tsx @@ -1,5 +1,5 @@ import { t } from 'i18next'; -import { MouseEventHandler, useMemo, useRef, useState } from 'react'; +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'; 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 index 1b9a24c116..940929660a 100644 --- 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 @@ -19,6 +19,7 @@ export const NewCheckListOption = ({ const updateNewOption = (value: string) => { const newOptionsCopy = [...newOptions]; + newOptionsCopy[index] = value; setNewOptions(newOptionsCopy); }; 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 index 0ba94e5777..58e68fd281 100644 --- 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 @@ -29,6 +29,7 @@ export const DateFormatPopup = ({ useEffect(() => { setDateType(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as IDateType); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [databaseStore]); const changeFormat = async (format: DateFormatPB) => { 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 index e9eb02af8e..8d5de41e41 100644 --- 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 @@ -30,6 +30,7 @@ export const DatePickerPopup = ({ useEffect(() => { const date_pb = data as DateCellDataPB | undefined; + if (!date_pb || !date_pb?.date.length) return; setSelectedDate(dayjs(date_pb.date).toDate()); @@ -39,6 +40,7 @@ export const DatePickerPopup = ({ if (v instanceof Date) { setSelectedDate(v); const date = new CalendarData(dayjs(v).add(dayjs().utcOffset(), 'minutes').toDate(), false); + await cellController?.saveCellData(date); } }; 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 index e024881350..25784c2efc 100644 --- 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 @@ -8,11 +8,14 @@ import { FieldController } from '$app/stores/effects/database/field/field_contro 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 = await dateTypeOptionContext.getTypeOption().then((a) => a.unwrap()); + change(typeOption); await dateTypeOptionContext.setTypeOption(typeOption); }; @@ -20,11 +23,13 @@ export const useDateTimeFormat = (cellIdentifier: CellIdentifier, fieldControlle 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) => { + + const includeTime = async (_include: boolean) => { + await changeFormat((_option) => { // option.include_time = include; }); }; 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 index 0b3f9415d2..c5e813b8b5 100644 --- 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 @@ -1,8 +1,6 @@ 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 { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; -import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; import { MouseEventHandler, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { IDateType } from '$app_reducers/database/slice'; @@ -27,14 +25,16 @@ export const DateTypeOptions = ({ 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(); 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) => { @@ -87,7 +87,7 @@ export const DateTypeOptions = ({ return (
-
+
- } - /> -
- - ); -}; \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridToolbar/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridToolbar/index.ts deleted file mode 100644 index e4a94567a0..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/grid/GridToolbar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './GridToolbar'; \ No newline at end of file diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts index fafeee00d4..42a6f31592 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/database/index.ts @@ -1,3 +1,3 @@ -export * from './database.context'; -export * from './database.hooks'; +export * from './Database.hooks'; export * from './Database'; +export * from './DatabaseTitle'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockRectSelection.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockRectSelection.hooks.ts index 5122af621b..f0258354c1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockRectSelection.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/BlockRectSelection.hooks.ts @@ -90,7 +90,7 @@ export function useBlockRectSelection({ container, getIntersectedBlockIds }: Blo const blockIds = getIntersectedBlockIds(newRect); setRect(newRect); - dispatch( + void dispatch( setRectSelectionThunk({ selection: blockIds, docId, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/NodesRect.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/NodesRect.hooks.ts index d528e41050..a0824836bd 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/NodesRect.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/NodesRect.hooks.ts @@ -16,6 +16,7 @@ export function useNodesRect(container: HTMLDivElement) { (node: Element) => { const { x, y, width, height } = node.getBoundingClientRect(); const id = node.getAttribute('data-block-id'); + if (!id) return; const rect = { id, @@ -24,6 +25,7 @@ export function useNodesRect(container: HTMLDivElement) { width, height, }; + regionGrid?.updateBlock(rect); }, [container.scrollLeft, container.scrollTop, regionGrid] @@ -31,6 +33,7 @@ export function useNodesRect(container: HTMLDivElement) { const updateViewPortNodesRect = useCallback(() => { const nodes = container.querySelectorAll('[data-block-id]'); + nodes.forEach(updateNodeRect); }, [container, updateNodeRect]); @@ -55,6 +58,7 @@ export function useNodesRect(container: HTMLDivElement) { const y = Math.min(startY, endY); const width = Math.abs(endX - startX); const height = Math.abs(endY - startY); + return regionGrid .getIntersectingBlocks({ x, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/RangeKeyDown.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/RangeKeyDown.hooks.ts index 8a85fcb71a..21d18b07b6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/RangeKeyDown.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSelection/RangeKeyDown.hooks.ts @@ -2,7 +2,6 @@ import { useCallback, useMemo } from 'react'; import { Keyboard } from '$app/constants/document/keyboard'; import { useAppDispatch } from '$app/stores/store'; import { arrowActionForRangeThunk, deleteRangeAndInsertThunk } from '$app_reducers/document/async-actions'; -import Delta from 'quill-delta'; import isHotkey from 'is-hotkey'; import { deleteRangeAndInsertEnterThunk } from '$app_reducers/document/async-actions/range'; import { useRangeRef } from '$app/components/document/_shared/SubscribeSelection.hooks'; @@ -26,7 +25,7 @@ export function useRangeKeyDown() { }, handler: (_: KeyboardEvent) => { if (!controller) return; - dispatch( + void dispatch( deleteRangeAndInsertThunk({ controller, }) @@ -40,7 +39,7 @@ export function useRangeKeyDown() { }, handler: (e: KeyboardEvent) => { if (!controller) return; - dispatch( + void dispatch( deleteRangeAndInsertThunk({ controller, insertChar: e.key, @@ -53,9 +52,9 @@ export function useRangeKeyDown() { canHandle: (e: KeyboardEvent) => { return isHotkey(Keyboard.keys.SHIFT_ENTER, e); }, - handler: (e: KeyboardEvent) => { + handler: () => { if (!controller) return; - dispatch( + void dispatch( deleteRangeAndInsertEnterThunk({ controller, shiftKey: true, @@ -68,9 +67,9 @@ export function useRangeKeyDown() { canHandle: (e: KeyboardEvent) => { return isHotkey(Keyboard.keys.ENTER, e); }, - handler: (e: KeyboardEvent) => { + handler: () => { if (!controller) return; - dispatch( + void dispatch( deleteRangeAndInsertEnterThunk({ controller, shiftKey: false, @@ -89,7 +88,7 @@ export function useRangeKeyDown() { ); }, handler: (e: KeyboardEvent) => { - dispatch( + void dispatch( arrowActionForRangeThunk({ key: e.key, docId, @@ -105,7 +104,7 @@ export function useRangeKeyDown() { const format = parseFormat(e); if (!format) return; - dispatch( + void dispatch( toggleFormatThunk({ format, controller, @@ -117,7 +116,7 @@ export function useRangeKeyDown() { [controller, dispatch, docId] ); - const onKeyDownCapture = useCallback( + return useCallback( (e: KeyboardEvent) => { if (!rangeRef.current) { return; @@ -147,6 +146,4 @@ export function useRangeKeyDown() { }, [interceptEvents, rangeRef] ); - - return onKeyDownCapture; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/BlockMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/BlockMenu.tsx index cef2c02184..932a3ad330 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/BlockMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/BlockMenu.tsx @@ -93,9 +93,7 @@ function BlockMenu({ id, onClose }: { id: string; onClose: () => void }) { if (hovered) { const option = options.find((option) => option.key === hovered); - if (option) { - option.operate?.(); - } + void option?.operate?.(); } else { onClose(); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/index.tsx index b48d34516d..a086fed1dc 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSideToolbar/index.tsx @@ -38,7 +38,7 @@ export default function BlockSideToolbar({ id }: { id: string }) { pointerEvents: show ? 'auto' : 'none', }} onClick={(_: React.MouseEvent) => { - dispatch( + void dispatch( addBlockBelowClickThunk({ id, controller, @@ -74,8 +74,8 @@ export default function BlockSideToolbar({ id }: { id: string }) { pointerEvents: show ? 'auto' : 'none', }} data-draggable-anchor={id} - onClick={(e: React.MouseEvent) => { - dispatch( + onClick={async (e: React.MouseEvent) => { + await dispatch( setRectSelectionThunk({ docId, selection: [id], diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSlash/BlockSlashMenu.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSlash/BlockSlashMenu.tsx index 234ff085f8..583d57594d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSlash/BlockSlashMenu.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/BlockSlash/BlockSlashMenu.tsx @@ -27,7 +27,7 @@ import { slashCommandActions } from '$app_reducers/document/slice'; import { Keyboard } from '$app/constants/document/keyboard'; import { selectOptionByUpDown } from '$app/utils/document/menu'; import { turnToBlockThunk } from '$app_reducers/document/async-actions'; -import {useTranslation} from "react-i18next"; +import { useTranslation } from 'react-i18next'; function BlockSlashMenu({ id, @@ -43,11 +43,11 @@ function BlockSlashMenu({ container: HTMLDivElement; }) { const dispatch = useAppDispatch(); - const { t } = useTranslation() + const { t } = useTranslation(); const ref = useRef(null); const { docId, controller } = useSubscribeDocument(); const handleInsert = useCallback( - async (type: BlockType, data?: BlockData) => { + async (type: BlockType, data?: BlockData) => { if (!controller) return; await dispatch( turnToBlockThunk({ @@ -245,7 +245,7 @@ function BlockSlashMenu({ e.preventDefault(); if (isEnter) { if (hoverOption) { - handleInsert(hoverOption.type, hoverOption.data); + void handleInsert(hoverOption.type, hoverOption.data); } return; @@ -282,9 +282,9 @@ function BlockSlashMenu({ ); const renderEmptyContent = useCallback(() => { - return
- {t('findAndReplace.noResult')} -
+ return ( +
{t('findAndReplace.noResult')}
+ ); }, [t]); return ( @@ -296,30 +296,32 @@ function BlockSlashMenu({ className={'flex h-[100%] max-h-[40vh] w-[324px] min-w-[180px] max-w-[calc(100vw-32px)] flex-col p-1'} >
- {options.length === 0 ? renderEmptyContent(): Object.entries(optionsByGroup).map(([group, options]) => ( -
-
{group}
-
- {options.map((option) => { - return ( - { - onHoverOption(option); - }} - isHovered={hoverOption?.key === option.key} - onClick={() => { - handleInsert(option.type, option.data); - }} - /> - ); - })} -
-
- ))} + {options.length === 0 + ? renderEmptyContent() + : Object.entries(optionsByGroup).map(([group, options]) => ( +
+
{group}
+
+ {options.map((option) => { + return ( + { + onHoverOption(option); + }} + isHovered={hoverOption?.key === option.key} + onClick={() => { + void handleInsert(option.type, option.data); + }} + /> + ); + })} +
+
+ ))}
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/SelectLanguage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/SelectLanguage.tsx index f1a5132818..01a96028df 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/SelectLanguage.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/SelectLanguage.tsx @@ -17,7 +17,7 @@ function SelectLanguage({ id, language }: { id: string; language: string }) { if (!controller) return; const language = event.target.value; - dispatch( + void dispatch( updateNodeDataThunk({ id, controller, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/useKeyDown.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/useKeyDown.ts index 2a64f350de..a24413a69b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/useKeyDown.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/CodeBlock/useKeyDown.ts @@ -1,5 +1,5 @@ import isHotkey from 'is-hotkey'; -import { useCallback, useContext, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { useAppDispatch } from '$app/stores/store'; import { Keyboard } from '$app/constants/document/keyboard'; import { useCommonKeyEvents } from '$app/components/document/_shared/EditorHooks/useCommonKeyEvents'; @@ -8,7 +8,7 @@ import { useSubscribeDocument } from '$app/components/document/_shared/Subscribe export function useKeyDown(id: string) { const dispatch = useAppDispatch(); - const { docId, controller } = useSubscribeDocument(); + const { controller } = useSubscribeDocument(); const commonKeyEvents = useCommonKeyEvents(id); const customEvents = useMemo(() => { @@ -22,7 +22,7 @@ export function useKeyDown(id: string) { handler: (e: React.KeyboardEvent) => { e.preventDefault(); if (!controller) return; - dispatch( + void dispatch( enterActionForBlockThunk({ id, controller, @@ -37,6 +37,7 @@ export function useKeyDown(id: string) { (e) => { e.stopPropagation(); const keyEvents = [...customEvents]; + keyEvents.forEach((keyEvent) => { // Here we check if the key event can be handled by the current key event if (keyEvent.canHandle(e)) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnListBlock/Column.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnListBlock/Column.tsx deleted file mode 100644 index f88031e64f..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnListBlock/Column.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import NodeComponent from '$app/components/document/Node'; -import React from 'react'; - -export function ColumnBlock({ id, index, width }: { id: string; index: number; width: string }) { - const renderResizer = () => { - return ( -
- ); - }; - - return ( - <> - {index === 0 ? ( -
-
- {renderResizer()} -
-
- ) : ( - renderResizer() - )} - - - - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnListBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnListBlock/index.tsx deleted file mode 100644 index b0c406908b..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/ColumnListBlock/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React, { useMemo } from 'react'; -import { Node } from '$app/interfaces/document'; -import { ColumnBlock } from './Column'; - -export default function ColumnListBlock({ - node, - childIds, -}: { - node: Node & { - data: Record; - }; - childIds?: string[]; -}) { - const resizerWidth = useMemo(() => { - return 46 * (node.children?.length || 0); - }, [node.children?.length]); - return ( - <> -
- {childIds?.map((item, index) => ( - - ))} -
- - ); -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/DocumentBanner.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/DocumentBanner.hooks.ts index 6c57f6ba5b..200816463a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/DocumentBanner.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/DocumentBanner.hooks.ts @@ -6,6 +6,7 @@ import { ViewIconTypePB } from '@/services/backend'; import { CoverType } from '$app/interfaces/document'; import { updateNodeDataThunk } from '$app_reducers/document/async-actions'; import { useSubscribeNode } from '$app/components/document/_shared/SubscribeNode.hooks'; + export const heightCls = { cover: 'h-[220px]', icon: 'h-[80px]', @@ -22,7 +23,7 @@ export function useDocumentBanner(id: string) { const onUpdateIcon = useCallback( (icon: string) => { - dispatch( + void dispatch( updatePageIcon({ id: docId, icon: icon @@ -39,7 +40,7 @@ export function useDocumentBanner(id: string) { const onUpdateCover = useCallback( (coverType: CoverType | null, cover: string | null) => { - dispatch( + void dispatch( updateNodeDataThunk({ id, data: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/cover/ChangeImages.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/cover/ChangeImages.tsx index 1ff91eaea9..0a3b9d42e7 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/cover/ChangeImages.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/cover/ChangeImages.tsx @@ -7,7 +7,7 @@ import { readCoverImageUrls, readImage, writeCoverImageUrls } from '$app/utils/d import { Log } from '$app/utils/log'; import { Image } from '$app/components/document/DocumentBanner/cover/GalleryItem'; -function ChangeImages({ cover, onChange }: { onChange: (url: string) => void; cover: string }) { +function ChangeImages({ onChange }: { onChange: (url: string) => void; cover: string }) { const { t } = useTranslation(); const [images, setImages] = useState([]); const loadImageUrls = useCallback(async () => { @@ -58,7 +58,7 @@ function ChangeImages({ cover, onChange }: { onChange: (url: string) => void; co }, [loadImageUrls]); useEffect(() => { - loadImageUrls(); + void loadImageUrls(); }, [loadImageUrls]); return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/index.tsx index 036ee0e127..f8d4c269ac 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/DocumentBanner/index.tsx @@ -5,6 +5,7 @@ import DocumentIcon from './DocumentIcon'; function DocumentBanner({ id, hover }: { id: string; hover: boolean }) { const { onUpdateCover, node, onUpdateIcon, icon, cover, className, coverType } = useDocumentBanner(id); + return ( <>
{ - dispatch( + void dispatch( updateNodeDataThunk({ id, data: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/ImageToolbar.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/ImageToolbar.tsx index c2175ec3f5..c9fc838a81 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/ImageToolbar.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/ImageToolbar.tsx @@ -27,7 +27,7 @@ function ImageToolbar({ id, open, align }: { id: string; open: boolean; align: A
{ - dispatch(deleteNodeThunk({ id, controller })); + void dispatch(deleteNodeThunk({ id, controller })); }} className='flex items-center justify-center p-1' > diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/index.tsx index 9aecb525f5..b497989eb3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/index.tsx @@ -20,7 +20,7 @@ function ImageBlock({ node }: { node: NestedBlock }) { ({ onClose }: { onClose: () => void }) => { const onSubmitUrl = (url: string) => { if (!url) return; - dispatch( + void dispatch( updateNodeDataThunk({ id, data: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/useImageBlock.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/useImageBlock.ts index 80c0940f17..f9b7c7da5d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/useImageBlock.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ImageBlock/useImageBlock.ts @@ -47,7 +47,7 @@ export function useImageBlock(node: NestedBlock) { const updateWidth = useCallback( (width: number, height: number) => { - dispatch( + void dispatch( updateNodeDataThunk({ id: node.id, data: { @@ -93,7 +93,7 @@ export function useImageBlock(node: NestedBlock) { }); }; - const onResizeEnd = (e: MouseEvent) => { + const onResizeEnd = () => { setResizing(false); if (!startResizePoint.current) return; startResizePoint.current = undefined; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/NumberedListBlock/NumberedListBlock.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/NumberedListBlock/NumberedListBlock.hooks.ts index 5356456558..f9ff2c55ed 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/NumberedListBlock/NumberedListBlock.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/NumberedListBlock/NumberedListBlock.hooks.ts @@ -10,16 +10,20 @@ export function useNumberedListBlock(node: NestedBlock { return nodes[id].type !== BlockType.NumberedListBlock; }); + if (lastIndex === -1) return prevNodeIds.length; return lastIndex; }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/CustomColorPicker.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/CustomColorPicker.tsx index ed0e4e6cce..eee5f9c126 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/CustomColorPicker.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/CustomColorPicker.tsx @@ -46,7 +46,7 @@ function CustomColorPicker({ onClose={onClose} > { + onChange={(color) => { setColor(color.rgb); }} color={color} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/FormatButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/FormatButton.tsx index 8180ebc868..9d8412e5e3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/FormatButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/FormatButton.tsx @@ -61,7 +61,7 @@ const FormatButton = ({ format, icon }: { format: TextAction; icon: string }) => const addTemporaryInput = useCallback( (type: TemporaryType) => { - dispatch(createTemporary({ type, docId })); + void dispatch(createTemporary({ type, docId })); }, [dispatch, docId] ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/index.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/index.hooks.ts index a1098e99c2..315ccd3337 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/index.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextActionMenu/menu/index.hooks.ts @@ -36,6 +36,7 @@ export function useTextActionMenu() { return groups.map((group) => { return group.filter((item) => items.includes(item)); }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [JSON.stringify(items), node]); return { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useKeyDown.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useKeyDown.ts index 931f81a396..0c49ef54e4 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useKeyDown.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useKeyDown.ts @@ -10,10 +10,9 @@ import { import { useTurnIntoBlockEvents } from './useTurnIntoBlockEvents'; import { useCommonKeyEvents } from '../_shared/EditorHooks/useCommonKeyEvents'; import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks'; -import { openMention } from '$app_reducers/document/async-actions/mention'; export function useKeyDown(id: string) { - const { controller, docId } = useSubscribeDocument(); + const { controller } = useSubscribeDocument(); const dispatch = useAppDispatch(); const turnIntoEvents = useTurnIntoBlockEvents(id); const commonKeyEvents = useCommonKeyEvents(id); @@ -34,9 +33,9 @@ export function useKeyDown(id: string) { canHandle: (e: React.KeyboardEvent) => { return isHotkey(Keyboard.keys.ENTER, e); }, - handler: (e: React.KeyboardEvent) => { + handler: () => { if (!controller) return; - dispatch( + void dispatch( enterActionForBlockThunk({ id, controller, @@ -58,9 +57,9 @@ export function useKeyDown(id: string) { canHandle: (e: React.KeyboardEvent) => { return isHotkey(Keyboard.keys.TAB, e); }, - handler: (e: React.KeyboardEvent) => { + handler: () => { if (!controller) return; - dispatch( + void dispatch( tabActionForBlockThunk({ id, controller, @@ -73,9 +72,9 @@ export function useKeyDown(id: string) { canHandle: (e: React.KeyboardEvent) => { return isHotkey(Keyboard.keys.SHIFT_TAB, e); }, - handler: (e: React.KeyboardEvent) => { + handler: () => { if (!controller) return; - dispatch( + void dispatch( shiftTabActionForBlockThunk({ id, controller, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useTurnIntoBlockEvents.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useTurnIntoBlockEvents.ts index bb4859017e..d06f17b1b0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useTurnIntoBlockEvents.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/TextBlock/useTurnIntoBlockEvents.ts @@ -6,7 +6,7 @@ import { blockConfig } from '$app/constants/document/config'; import Delta from 'quill-delta'; import { useRangeRef } from '$app/components/document/_shared/SubscribeSelection.hooks'; -import { getBlockDelta } from '$app/components/document/_shared/SubscribeNode.hooks'; +import { getBlockDelta } from '$app/components/document/_shared/SubscribeNode.hooks'; import { getDeltaText } from '$app/utils/document/delta'; import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks'; import { turnIntoConfig } from './shortchut'; @@ -155,7 +155,7 @@ export function useTurnIntoBlockEvents(id: string) { const data = getData(); if (!data) return; - dispatch(turnToBlockThunk({ id, data, type: blockType, controller })); + void dispatch(turnToBlockThunk({ id, data, type: blockType, controller })); }, }; }); @@ -167,9 +167,8 @@ export function useTurnIntoBlockEvents(id: string) { handler: (e: React.KeyboardEvent) => { e.preventDefault(); if (!controller) return; - const delta = getDeltaContent(); - dispatch( + void dispatch( turnToBlockThunk({ id, controller, @@ -186,7 +185,7 @@ export function useTurnIntoBlockEvents(id: string) { if (!controller) return; const defaultData = blockConfig[BlockType.CodeBlock].defaultData; - dispatch( + void dispatch( turnToBlockThunk({ id, data: { @@ -208,10 +207,11 @@ export function useTurnIntoBlockEvents(id: string) { formula, }; - dispatch(turnToBlockThunk({ id, data, type: BlockType.EquationBlock, controller })); + void dispatch(turnToBlockThunk({ id, data, type: BlockType.EquationBlock, controller })); }, - } + }, ]; + // eslint-disable-next-line react-hooks/exhaustive-deps }, [canHandle, controller, dispatch, getAttrs, getDeltaContent, id, spaceTriggerMap]); return turnIntoBlockEvents; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/ToggleListBlock/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/ToggleListBlock/index.tsx index 9c3b980bdf..54387fe922 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/ToggleListBlock/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/ToggleListBlock/index.tsx @@ -3,13 +3,13 @@ import { BlockType, NestedBlock } from '$app/interfaces/document'; import TextBlock from '$app/components/document/TextBlock'; import NodeChildren from '$app/components/document/Node/NodeChildren'; import { useToggleListBlock } from '$app/components/document/ToggleListBlock/ToggleListBlock.hooks'; -import { IconButton } from '@mui/material'; import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg'; import Button from '@mui/material/Button'; function ToggleListBlock({ node, childIds }: { node: NestedBlock; childIds?: string[] }) { const { toggleCollapsed, handleShortcut } = useToggleListBlock(node.id, node.data); const collapsed = node.data.collapsed; + return ( <>
diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/BlockPopover/BlockPopover.hooks.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/BlockPopover/BlockPopover.hooks.tsx index 81c9cd0e4c..a86f5e8e9e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/BlockPopover/BlockPopover.hooks.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/BlockPopover/BlockPopover.hooks.tsx @@ -42,7 +42,7 @@ export function useBlockPopover({ }, [dispatch, docId, id, onAfterClose]); const selectBlock = useCallback(() => { - dispatch( + void dispatch( setRectSelectionThunk({ docId, selection: [id], diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/useCopy.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/useCopy.ts index 2c31f9ab95..0a34e4971c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/useCopy.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/useCopy.ts @@ -19,7 +19,8 @@ export function useCopy(container: HTMLDivElement) { e.clipboardData?.setData(clipboardTypes.TEXT, data.text); e.clipboardData?.setData(clipboardTypes.HTML, data.html); }; - dispatch( + + void dispatch( copyThunk({ setClipboardData, controller, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/usePaste.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/usePaste.ts index a2cc09d643..6ed3c9fd6b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/usePaste.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/CopyPasteHooks/usePaste.ts @@ -1,4 +1,4 @@ -import { useCallback, useContext, useEffect } from 'react'; +import { useCallback, useEffect } from 'react'; import { useAppDispatch } from '$app/stores/store'; import { pasteThunk } from '$app_reducers/document/async-actions/copy_paste'; import { clipboardTypes } from '$app/constants/document/copy_paste'; @@ -12,7 +12,7 @@ export function usePaste(container: HTMLDivElement) { if (!controller) return; e.stopPropagation(); e.preventDefault(); - dispatch( + void dispatch( pasteThunk({ controller, data: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useCommonKeyEvents.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useCommonKeyEvents.ts index 84a95e5bd9..e7da44baf8 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useCommonKeyEvents.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useCommonKeyEvents.ts @@ -12,8 +12,6 @@ import { useAppDispatch } from '$app/stores/store'; import { isFormatHotkey, parseFormat } from '$app/utils/document/format'; import { toggleFormatThunk } from '$app_reducers/document/async-actions/format'; import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks'; -import { getBlock } from '$app/components/document/_shared/SubscribeNode.hooks'; -import Delta from 'quill-delta'; export function useCommonKeyEvents(id: string) { const { focused, caretRef } = useFocused(id); @@ -35,7 +33,7 @@ export function useCommonKeyEvents(id: string) { handler: (e: React.KeyboardEvent) => { e.preventDefault(); if (!controller) return; - dispatch(backspaceDeleteActionForBlockThunk({ id, controller })); + void dispatch(backspaceDeleteActionForBlockThunk({ id, controller })); }, }, { @@ -45,7 +43,7 @@ export function useCommonKeyEvents(id: string) { }, handler: (e: React.KeyboardEvent) => { e.preventDefault(); - dispatch(upDownActionForBlockThunk({ docId, id })); + void dispatch(upDownActionForBlockThunk({ docId, id })); }, }, { @@ -55,7 +53,7 @@ export function useCommonKeyEvents(id: string) { }, handler: (e: React.KeyboardEvent) => { e.preventDefault(); - dispatch(upDownActionForBlockThunk({ docId, id, down: true })); + void dispatch(upDownActionForBlockThunk({ docId, id, down: true })); }, }, { @@ -66,7 +64,7 @@ export function useCommonKeyEvents(id: string) { handler: (e: React.KeyboardEvent) => { e.preventDefault(); e.stopPropagation(); - dispatch(leftActionForBlockThunk({ docId, id })); + void dispatch(leftActionForBlockThunk({ docId, id })); }, }, { @@ -76,7 +74,7 @@ export function useCommonKeyEvents(id: string) { }, handler: (e: React.KeyboardEvent) => { e.preventDefault(); - dispatch(rightActionForBlockThunk({ docId, id })); + void dispatch(rightActionForBlockThunk({ docId, id })); }, }, { @@ -87,7 +85,7 @@ export function useCommonKeyEvents(id: string) { const format = parseFormat(e); if (!format) return; - dispatch( + void dispatch( toggleFormatThunk({ format, controller, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useDelta.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useDelta.ts index 98baf30121..9d690d9b0a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useDelta.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useDelta.ts @@ -1,5 +1,5 @@ import { useSubscribeNode } from '$app/components/document/_shared/SubscribeNode.hooks'; -import { useCallback, useContext, useEffect, useMemo, useRef } from 'react'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useAppDispatch } from '$app/stores/store'; import { updateNodeDeltaThunk } from '$app_reducers/document/async-actions'; import Delta, { Op } from 'quill-delta'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useSelection.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useSelection.ts index 92517f5720..563241fec3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useSelection.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/EditorHooks/useSelection.ts @@ -19,8 +19,8 @@ export function useSelection(id: string) { const { docId } = useSubscribeDocument(); const storeRange = useCallback( - (range: RangeStatic) => { - dispatch(storeRangeThunk({ id, range, docId })); + async (range: RangeStatic) => { + await dispatch(storeRangeThunk({ id, range, docId })); }, [docId, id, dispatch] ); @@ -38,7 +38,7 @@ export function useSelection(id: string) { }, }) ); - storeRange(range); + void storeRange(range); }, [docId, id, dispatch, storeRange] ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/CodeInline.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/CodeInline.tsx index 4f2c031602..f28892ab22 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/CodeInline.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/CodeInline.tsx @@ -1,6 +1,6 @@ import React from 'react'; -function CodeInline({ text, children, selected }: { text: string; children: React.ReactNode; selected: boolean }) { +function CodeInline({ children, selected }: { text: string; children: React.ReactNode; selected: boolean }) { return ( { + async (node: HTMLSpanElement) => { const selection = getSelection(node); if (!selection) return; - dispatch( + await dispatch( createTemporary({ docId, state: { @@ -57,7 +57,7 @@ function FormulaInline({ getSelection={getSelection} isFirst={isFirst} isLast={isLast} - renderNode={() => } + renderNode={() => } > {children} diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/LinkInline.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/LinkInline.tsx index 070ec257f0..ebb5c0f4c2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/LinkInline.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/LinkInline.tsx @@ -26,17 +26,18 @@ function LinkInline({ const dispatch = useAppDispatch(); const onClick = useCallback( - (e: React.MouseEvent) => { + async (e: React.MouseEvent) => { if (!ref.current) return; const selection = getSelection(ref.current); if (!selection) return; const rect = ref.current?.getBoundingClientRect(); + if (!rect) return; e.stopPropagation(); e.preventDefault(); - dispatch( + await dispatch( createTemporary({ docId, state: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/PageInline.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/PageInline.tsx index a97726aded..6786ba8389 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/PageInline.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/InlineBlock/PageInline.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { pageTypeMap } from '$app/constants'; import { LinearProgress } from '@mui/material'; -import Tooltip from "@mui/material/Tooltip"; +import Tooltip from '@mui/material/Tooltip'; function PageInline({ pageId }: { pageId: string }) { const { t } = useTranslation(); @@ -18,12 +18,14 @@ function PageInline({ pageId }: { pageId: string }) { const controller = new PageController(id); const page = await controller.getPage(); + setCurrentPage(page); }, []); const navigateToPage = useCallback( (page: Page) => { const pageType = pageTypeMap[page.layout]; + navigate(`/page/${pageType}/${page.id}`); }, [navigate] @@ -31,14 +33,12 @@ function PageInline({ pageId }: { pageId: string }) { useEffect(() => { if (!page) { - loadPage(pageId); + void loadPage(pageId); } else { setCurrentPage(page); } - }, [page, loadPage, pageId]); - return currentPage ? ( - {currentPage.icon?.value ||
} - {currentPage.name || t('menuAppHeader.defaultNewPageName')} - + {currentPage.icon?.value ||
} + {currentPage.name || t('menuAppHeader.defaultNewPageName')} + - ) : ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx index 83bd7fe680..6e2e87bb64 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/TextLeaf.tsx @@ -92,6 +92,7 @@ const TextLeaf = (props: TextLeafProps) => { } const mention = leaf.mention; + if (mention && mention.type === MentionType.PAGE && leaf.text) { newChildren = ( 0; + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_, path] = editor.node(currentSelection); if (removeMark) { @@ -113,9 +113,10 @@ export function useEditor({ const decorate = useCallback( (entry: NodeEntry) => { - const [node, path] = entry; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_, path] = entry; - const ranges: Range[] = [ + return [ getDecorateRange(path, decorateSelection, { selection_high_lighted: true, }), @@ -123,8 +124,6 @@ export function useEditor({ temporary: true, }), ].filter((range) => range !== null) as Range[]; - - return ranges; }, [temporarySelection, decorateSelection, getDecorateRange] ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/useSlateYjs.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/useSlateYjs.ts index f377567d52..ba4669db6c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/useSlateYjs.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SlateEditor/useSlateYjs.ts @@ -21,6 +21,7 @@ export function useSlateYjs({ delta, onChange }: { delta?: Delta; onChange: (ops // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // eslint-disable-next-line react-hooks/exhaustive-deps const editor = useMemo(() => withYjs(withMarkdown(withReact(createEditor())), sharedType), []); // Connect editor in useEffect to comply with concurrent mode requirements. diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeMention.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeMention.hooks.ts index 17d103a30d..2043209b40 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeMention.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeMention.hooks.ts @@ -7,6 +7,7 @@ const initialState: MentionState = { open: false, blockId: '', }; + export function useSubscribeMentionState() { const { docId } = useSubscribeDocument(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts index c687177c6d..0efefa4621 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeNode.hooks.ts @@ -18,14 +18,17 @@ export function useSubscribeNode(id: string) { }>((state) => { const documentState = state[DOCUMENT_NAME][docId]; const node = documentState?.nodes[id]; + // if node is root, return page name if (!node?.parent) { const delta = state.pages?.pageMap[docId]?.name; + return { node, delta: delta ? JSON.stringify(new Delta().insert(delta)) : '', }; } + const externalId = node?.externalId; return { @@ -51,7 +54,9 @@ export function useSubscribeNode(id: string) { // Memoize the node and its children // So that the component will not be re-rendered when other node is changed // It very important for performance + // eslint-disable-next-line react-hooks/exhaustive-deps const memoizedNode = useMemo(() => node, [JSON.stringify(node)]); + // eslint-disable-next-line react-hooks/exhaustive-deps const memoizedChildIds = useMemo(() => childIds, [JSON.stringify(childIds)]); return { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeSelection.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeSelection.hooks.ts index 649d33bc38..fa1ee30fd1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeSelection.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/SubscribeSelection.hooks.ts @@ -2,7 +2,7 @@ import { useAppSelector } from '$app/stores/store'; import { RangeState, RangeStatic } from '$app/interfaces/document'; import { useMemo, useRef } from 'react'; import { useSubscribeDocument } from '$app/components/document/_shared/SubscribeDoc.hooks'; -import { RANGE_NAME, TEMPORARY_NAME, TEXT_LINK_NAME } from '$app/constants/document/name'; +import { RANGE_NAME, TEMPORARY_NAME } from '$app/constants/document/name'; export function useSubscribeDecorate(id: string) { const { docId } = useSubscribeDocument(); @@ -51,7 +51,7 @@ export function useFocused(id: string) { } export function useRangeRef() { - const { docId, controller } = useSubscribeDocument(); + const { docId } = useSubscribeDocument(); const rangeRef = useRef(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryLink.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryLink.tsx index 117f787b1a..f16352a31b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryLink.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryLink.tsx @@ -2,8 +2,9 @@ import React from 'react'; import { AddLinkOutlined } from '@mui/icons-material'; import { useTranslation } from 'react-i18next'; -function TemporaryLink({ href = '', text = '' }: { href?: string; text?: string }) { +function TemporaryLink({ text = '' }: { href?: string; text?: string }) { const { t } = useTranslation(); + return ( {text ? ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryPopover.tsx index 6f27a871bf..ec53767774 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryPopover.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/TemporaryPopover.tsx @@ -17,7 +17,6 @@ function TemporaryPopover() { const anchorPosition = useMemo(() => temporaryState?.popoverPosition, [temporaryState]); const open = Boolean(anchorPosition); const id = temporaryState?.id; - const type = temporaryState?.type; const dispatch = useAppDispatch(); const { docId, controller } = useSubscribeDocument(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/index.tsx index 4afe2ae2c6..4e9460dbf3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TemporaryInput/index.tsx @@ -77,6 +77,7 @@ function TemporaryInput({ useEffect(() => { const match = getMatch(); + setMatch(match); }, [getMatch]); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/TurnInto.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/TurnInto.hooks.ts index edbda07333..1f1df71304 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/TurnInto.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/TurnInto.hooks.ts @@ -12,7 +12,7 @@ export function useTurnInto({ node, onClose }: { node: NestedBlock; onClose?: () const { controller, docId } = useSubscribeDocument(); const turnIntoBlock = useCallback( - async (type: BlockType, isSelected: boolean, data?: BlockData) => { + async (type: BlockType, isSelected: boolean, data?: BlockData) => { if (!controller || isSelected) { onClose?.(); return; @@ -35,7 +35,7 @@ export function useTurnInto({ node, onClose }: { node: NestedBlock; onClose?: () ); onClose?.(); - dispatch( + await dispatch( setRectSelectionThunk({ docId, selection: [newBlockId as string], diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/index.tsx index eb3661fb51..b1388ff6ce 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/TurnInto/index.tsx @@ -57,7 +57,7 @@ const TurnIntoPopover = ({ icon: , selected: node?.data?.level === 1, onClick: (type: BlockType, isSelected: boolean) => { - turnIntoHeading(1, isSelected); + void turnIntoHeading(1, isSelected); }, }, { @@ -67,7 +67,7 @@ const TurnIntoPopover = ({ icon: <Title />, selected: node?.data?.level === 2, onClick: (type: BlockType, isSelected: boolean) => { - turnIntoHeading(2, isSelected); + void turnIntoHeading(2, isSelected); }, }, { @@ -77,7 +77,7 @@ const TurnIntoPopover = ({ icon: <Title />, selected: node?.data?.level === 3, onClick: (type: BlockType, isSelected: boolean) => { - turnIntoHeading(3, isSelected); + void turnIntoHeading(3, isSelected); }, }, { @@ -143,7 +143,7 @@ const TurnIntoPopover = ({ (option: Option) => { const isSelected = getSelected(option); - option.onClick ? option.onClick(option.type, isSelected) : turnIntoBlock(option.type, isSelected); + option.onClick ? option.onClick(option.type, isSelected) : void turnIntoBlock(option.type, isSelected); onOk?.(); }, [onOk, getSelected, turnIntoBlock] diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UndoHooks/useUndoRedo.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UndoHooks/useUndoRedo.ts index f9f3579963..3a101cac22 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UndoHooks/useUndoRedo.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UndoHooks/useUndoRedo.ts @@ -6,25 +6,26 @@ import { useSubscribeDocument } from '$app/components/document/_shared/Subscribe export function useUndoRedo(container: HTMLDivElement) { const { controller } = useSubscribeDocument(); - const onUndo = useCallback(() => { + const onUndo = useCallback(async () => { if (!controller) return; - controller.undo(); + await controller.undo(); }, [controller]); - const onRedo = useCallback(() => { + const onRedo = useCallback(async () => { if (!controller) return; - controller.redo(); + await controller.redo(); }, [controller]); const handleKeyDownCapture = useCallback( - (e: KeyboardEvent) => { + async (e: KeyboardEvent) => { if (isHotkey(Keyboard.keys.UNDO, e)) { e.stopPropagation(); - onUndo(); + await onUndo(); } + if (isHotkey(Keyboard.keys.REDO, e)) { e.stopPropagation(); - onRedo(); + await onRedo(); } }, [onRedo, onUndo] diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/ImageEditPopover.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/ImageEditPopover.tsx index af31ae70c5..e341563d57 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/ImageEditPopover.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/ImageEditPopover.tsx @@ -1,7 +1,6 @@ import React from 'react'; import Popover, { PopoverProps } from '@mui/material/Popover'; import ImageEdit from './ImageEdit'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; interface Props extends PopoverProps { onSubmitUrl: (url: string) => void; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/TabPanel.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/TabPanel.tsx index 1f0f6ab4ef..9356a246aa 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/TabPanel.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/TabPanel.tsx @@ -1,4 +1,5 @@ import React from 'react'; + export enum TAB_KEYS { UPLOAD = 'upload', LINK = 'link', diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/UploadImage.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/UploadImage.tsx index ecb14d7d2f..5990f5b5b9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/UploadImage.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/UploadImage/UploadImage.tsx @@ -42,6 +42,7 @@ function UploadImage({ onChange }: UploadImageProps) { duration: 3000, type: 'error', }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [error]); const handleUpload = useCallback( @@ -74,7 +75,7 @@ function UploadImage({ onChange }: UploadImageProps) { if (!files || files.length === 0) return; const file = files[0]; - handleUpload(file); + void handleUpload(file); }, [handleUpload] ); @@ -87,7 +88,7 @@ function UploadImage({ onChange }: UploadImageProps) { if (!files || files.length === 0) return; const file = files[0]; - handleUpload(file); + void handleUpload(file); }, [handleUpload] ); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/useBindArrowKey.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/useBindArrowKey.ts index 8d2ac27796..29aebe52e9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/useBindArrowKey.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/useBindArrowKey.ts @@ -83,6 +83,7 @@ export const useBindArrowKey = ({ } else { document.removeEventListener('keydown', handleArrowKey, true); } + return () => { document.removeEventListener('keydown', handleArrowKey, true); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/usePanelSearchText.ts b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/usePanelSearchText.ts index 245041f702..034919e838 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/usePanelSearchText.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/document/_shared/usePanelSearchText.ts @@ -25,9 +25,11 @@ export function useSubscribePanelSearchText({ blockId, open }: { blockId: string beforeOpenDeltaRef.current = []; return; } + if (beforeOpenDeltaRef.current.length > 0) return; const delta = new Delta(JSON.parse(deltaStr || "{}")); + beforeOpenDeltaRef.current = delta.ops; handleSearch(delta); }, [deltaStr, handleSearch, open]); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts index 2da03c5f5c..ceaa5a51a0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/error/Error.hooks.ts @@ -1,6 +1,6 @@ -import { useAppDispatch, useAppSelector } from '../../stores/store'; -import { errorActions } from '../../stores/reducers/error/slice'; -import { useEffect, useState } from 'react'; +import { useAppDispatch, useAppSelector } from '$app/stores/store'; +import { errorActions } from '$app_reducers/error/slice'; +import { useCallback, useEffect, useState } from 'react'; export const useError = (e: Error) => { const dispatch = useAppDispatch(); @@ -13,15 +13,18 @@ export const useError = (e: Error) => { setErrorMessage(error.message); }, [error]); + const showError = useCallback( + (msg: string) => { + dispatch(errorActions.showError(msg)); + }, + [dispatch] + ); + useEffect(() => { if (e) { showError(e.message); } - }, [e]); - - const showError = (msg: string) => { - dispatch(errorActions.showError(msg)); - }; + }, [e, showError]); const hideError = () => { dispatch(errorActions.hideError()); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableCount/GridTableCount.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableCount/GridTableCount.tsx index 727f831a81..51ffd11186 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableCount/GridTableCount.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableCount/GridTableCount.tsx @@ -2,6 +2,7 @@ import { RowInfo } from '@/appflowy_app/stores/effects/database/row/row_cache'; export const GridTableCount = ({ rows }: { rows: readonly RowInfo[] }) => { const count = rows.length; + return ( <span> Count : <span className='font-semibold'>{count}</span> diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableHeader/GridTableHeaderItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableHeader/GridTableHeaderItem.tsx index c4b85be45b..194709d40c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableHeader/GridTableHeaderItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableHeader/GridTableHeaderItem.tsx @@ -53,7 +53,8 @@ export const GridTableHeaderItem = ({ if (newSizeX >= MIN_COLUMN_WIDTH) { dispatch(databaseActions.changeWidth({ fieldId: field.fieldId, width: newSizeX })); } - }, [newSizeX]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [newSizeX, dispatch]); const changeFieldType = async (newType: FieldType) => { if (!editingField) return; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts index 2288657d7b..7063cea877 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/Breadcrumb/Breadcrumb.hooks.ts @@ -1,9 +1,9 @@ -import { useAppSelector } from "$app/stores/store"; +import { useAppSelector } from '$app/stores/store'; 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 { PageController } from '$app/stores/effects/workspace/page/page_controller'; export function useLoadExpandedPages() { const { t } = useTranslation(); @@ -25,6 +25,7 @@ export function useLoadExpandedPages() { async (pageId: string) => { let page = pageMap[pageId]; const controller = new PageController(pageId); + if (!page) { try { page = await controller.getPage(); @@ -36,22 +37,22 @@ export function useLoadExpandedPages() { return; } } - setPagePath(prev => { - return [ - page, - ...prev - ] + + setPagePath((prev) => { + return [page, ...prev]; }); await loadPagePath(page.parentId); - - }, [pageMap]); + }, + [pageMap] + ); useEffect(() => { setPagePath([]); if (!currentPageId) { return; } - loadPagePath(currentPageId); + + void loadPagePath(currentPageId); // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentPageId]); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts index 7acd565087..428c77193b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/NestedPage/NestedPage.hooks.ts @@ -36,30 +36,30 @@ export function useLoadChildPages(pageId: string) { [dispatch] ); + const loadPageChildren = useCallback( + async (pageId: string) => { + const childPages = await controller.getChildPages(); - const loadPageChildren = useCallback(async (pageId: string) => { - const childPages = await controller.getChildPages(); - - dispatch( - pagesActions.addChildPages({ - id: pageId, - childPages, - }) - ); - - }, [controller, dispatch]); - + dispatch( + pagesActions.addChildPages({ + id: pageId, + childPages, + }) + ); + }, + [controller, dispatch] + ); useEffect(() => { void loadPageChildren(pageId); }, [loadPageChildren, pageId]); useEffect(() => { - controller.subscribe({ + void controller.subscribe({ onPageChanged, }); return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller, onPageChanged]); @@ -88,7 +88,7 @@ export function usePageActions(pageId: string) { async (layout: ViewLayoutPB) => { const newViewId = await controller.createPage({ layout, - name: "" + name: '', }); dispatch(pagesActions.expandPage(pageId)); @@ -116,7 +116,7 @@ export function usePageActions(pageId: string) { useEffect(() => { return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller]); @@ -134,4 +134,3 @@ export function useSelectedPage(pageId: string) { return id === pageId; } - 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 index 8cce47a2ab..b281706848 100644 --- 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 @@ -1,4 +1,4 @@ -import { useLocation, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; export function useShareConfig() { const params = useParams(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx index bf2d48d827..ca104218ab 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreButton.tsx @@ -1,8 +1,7 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { Drawer, IconButton } from '@mui/material'; import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; -import { LogoutOutlined } from '@mui/icons-material'; import Tooltip from '@mui/material/Tooltip'; import MoreOptions from '$app/components/layout/TopBar/MoreOptions'; import { useMoreOptionsConfig } from '$app/components/layout/TopBar/MoreOptions.hooks'; @@ -19,7 +18,7 @@ function MoreButton() { return ( <> <Tooltip placement={'bottom-end'} title={t('moreAction.moreOptions')}> - <IconButton onClick={(e) => toggleDrawer(true)} className={'h-8 w-8 text-icon-primary'}> + <IconButton onClick={() => toggleDrawer(true)} className={'h-8 w-8 text-icon-primary'}> <Details2Svg /> </IconButton> </Tooltip> diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.hooks.ts index 57b10a4fed..63f1173885 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.hooks.ts @@ -4,7 +4,8 @@ import { useMemo } from 'react'; export function useMoreOptionsConfig() { const location = useLocation(); - const { type, pageType, id } = useMemo(() => { + const { type, pageType } = useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_, type, pageType, id] = location.pathname.split('/'); return { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx index 97a48128f7..28f2b46759 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/TopBar/MoreOptions.tsx @@ -1,12 +1,8 @@ import React from 'react'; -import { useTranslation } from 'react-i18next'; import FontSizeConfig from '$app/components/layout/TopBar/FontSizeConfig'; -import { Divider } from '@mui/material'; -import { useLocation } from 'react-router-dom'; import { useMoreOptionsConfig } from '$app/components/layout/TopBar/MoreOptions.hooks'; function MoreOptions() { - const { t } = useTranslation(); const { showStyleOptions } = useMoreOptionsConfig(); return <div className={'flex w-[220px] flex-col'}>{showStyleOptions && <FontSizeConfig />}</div>; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx index b2395d7645..bcd171f4c0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/LanguageSetting.tsx @@ -56,7 +56,7 @@ function LanguageSetting({ onChange({ language, }); - i18n.changeLanguage(language); + void i18n.changeLanguage(language); }} > {languages.map((option) => ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx index 5b4ec98294..9fa5beabcf 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/UserSetting/index.tsx @@ -30,7 +30,7 @@ function UserSettings({ open, onClose }: { open: boolean; onClose: () => void }) if (userSettingController) { const language = newSetting.language || 'en'; - userSettingController.setAppearanceSetting({ + void userSettingController.setAppearanceSetting({ theme: newSetting.theme || Theme.Default, theme_mode: newSetting.themeMode || ThemeModePB.Light, locale: { diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx index e2346fa7a9..ad405024a1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/NewPageButton.tsx @@ -12,7 +12,7 @@ function NewPageButton({ workspaceId }: { workspaceId: string }) { useEffect(() => { return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller]); @@ -21,7 +21,7 @@ function NewPageButton({ workspaceId }: { workspaceId: string }) { <button onClick={async () => { const { id } = await controller.createView({ - name: "", + name: '', layout: ViewLayoutPB.Document, parent_view_id: workspaceId, }); 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 index a6e00bbbfe..e04bd734a0 100644 --- 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 @@ -45,7 +45,7 @@ export function useLoadWorkspaces() { })(); return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller, initializeWorkspaces, subscribeToWorkspaces]); @@ -85,6 +85,7 @@ export function useLoadWorkspace(workspace: WorkspaceItem) { const initializeWorkspace = useCallback(async () => { const childPages = await controller.getChildPages(); + dispatch( pagesActions.addChildPages({ id, @@ -106,7 +107,7 @@ export function useLoadWorkspace(workspace: WorkspaceItem) { })(); return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller, initializeWorkspace, subscribeToWorkspace]); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx index bafea58b3a..b30002777d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/layout/WorkspaceManager/Workspace.tsx @@ -1,12 +1,8 @@ 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 WorkspaceTitle from '$app/components/layout/WorkspaceManager/WorkspaceTitle'; function Workspace({ workspace, opened }: { workspace: WorkspaceItem; opened: boolean }) { - const { openWorkspace, deleteWorkspace } = useLoadWorkspace(workspace); - return ( <div className={'flex h-[100%] flex-col'}> <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 index 98a91855a4..eb5ea8a909 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts @@ -1,32 +1,23 @@ -import { - FieldType, - FlowyError, - SingleSelectTypeOptionPB, - ViewLayoutPB, - ViewPB, - WorkspaceSettingPB, -} from '../../../services/backend'; -import { DatabaseController } from '../../stores/effects/database/database_controller'; -import { RowInfo } from '../../stores/effects/database/row/row_cache'; -import { RowController } from '../../stores/effects/database/row/row_controller'; +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, - NumberCellController, SelectOptionCellController, TextCellController, URLCellController, -} from '../../stores/effects/database/cell/controller_builder'; -import { None, Ok, Option, Result, Some } from 'ts-results'; -import { TypeOptionBackendService } from '../../stores/effects/database/field/type_option/type_option_bd_svc'; -import { DatabaseBackendService } from '../../stores/effects/database/database_bd_svc'; -import { FieldInfo } from '../../stores/effects/database/field/field_controller'; -import { TypeOptionController } from '../../stores/effects/database/field/type_option/type_option_controller'; -import { makeSingleSelectTypeOptionContext } from '../../stores/effects/database/field/type_option/type_option_context'; -import { SelectOptionBackendService } from '../../stores/effects/database/cell/select_option_bd_svc'; -import { Log } from '$app/utils/log'; -import { WorkspaceController } from '../../stores/effects/workspace/workspace_controller'; +} from '$app/stores/effects/database/cell/controller_builder'; +import { None, Option, Some } from 'ts-results'; +import { TypeOptionBackendService } from '$app/stores/effects/database/field/type_option/type_option_bd_svc'; +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-folder2'; // Create a database page for specific layout type @@ -36,9 +27,8 @@ export async function createTestDatabaseView(layout: ViewLayoutPB): Promise<View result.unwrap() ); const wsSvc = new WorkspaceController(workspaceSetting.workspace_id); - const viewRes = await wsSvc.createView({ name: 'New Grid', layout }); - return viewRes; + return await wsSvc.createView({ name: 'New Grid', layout }); } export async function openTestDatabase(viewId: string): Promise<DatabaseController> { @@ -92,18 +82,6 @@ export async function makeTextCellController( return Some(builder.build() as TextCellController); } -export async function makeNumberCellController( - fieldId: string, - rowInfo: RowInfo, - databaseController: DatabaseController -): Promise<Option<NumberCellController>> { - const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.Number, databaseController).then( - (result) => result.unwrap() - ); - - return Some(builder.build() as NumberCellController); -} - export async function makeSingleSelectCellController( fieldId: string, rowInfo: RowInfo, @@ -167,7 +145,7 @@ export async function makeURLCellController( export async function makeCellControllerBuilder( fieldId: string, rowInfo: RowInfo, - fieldType: FieldType, + _fieldType: FieldType, databaseController: DatabaseController ): Promise<Option<CellControllerBuilder>> { const rowCache = databaseController.databaseViewCache.getRowCache(); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestDocument.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestDocument.tsx index 8508232eb9..b6f70acb02 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestDocument.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestDocument.tsx @@ -5,7 +5,8 @@ import { DocumentBackendService } from '../../stores/effects/document/document_b async function testCreateDocument() { const view = await createTestDocument(); const svc = new DocumentBackendService(view.id); - const document = await svc.open().then((result) => result.unwrap()); + + await svc.open().then((result) => result.unwrap()); // eslint-disable-next-line @typescript-eslint/no-unused-vars // const content = JSON.parse(document.content); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx index 44585d039a..6e24aae8a1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFolder.tsx @@ -33,7 +33,7 @@ const testCreateFolder = async (userId?: number) => { } for (let i = 1; i <= 3; i++) { - const result = await workspaceService.createView({ + await workspaceService.createView({ name: `test board 1 ${i}`, desc: 'test description', layout: ViewLayoutPB.Board, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx index c2c5d5d13c..04e4c566a6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestFonts.tsx @@ -1,9 +1,9 @@ -import { useState } from 'react'; +import { ChangeEvent, useState } from 'react'; const TestFonts = () => { const [sampleText, setSampleText] = useState('Sample Text'); - const onInputChange = (e: any) => { + const onInputChange = (e: ChangeEvent<HTMLInputElement>) => { setSampleText(e.target.value); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx index d4345b6760..e9e687a923 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx @@ -65,6 +65,7 @@ export const RunAllGridTests = () => { 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); @@ -87,11 +88,13 @@ async function createBuildInGrid() { 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); } @@ -100,6 +103,7 @@ async function testEditGridCell() { 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]; @@ -122,9 +126,11 @@ async function testEditTextCell() { 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]; @@ -135,6 +141,7 @@ async function testEditURLCell() { urlCellController.subscribeChanged({ onCellChanged: (content) => { const pb = content.unwrap(); + Log.info('Receive url data:', pb.url, pb.content); }, }); @@ -149,9 +156,11 @@ async function testEditURLCell() { 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]; @@ -162,11 +171,13 @@ async function testEditDateCell() { 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)); } @@ -174,15 +185,18 @@ async function testEditDateCell() { 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 = await dateTypeOptionContext.getTypeOption().then((a) => a.unwrap()); + 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; @@ -190,6 +204,7 @@ async function testEditDateFormatPB() { await dateTypeOptionContext.setTypeOption(typeOption); const typeOption2 = await dateTypeOptionContext.getTypeOption().then((a) => a.unwrap()); + assert(typeOption2.date_format === DateFormatPB.Local, 'Date format not match'); assert(typeOption2.time_format === TimeFormatPB.TwelveHour, 'Time format not match'); @@ -199,20 +214,24 @@ async function testEditDateFormatPB() { 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 = await dateTypeOptionContext.getTypeOption().then((a) => a.unwrap()); + typeOption.format = NumberFormatPB.EUR; typeOption.name = 'Money'; await dateTypeOptionContext.setTypeOption(typeOption); const typeOption2 = await dateTypeOptionContext.getTypeOption().then((a) => a.unwrap()); + Log.info(typeOption2); await new Promise((resolve) => setTimeout(resolve, 200)); } @@ -220,9 +239,11 @@ async function testEditNumberFormatPB() { 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]; @@ -235,6 +256,7 @@ async function testCheckboxCell() { checkboxCellController.subscribeChanged({ onCellChanged: (content) => { const pb = content.unwrap(); + Log.info('Receive checkbox data:', pb); }, }); @@ -246,6 +268,7 @@ async function testCheckboxCell() { 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); @@ -258,9 +281,11 @@ async function testCreateRow() { 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); @@ -270,12 +295,14 @@ async function testDeleteRow() { 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) { @@ -283,39 +310,47 @@ async function testCreateOptionInCell() { 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 field_id = fieldInfos[0].field.id; + await databaseController.moveField({ fieldId: field_id, fromIndex: 0, toIndex: 1 }); await new Promise((resolve) => setTimeout(resolve, 200)); assert(databaseController.fieldController.fieldInfos[1].field.id === field_id); @@ -324,6 +359,7 @@ async function testMoveField() { 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 @@ -341,6 +377,7 @@ async function testGetSingleSelectFieldData() { // Read options const options = await singleSelectTypeOptionContext.getTypeOption().then((result) => result.unwrap()); + console.log(options); await databaseController.dispose(); @@ -349,6 +386,7 @@ async function testGetSingleSelectFieldData() { 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 @@ -357,6 +395,7 @@ async function testSwitchFromSingleSelectToNumber() { (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 @@ -365,6 +404,7 @@ async function testSwitchFromSingleSelectToNumber() { .getTypeOption() .then((result) => result.unwrap()); const format: NumberFormatPB = numberTypeOption.format; + if (format !== NumberFormatPB.Num) { throw Error('The default format should be number'); } @@ -375,10 +415,12 @@ async function testSwitchFromSingleSelectToNumber() { 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 @@ -391,11 +433,13 @@ async function testSwitchFromMultiSelectToRichText() { 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'); } @@ -403,6 +447,7 @@ async function testSwitchFromMultiSelectToRichText() { if (selectOptionCellData.select_options.length !== 3) { throw Error('The selected options should equal to 3'); } + await selectOptionCellController.dispose(); // Switch to RichText field type @@ -415,6 +460,7 @@ async function testSwitchFromMultiSelectToRichText() { (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()); } @@ -425,14 +471,17 @@ async function testSwitchFromMultiSelectToRichText() { 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)); @@ -443,11 +492,13 @@ async function testEditField() { 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(); @@ -456,6 +507,7 @@ async function testCreateNewField() { 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. @@ -463,6 +515,7 @@ async function testDeleteField() { // 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(); @@ -485,24 +538,31 @@ export const 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); }; @@ -522,6 +582,7 @@ export const TestSwitchFromMultiSelectToText = () => { export const TestMoveField = () => { return TestButton('Test move field', testMoveField); }; + export const TestEditField = () => { return TestButton('Test edit the column name', testEditField); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx index 31ea758924..c161eeeeb1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGroup.tsx @@ -31,6 +31,7 @@ export const TestAllKanbanTests = () => { async function createBuildInBoard() { const view = await createTestDatabaseView(ViewLayoutPB.Board); const databaseController = await openTestDatabase(view.id); + databaseController.subscribe({ onGroupByField: (groups) => { console.log(groups); @@ -51,10 +52,12 @@ async function createBuildInBoard() { 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); @@ -64,11 +67,13 @@ async function createKanbanBoardRow() { 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) => { @@ -101,6 +106,7 @@ async function moveKanbanBoardRow() { }); const row = firstGroup.rowAtIndex(0).unwrap(); + await databaseController.moveGroupRow(row.id, secondGroup.groupId); assert(firstGroup.rows.length === 2); @@ -115,11 +121,13 @@ async function moveKanbanBoardRow() { 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); @@ -130,6 +138,7 @@ async function createKanbanBoardColumn() { 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 @@ -139,6 +148,7 @@ async function createColumnInBoard() { // 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 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 a95ae94a77..9948a2b189 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 @@ -17,7 +17,7 @@ export function useLoadTrash() { }, [controller, dispatch]); const subscribeToTrash = useCallback(async () => { - controller.subscribe({ + await controller.subscribe({ onTrashChanged: (trash) => { dispatch(trashActions.onTrashChanged(trash.map(trashPBToTrash))); }, @@ -33,7 +33,7 @@ export function useLoadTrash() { useEffect(() => { return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller]); @@ -52,7 +52,7 @@ export function useTrashActions() { useEffect(() => { return () => { - controller.dispose(); + void controller.dispose(); }; }, [controller]); 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 23a78a9bef..6387437162 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/trash/Trash.tsx @@ -28,11 +28,11 @@ function Trash() { <div className={'flex items-center justify-between'}> <div className={'text-2xl font-bold'}>{t('trash.text')}</div> <div className={'flex items-center justify-end'}> - <Button color={'inherit'} onClick={(e) => onClickRestoreAll()}> + <Button color={'inherit'} onClick={() => onClickRestoreAll()}> <RestoreOutlined /> <span className={'ml-1'}>{t('trash.restoreAll')}</span> </Button> - <Button color={'error'} onClick={(e) => onClickDeleteAll()}> + <Button color={'error'} onClick={() => onClickDeleteAll()}> <DeleteOutline /> <span className={'ml-1'}>{t('trash.deleteAll')}</span> </Button> diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx index 78ce773423..3d970257fd 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/trash/TrashItem.tsx @@ -23,10 +23,10 @@ function TrashItem({ return ( <ListItem - onMouseEnter={(e) => { + onMouseEnter={() => { setHoverId(item.id); }} - onMouseLeave={(e) => { + onMouseLeave={() => { setHoverId(''); }} key={item.id} 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 index 4e3a57af4b..866d910e83 100644 --- 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 @@ -10,6 +10,7 @@ export class UserNotificationParser extends NotificationParser<UserNotification> params.callback, (ty) => { const notification = UserNotification[ty]; + if (isUserNotification(notification)) { return UserNotification[notification]; } else { 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 index d63963eaf0..112ee9b6a2 100644 --- 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 @@ -18,6 +18,7 @@ export class UserNotificationListener extends AFNotificationObserver<UserNotific } else { this.onProfileUpdate?.(result); } + break; default: break; @@ -26,6 +27,7 @@ export class UserNotificationListener extends AFNotificationObserver<UserNotific id: params.userId, onError: params.onError, }); + super(parser); this.onProfileUpdate = params.onProfileUpdate; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts new file mode 100644 index 0000000000..6711ece8c8 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/hooks/ViewId.hooks.ts @@ -0,0 +1,6 @@ +import { createContext, useContext } from 'react'; + +const ViewIdContext = createContext(''); + +export const ViewIdProvider = ViewIdContext.Provider; +export const useViewId = () => useContext(ViewIdContext); diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts b/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts index d9223c236b..c29ddd04aa 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/hooks/index.ts @@ -1 +1,2 @@ export * from './notification.hooks'; +export * from './ViewId.hooks'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts b/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts index 87327b71be..5c1efe43db 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/hooks/notification.hooks.ts @@ -1,9 +1,8 @@ /* eslint-disable no-redeclare */ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { useEffect } from 'react'; import { listen } from '@tauri-apps/api/event'; -import { Ok, Err, Result } from 'ts-results'; import { SubscribeObject } from '@/services/backend/models/flowy-notification'; -import { FlowyError } from '@/services/backend/models/flowy-error'; import { DatabaseFieldChangesetPB, DatabaseNotification, @@ -14,33 +13,39 @@ import { ReorderSingleRowPB, RowsChangePB, RowsVisibilityChangePB, + SortChangesetNotificationPB, } from '@/services/backend'; const NotificationPBMap = { [DatabaseNotification.DidUpdateViewRowsVisibility]: RowsVisibilityChangePB, [DatabaseNotification.DidUpdateViewRows]: RowsChangePB, [DatabaseNotification.DidReorderRows]: ReorderAllRowsPB, - [DatabaseNotification.DidReorderSingleRow]:ReorderSingleRowPB, - [DatabaseNotification.DidUpdateFields]:DatabaseFieldChangesetPB, - [DatabaseNotification.DidGroupByField]:GroupChangesPB, - [DatabaseNotification.DidUpdateNumOfGroups]:GroupChangesPB, + [DatabaseNotification.DidReorderSingleRow]: ReorderSingleRowPB, + [DatabaseNotification.DidUpdateFields]: DatabaseFieldChangesetPB, + [DatabaseNotification.DidGroupByField]: GroupChangesPB, + [DatabaseNotification.DidUpdateNumOfGroups]: GroupChangesPB, [DatabaseNotification.DidUpdateGroupRow]: GroupRowsNotificationPB, [DatabaseNotification.DidUpdateField]: FieldPB, [DatabaseNotification.DidUpdateCell]: null, + [DatabaseNotification.DidUpdateSort]: SortChangesetNotificationPB, }; type NotificationMap = typeof NotificationPBMap; type NotificationEnum = keyof NotificationMap; -type NullableInstanceType<K extends ((abstract new (...args: any) => any) | null)> = K extends (abstract new (...args: any) => any) ? InstanceType<K> : void; +type NullableInstanceType<K extends (abstract new (...args: any) => any) | null> = K extends abstract new ( + ...args: any +) => any + ? InstanceType<K> + : void; -type NotificationHandler<K extends NotificationEnum> = (result: Result<NullableInstanceType<NotificationMap[K]>, FlowyError>) => void; +type NotificationHandler<K extends NotificationEnum> = (result: NullableInstanceType<NotificationMap[K]>) => void; /** * Subscribes to a set of notifications. - * - * This function subscribes to notifications defined by the `NotificationEnum` and + * + * This function subscribes to notifications defined by the `NotificationEnum` and * calls the appropriate `NotificationHandler` when each type of notification is received. * * @param {Object} callbacks - An object containing handlers for various notification types. @@ -48,9 +53,9 @@ type NotificationHandler<K extends NotificationEnum> = (result: Result<NullableI * * @param {Object} [options] - Optional settings for the subscription. * @param {string} [options.id] - An optional ID. If provided, only notifications with a matching ID will be processed. - * + * * @returns {Promise<() => void>} A Promise that resolves to an unsubscribe function. - * + * * @example * subscribeNotifications({ * [DatabaseNotification.DidUpdateField]: (result) => { @@ -75,16 +80,16 @@ type NotificationHandler<K extends NotificationEnum> = (result: Result<NullableI * // ... * // To unsubscribe, call `unsubscribe()` * }); - * + * * @throws {Error} Throws an error if unable to subscribe. */ export function subscribeNotifications( callbacks: { [K in NotificationEnum]?: NotificationHandler<K>; }, - options?: { id?: string }, + options?: { id?: string } ): Promise<() => void> { - return listen<ReturnType<typeof SubscribeObject.prototype.toObject>>('af-notification', event => { + return listen<ReturnType<typeof SubscribeObject.prototype.toObject>>('af-notification', (event) => { const subject = SubscribeObject.fromObject(event.payload); const { id, ty } = subject; @@ -101,13 +106,12 @@ export function subscribeNotifications( } if (subject.has_error) { - const error = FlowyError.deserializeBinary(subject.error); - - callback(Err(error)); + // const error = FlowyError.deserialize(subject.error); + return; } else { const { payload } = subject; - callback(pb ? Ok(pb.deserializeBinary(payload)) : Ok.EMPTY); + pb ? callback(pb.deserialize(payload)) : callback(); } }); } @@ -115,7 +119,7 @@ export function subscribeNotifications( export function subscribeNotification<K extends NotificationEnum>( notification: K, callback: NotificationHandler<K>, - options?: { id?: string }, + options?: { id?: string } ): Promise<() => void> { return subscribeNotifications({ [notification]: callback }, options); } @@ -123,7 +127,7 @@ export function subscribeNotification<K extends NotificationEnum>( export function useNotification<K extends NotificationEnum>( notification: K, callback: NotificationHandler<K>, - options: { id?: string }, + options: { id?: string } ): void { const { id } = options; @@ -131,7 +135,7 @@ export function useNotification<K extends NotificationEnum>( const unsubscribePromise = subscribeNotification(notification, callback, { id }); return () => { - void unsubscribePromise.then(fn => fn()); + void unsubscribePromise.then((fn) => fn()); }; }, [callback, id, notification]); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts b/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts index 59f0d77cf2..202b6a4220 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/i18n/config.ts @@ -3,7 +3,7 @@ import LanguageDetector from 'i18next-browser-languagedetector'; import { initReactI18next } from 'react-i18next'; import resourcesToBackend from 'i18next-resources-to-backend'; -i18next +void i18next .use(resourcesToBackend((language: string) => import(`./translations/${language}.json`))) .use(LanguageDetector) .use(initReactI18next) diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/index.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/index.ts deleted file mode 100644 index a478f9c12d..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './types'; -export * from './transform'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/transform.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/transform.ts deleted file mode 100644 index cb35f1e8e2..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/transform.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { CellPB, ChecklistCellDataPB, DateCellDataPB, FieldPB, FieldType, SelectOptionCellDataPB, URLCellDataPB } from '@/services/backend'; -import type { Database } from './types'; - -export const fieldPbToField = (fieldPb: FieldPB): Database.Field => ({ - id: fieldPb.id, - name: fieldPb.name, - type: fieldPb.field_type, - visibility: fieldPb.visibility, - width: fieldPb.width, - isPrimary: fieldPb.is_primary, -}); - -const toDateCellData = (pb: DateCellDataPB): Database.DateTimeCellData => ({ - date: pb.date, - time: pb.time, - timestamp: pb.timestamp, - includeTime: pb.include_time, -}); - -const toSelectCellData = (pb: SelectOptionCellDataPB): Database.SelectCellData => { - return { - options: pb.options.map(option => ({ - id: option.id, - name: option.name, - color: option.color, - })), - selectOptions: pb.select_options.map(option => ({ - id: option.id, - name: option.name, - color: option.color, - })), - }; -}; - -const toURLCellData = (pb: URLCellDataPB): Database.UrlCellData => ({ - url: pb.url, - content: pb.content, -}); - -const toChecklistCellData = (pb: ChecklistCellDataPB): Database.ChecklistCellData => ({ - selectedOptions: pb.selected_options.map(({ id }) => id), - percentage: pb.percentage, -}); - -function parseCellData(fieldType: FieldType, data: Uint8Array) { - switch (fieldType) { - case FieldType.RichText: - case FieldType.Number: - case FieldType.Checkbox: - return new TextDecoder().decode(data); - case FieldType.DateTime: - case FieldType.LastEditedTime: - case FieldType.CreatedTime: - return toDateCellData(DateCellDataPB.deserializeBinary(data)); - case FieldType.SingleSelect: - case FieldType.MultiSelect: - return toSelectCellData(SelectOptionCellDataPB.deserializeBinary(data)); - case FieldType.URL: - return toURLCellData(URLCellDataPB.deserializeBinary(data)); - case FieldType.Checklist: - return toChecklistCellData(ChecklistCellDataPB.deserializeBinary(data)); - } -} - -export const cellPbToCell = (cellPb: CellPB, fieldType: FieldType): Database.Cell => { - return { - rowId: cellPb.row_id, - fieldId: cellPb.field_id, - fieldType: fieldType, - data: parseCellData(fieldType, cellPb.data), - }; -}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/types.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/types.ts deleted file mode 100644 index 445406c490..0000000000 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/database/types.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { - CalendarLayoutPB, - DatabaseLayoutPB, - DateFormatPB, - FieldType, - NumberFormatPB, - SelectOptionColorPB, - SelectOptionConditionPB, - SortConditionPB, - TextFilterConditionPB, - TimeFormatPB, -} from '@/services/backend'; - - -export interface Database { - id: string; - viewId: string; - name: string; - fields: Database.UndeterminedField[]; - rows: Database.Row[]; - layoutType: DatabaseLayoutPB; - layoutSetting: Database.GridLayoutSetting | Database.CalendarLayoutSetting; - isLinked: boolean; -} - -// eslint-disable-next-line @typescript-eslint/no-namespace, no-redeclare -export namespace Database { - - export interface GridLayoutSetting { - filters?: UndeterminedFilter[]; - groups?: Group[]; - sorts?: Sort[]; - } - - export interface CalendarLayoutSetting { - fieldId?: string; - layoutTy?: CalendarLayoutPB; - firstDayOfWeek?: number; - showWeekends?: boolean; - showWeekNumbers?: boolean; - } - - export interface Field { - id: string; - name: string; - type: FieldType; - typeOption?: unknown; - visibility?: boolean; - width?: number; - isPrimary?: boolean; - } - - export interface NumberTypeOption { - format?: NumberFormatPB; - scale?: number; - symbol?: string; - name?: string; - } - - export interface NumberField extends Field { - type: FieldType.Number; - typeOption: NumberTypeOption; - } - - export interface DateTimeTypeOption { - dateFormat?: DateFormatPB; - timeFormat?: TimeFormatPB; - timezoneId?: string; - fieldType?: FieldType; - } - - export interface DateTimeField extends Field { - type: FieldType.DateTime; - typeOption: DateTimeTypeOption; - } - - export interface SelectOption { - id: string; - name: string; - color: SelectOptionColorPB; - } - - export interface SelectTypeOption { - options?: SelectOption[]; - disableColor?: boolean; - } - - export interface SelectField extends Field { - type: FieldType.SingleSelect | FieldType.MultiSelect; - typeOption: SelectTypeOption; - } - - export interface ChecklistTypeOption { - config?: string; - } - - export interface ChecklistField extends Field { - type: FieldType.Checklist; - typeOption: ChecklistTypeOption; - } - - export type UndeterminedField = NumberField | DateTimeField | SelectField | ChecklistField | Field; - - export interface Sort { - id: string; - fieldId: string; - fieldType: FieldType; - condition: SortConditionPB; - } - - export interface Group { - id: string; - fieldId: string; - } - - export interface Filter { - id: string; - fieldId: string; - fieldType: FieldType; - data: unknown; - } - - export interface TextFilter extends Filter { - fieldType: FieldType.RichText; - data: TextFilterCondition; - } - - export interface TextFilterCondition { - condition?: TextFilterConditionPB; - content?: string; - } - - export interface SelectFilter extends Filter { - fieldType: FieldType.SingleSelect | FieldType.MultiSelect; - data: SelectFilterCondition; - } - - export interface SelectFilterCondition { - condition?: SelectOptionConditionPB; - /** - * link to [SelectOption's id property]{@link SelectOption#id}. - */ - optionIds?: string[]; - } - - export type UndeterminedFilter = TextFilter | SelectFilter | Filter; - - export interface Row { - id: string; - documentId?: string; - icon?: string; - cover?: string; - createdAt?: number; - modifiedAt?: number; - height?: number; - visibility?: boolean; - } - - export interface Cell { - rowId: string; - fieldId: string; - fieldType: FieldType; - data: unknown; - } - - export interface TextCell extends Cell { - fieldType: FieldType.RichText; - data: string; - } - - export interface NumberCell extends Cell { - fieldType: FieldType.Number; - data: string; - } - - export interface CheckboxCell extends Cell { - fieldType: FieldType.Checkbox; - data: 'Yes' | 'No'; - } - - export interface UrlCell extends Cell { - fieldType: FieldType.URL; - data: UrlCellData; - } - - export interface UrlCellData { - url: string; - content?: string; - } - - export interface SelectCell extends Cell { - fieldType: FieldType.SingleSelect | FieldType.MultiSelect; - data: SelectCellData; - } - - export interface SelectCellData { - options?: SelectOption[]; - selectOptions?: SelectOption[]; - } - - export interface DateTimeCell extends Cell { - fieldType: FieldType.DateTime; - data: DateTimeCellData; - } - - export interface DateTimeCellData { - date?: string; - time?: string; - timestamp?: number; - includeTime?: boolean; - } - - export interface ChecklistCell extends Cell { - fieldType: FieldType.Checklist; - data: ChecklistCellData; - } - - export interface ChecklistCellData { - /** - * link to [SelectOption's id property]{@link SelectOption#id}. - */ - selectedOptions?: string[]; - percentage?: number; - } - - export type UndeterminedCell = TextCell | NumberCell | DateTimeCell | SelectCell | CheckboxCell | UrlCell | ChecklistCell; -} diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts index a7a122dc29..e66edd77af 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/interfaces/document.ts @@ -3,12 +3,6 @@ import { BlockActionTypePB } from '@/services/backend'; import { Sources } from 'quill'; import React from 'react'; -export interface DocumentBlockJSON { - type: BlockType; - data: BlockData<any>; - children: DocumentBlockJSON[]; -} - export interface RangeStatic { id: string; length: number; @@ -61,11 +55,9 @@ export interface QuoteBlockData extends TextBlockData { export interface CalloutBlockData extends TextBlockData { icon: string; } - +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type TextBlockData = Record<string, any>; -export interface DividerBlockData {} - export enum Align { Left = 'left', Center = 'center', @@ -88,8 +80,11 @@ export interface PageBlockData extends TextBlockData { cover?: string; coverType?: CoverType; } +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Data = any; -export type BlockData<Type> = Type extends BlockType.HeadingBlock +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type BlockData<Type = any> = Type extends BlockType.HeadingBlock ? HeadingBlockData : Type extends BlockType.PageBlock ? PageBlockData @@ -103,8 +98,6 @@ export type BlockData<Type> = Type extends BlockType.HeadingBlock ? NumberedListBlockData : Type extends BlockType.ToggleListBlock ? ToggleListBlockData - : Type extends BlockType.DividerBlock - ? DividerBlockData : Type extends BlockType.CalloutBlock ? CalloutBlockData : Type extends BlockType.EquationBlock @@ -113,12 +106,13 @@ export type BlockData<Type> = Type extends BlockType.HeadingBlock ? ImageBlockData : Type extends BlockType.TextBlock ? TextBlockData - : any; + : Data; +// eslint-disable-next-line @typescript-eslint/no-explicit-any export interface NestedBlock<Type = any> { id: string; type: BlockType; - data: BlockData<Type> | any; + data: BlockData<Type> | Data; parent: string | null; children: string; externalId?: string; @@ -152,7 +146,6 @@ export interface SlashCommandState { export enum SlashCommandOptionKey { TEXT, - PAGE, TODO, BULLET, NUMBER, @@ -170,7 +163,7 @@ export enum SlashCommandOptionKey { export interface SlashCommandOption { type: BlockType; - data?: BlockData<any>; + data?: BlockData; key: SlashCommandOptionKey; } @@ -273,7 +266,7 @@ export interface BlockConfig { /** * The default data of the block */ - defaultData?: BlockData<any>; + defaultData?: BlockData; /** * The props that will be passed to the text split function diff --git a/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts b/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts index d10ea3cdff..7d1466630a 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/interfaces/index.ts @@ -1,7 +1,6 @@ import { ThemeModePB as ThemeMode } from '@/services/backend'; export { ThemeMode }; -export interface Document {} export interface UserSetting { theme?: Theme; 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 index 99d3a64f06..30ec4e5812 100644 --- 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 @@ -18,6 +18,7 @@ class CellBackendService { row_id: cellId.rowId, cell_changeset: data, }); + return DatabaseEventUpdateCell(payload); }; 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 index a19ad13d27..d81415660c 100644 --- 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 @@ -14,6 +14,7 @@ export class CellCache { remove = (key: CellCacheKey) => { const cellDataByRowId = this.cellDataByFieldId.get(key.fieldId); + if (cellDataByRowId !== undefined) { cellDataByRowId.delete(key.rowId); } @@ -25,8 +26,10 @@ export class CellCache { 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 { @@ -36,10 +39,12 @@ export class CellCache { 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; } 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 index d569c547ae..a753fb9ab1 100644 --- 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 @@ -55,6 +55,7 @@ export class CellController<T, D> { if (this.cellDataLoader.reloadOnFieldChanged) { await this._loadCellData(); } + this.subscribeCallbacks?.onFieldChanged?.(); }, }); @@ -71,6 +72,7 @@ export class CellController<T, D> { getTypeOption = async <P extends TypeOptionParser<PD>, PD>(parser: P) => { const result = await this.fieldBackendService.getTypeOptionData(this.cellIdentifier.fieldType); + if (result.ok) { return Ok(parser.fromBuffer(result.val.type_option_data)); } else { @@ -80,6 +82,7 @@ export class CellController<T, D> { saveCellData = async (data: D) => { const result = await this.cellDataPersistence.save(data); + if (result.err) { Log.error(result.val); } @@ -90,17 +93,21 @@ export class CellController<T, D> { /// 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; 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 index dba0290d7a..0cd5ae35c2 100644 --- 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 @@ -29,6 +29,7 @@ export class CellObserver { } else { this.notifier?.notify(result); } + return; default: break; 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 index 86b2198b4e..80afed8f9f 100644 --- 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 @@ -1,4 +1,3 @@ -import utf8 from 'utf8'; import { CellBackendService, CellIdentifier } from './cell_bd_svc'; import { SelectOptionCellDataPB, URLCellDataPB, DateCellDataPB } from '@/services/backend'; import { Err, None, Ok, Option, Some } from 'ts-results'; @@ -19,6 +18,7 @@ class CellDataLoader<T> { loadData = async () => { const result = await this.service.getCell(this.cellId); + if (result.ok) { return Ok(this.parser.parserData(result.val.data)); } else { @@ -48,6 +48,7 @@ class SelectOptionCellDataParser extends CellDataParser<SelectOptionCellDataPB> if (data.length === 0) { return None; } + return Some(SelectOptionCellDataPB.deserializeBinary(data)); } } @@ -57,6 +58,7 @@ class URLCellDataParser extends CellDataParser<URLCellDataPB> { if (data.length === 0) { return None; } + return Some(URLCellDataPB.deserializeBinary(data)); } } 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 index 9066b61150..ea8e9e2953 100644 --- 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 @@ -25,10 +25,12 @@ export class DateCellDataPersistence extends CellDataPersistence<CalendarData> { 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); } 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 index 068ae60797..5e3e86fadc 100644 --- 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 @@ -40,6 +40,7 @@ export class SelectOptionCellBackendService { }); const result = await DatabaseEventCreateSelectOption(payload); + if (result.ok) { return await this._insertOption(result.val, params.isSelect || true); } else { @@ -47,7 +48,7 @@ export class SelectOptionCellBackendService { } }; - private _insertOption = (option: SelectOptionPB, isSelect: boolean) => { + private _insertOption = (option: SelectOptionPB, _: boolean) => { const payload = RepeatedSelectOptionPayload.fromObject({ view_id: this.cellIdentifier.viewId, field_id: this.cellIdentifier.fieldId, @@ -73,6 +74,7 @@ export class SelectOptionCellBackendService { field_id: this.cellIdentifier.fieldId, row_id: this.cellIdentifier.rowId, }); + payload.items.push(...options); return DatabaseEventDeleteSelectOption(payload); }; @@ -83,12 +85,14 @@ export class SelectOptionCellBackendService { 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); }; 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 index 7a03c82beb..a38dc176c4 100644 --- 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 @@ -19,7 +19,6 @@ import { MoveGroupRowPayloadPB, MoveRowPayloadPB, RowIdPB, - DatabaseEventUpdateDatabaseSetting, DuplicateFieldPayloadPB, DatabaseEventDuplicateField, } from '@/services/backend/events/flowy-database2'; @@ -92,6 +91,7 @@ export class DatabaseBackendService { 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); }; 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 index f4e94f4790..39d1db308a 100644 --- 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 @@ -76,7 +76,6 @@ export class DatabaseController { // load database initial data await this.fieldController.loadFields(database.fields); - const loadGroupResult = await this.loadGroup(); this.databaseViewCache.initializeWithRows(database.rows); 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 index 3580aece0b..10c9010678 100644 --- 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 @@ -55,11 +55,13 @@ export class FieldBackendService { 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 index 46902ddef4..2234c06dbd 100644 --- 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 @@ -29,6 +29,7 @@ export class FieldController { 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 { @@ -47,6 +48,7 @@ export class FieldController { onFieldsChanged: (result) => { if (result.ok) { const changeset = result.val; + this._deleteFields(changeset.deleted_fields); this._insertFields(changeset.inserted_fields); this._updateFields(changeset.updated_fields); @@ -66,6 +68,7 @@ export class FieldController { const predicate = (element: FieldInfo): boolean => { return !deletedFieldIds.includes(element.field.id); }; + this.numOfFieldsNotifier.fieldInfos = [...this.fieldInfos].filter(predicate); }; @@ -73,9 +76,12 @@ export class FieldController { 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 { @@ -91,10 +97,12 @@ export class FieldController { } 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)); } 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 index d6a3e307b2..162c5c972a 100644 --- 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 @@ -26,6 +26,7 @@ export class DatabaseFieldChangesetObserver { } else { this.notifier?.notify(result); } + return; default: break; @@ -64,6 +65,7 @@ export class DatabaseFieldObserver { } else { this._notifier?.notify(result); } + break; default: break; 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 index 31da01b9d8..240615a2e6 100644 --- 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 @@ -10,6 +10,7 @@ export class TypeOptionBackendService { createTypeOption = (fieldType: FieldType) => { const payload = CreateFieldPayloadPB.fromObject({ view_id: this.viewId, field_type: fieldType }); + return DatabaseEventCreateTypeOption(payload); }; @@ -19,6 +20,7 @@ export class TypeOptionBackendService { field_id: fieldId, field_type: fieldType, }); + return DatabaseEventGetTypeOption(payload); }; @@ -28,6 +30,7 @@ export class TypeOptionBackendService { 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 index 7e1f55bc30..ce1b05251d 100644 --- 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 @@ -22,6 +22,7 @@ abstract class TypeOptionSerde<T> { // RichText export function makeRichTextTypeOptionContext(controller: TypeOptionController): RichTextTypeOptionContext { const parser = new RichTextTypeOptionSerde(); + return new TypeOptionContext<string>(parser, controller); } @@ -40,6 +41,7 @@ class RichTextTypeOptionSerde extends TypeOptionSerde<string> { // Number export function makeNumberTypeOptionContext(controller: TypeOptionController): NumberTypeOptionContext { const parser = new NumberTypeOptionSerde(); + return new TypeOptionContext<NumberTypeOptionPB>(parser, controller); } @@ -58,6 +60,7 @@ class NumberTypeOptionSerde extends TypeOptionSerde<NumberTypeOptionPB> { // Checkbox export function makeCheckboxTypeOptionContext(controller: TypeOptionController): CheckboxTypeOptionContext { const parser = new CheckboxTypeOptionSerde(); + return new TypeOptionContext<CheckboxTypeOptionPB>(parser, controller); } @@ -76,6 +79,7 @@ class CheckboxTypeOptionSerde extends TypeOptionSerde<CheckboxTypeOptionPB> { // URL export function makeURLTypeOptionContext(controller: TypeOptionController): URLTypeOptionContext { const parser = new URLTypeOptionSerde(); + return new TypeOptionContext<URLTypeOptionPB>(parser, controller); } @@ -94,6 +98,7 @@ class URLTypeOptionSerde extends TypeOptionSerde<URLTypeOptionPB> { // Date export function makeDateTypeOptionContext(controller: TypeOptionController): DateTypeOptionContext { const parser = new DateTypeOptionSerde(); + return new TypeOptionContext<DateTypeOptionPB>(parser, controller); } @@ -112,6 +117,7 @@ class DateTypeOptionSerde extends TypeOptionSerde<DateTypeOptionPB> { // SingleSelect export function makeSingleSelectTypeOptionContext(controller: TypeOptionController): SingleSelectTypeOptionContext { const parser = new SingleSelectTypeOptionSerde(); + return new TypeOptionContext<SingleSelectTypeOptionPB>(parser, controller); } @@ -130,6 +136,7 @@ class SingleSelectTypeOptionSerde extends TypeOptionSerde<SingleSelectTypeOption // Multi-select export function makeMultiSelectTypeOptionContext(controller: TypeOptionController): MultiSelectTypeOptionContext { const parser = new MultiSelectTypeOptionSerde(); + return new TypeOptionContext<MultiSelectTypeOptionPB>(parser, controller); } @@ -148,6 +155,7 @@ class MultiSelectTypeOptionSerde extends TypeOptionSerde<MultiSelectTypeOptionPB // Checklist export function makeChecklistTypeOptionContext(controller: TypeOptionController): ChecklistTypeOptionContext { const parser = new ChecklistTypeOptionSerde(); + return new TypeOptionContext<ChecklistTypeOptionPB>(parser, controller); } @@ -184,8 +192,10 @@ export class TypeOptionContext<T> { getTypeOption = async (): Promise<Result<T, FlowyError>> => { const result = await this.controller.getTypeOption(); + if (result.ok) { const typeOption = this.parser.deserialize(result.val.type_option_data); + this.typeOption = Some(typeOption); return Ok(typeOption); } else { 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 index a13196a236..a315140d94 100644 --- 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 @@ -48,18 +48,23 @@ export class TypeOptionController { throw Error('Unexpected empty type option data. Should call initialize first'); } } + return new FieldInfo(this.typeOptionData.val.field); }; switchToField = async (fieldType: FieldType) => { const result = await this.typeOptionBackendSvc.updateTypeOptionType(this.fieldId, fieldType); + if (result.ok) { const getResult = await this.typeOptionBackendSvc.getTypeOption(this.fieldId, fieldType); + if (getResult.ok) { this.updateTypeOptionData(getResult.val); } + return getResult; } + return result; }; @@ -114,6 +119,7 @@ export class TypeOptionController { if (this.fieldBackendSvc === undefined) { Log.error('Unexpected empty field backend service'); } + return this.fieldBackendSvc?.deleteField(); }; @@ -121,6 +127,7 @@ export class TypeOptionController { if (this.fieldBackendSvc === undefined) { Log.error('Unexpected empty field backend service'); } + return this.fieldBackendSvc?.duplicateField(); }; @@ -130,6 +137,7 @@ export class TypeOptionController { if (result.ok) { this.updateTypeOptionData(result.val); } + return result; }); }; @@ -139,6 +147,7 @@ export class TypeOptionController { if (result.ok) { this.updateTypeOptionData(result.val); } + return result; }); }; 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 index 71e498bf9a..0a5c8de4af 100644 --- 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 @@ -1,11 +1,4 @@ -import { - DatabaseNotification, - FlowyError, - GroupPB, - GroupRowsNotificationPB, - RowMetaPB, - RowPB, -} from '@/services/backend'; +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'; @@ -48,6 +41,7 @@ export class DatabaseGroupController { if (this.group.rows.length < index) { return None; } + return Some(this.group.rows[index]); }; @@ -56,6 +50,7 @@ export class DatabaseGroupController { 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); @@ -65,6 +60,7 @@ export class DatabaseGroupController { // 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 { @@ -82,6 +78,7 @@ export class DatabaseGroupController { // 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); @@ -134,6 +131,7 @@ class GroupDataObserver { } else { this.notifier?.notify(result); } + return; default: break; 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 index 46cfb10961..7967351145 100644 --- 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 @@ -33,6 +33,7 @@ export class DatabaseGroupObserver { } else { this.groupByNotifier?.notify(result); } + break; case DatabaseNotification.DidUpdateNumOfGroups: if (result.ok) { @@ -40,6 +41,7 @@ export class DatabaseGroupObserver { } else { this.groupChangesetNotifier?.notify(result); } + break; default: break; 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 index 04a819d884..210258cde8 100644 --- 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 @@ -11,6 +11,7 @@ export class DatabaseNotificationObserver extends AFNotificationObserver<Databas 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 index 264a6036bd..caac06d1ba 100644 --- 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 @@ -10,6 +10,7 @@ export class DatabaseNotificationParser extends NotificationParser<DatabaseNotif params.callback, (ty) => { const notification = DatabaseNotification[ty]; + if (isDatabaseNotification(notification)) { return DatabaseNotification[notification]; } else { 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 index 8cf7bc6230..a353fa6198 100644 --- 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 @@ -1,9 +1,7 @@ import { - RowPB, InsertedRowPB, UpdatedRowPB, RowIdPB, - OptionalRowPB, RowsChangePB, RowsVisibilityChangePB, ReorderSingleRowPB, @@ -13,7 +11,7 @@ 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 { DatabaseEventGetRow, DatabaseEventGetRowMeta } from '@/services/backend/events/flowy-database2'; +import { DatabaseEventGetRowMeta } from '@/services/backend/events/flowy-database2'; import { None, Option, Some } from 'ts-results'; import { Log } from '$app/utils/log'; @@ -40,10 +38,12 @@ export class RowCache { 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()); @@ -101,6 +101,7 @@ export class RowCache { 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); @@ -109,24 +110,29 @@ export class RowCache { 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); } @@ -137,6 +143,7 @@ export class RowCache { 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); } @@ -149,9 +156,11 @@ export class RowCache { } const rowInfos: RowInfo[] = []; + updatedRows.forEach((updatedRow) => { updatedRow.field_ids.forEach((fieldId) => { const key = new CellCacheKey(fieldId, updatedRow.row_meta.id); + this.cellCache.remove(key); }); @@ -159,6 +168,7 @@ export class RowCache { }); const updatedIndexs = this.rowList.insertRows(rowInfos); + updatedIndexs.forEach((row) => { this.notifier.withChange(RowChangedReason.Update, row.rowId); }); @@ -167,6 +177,7 @@ export class RowCache { 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); } @@ -196,6 +207,7 @@ export class RowCache { fieldInfos.forEach((fieldInfo) => { const identifier = new CellIdentifier(this.viewId, rowId, fieldInfo.field.id, fieldInfo.field.field_type); + cellIdentifierByFieldId.set(fieldInfo.field.id, identifier); }); @@ -213,6 +225,7 @@ class RowList { getRow = (rowId: string): Option<RowInfo> => { const rowInfo = this._rowInfoByRowId.get(rowId); + if (rowInfo === undefined) { return None; } else { @@ -221,23 +234,29 @@ class RowList { }; 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 { @@ -249,8 +268,10 @@ class RowList { 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); @@ -263,13 +284,16 @@ class RowList { 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 @@ -285,8 +309,10 @@ class RowList { 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); @@ -299,8 +325,10 @@ class RowList { 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); } @@ -312,6 +340,7 @@ class RowList { this._rowInfos = []; rowIds.forEach((rowId) => { const rowInfo = this._rowInfoByRowId.get(rowId); + if (rowInfo !== undefined) { this._rowInfos.push(rowInfo); } @@ -324,6 +353,7 @@ class RowList { setFieldInfos = (fieldInfos: readonly FieldInfo[]) => { const newRowInfos: RowInfo[] = []; + this._rowInfos.forEach((rowInfo) => { newRowInfos.push(rowInfo.copyWith({ fieldInfos: fieldInfos })); }); @@ -371,6 +401,7 @@ export class RowChangeNotifier extends ChangeNotifier<RowChanged> { withChange = (reason: RowChangedReason, rowId?: string) => { const newChange = new RowChanged(reason, rowId); + if (this._currentChanged !== newChange) { this._currentChanged = newChange; this.notify(this._currentChanged); 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 index e099d3ba6b..072dae5189 100644 --- 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 @@ -1,7 +1,7 @@ import { DatabaseViewRowsObserver } from './view_row_observer'; import { RowCache, RowInfo } from '../row/row_cache'; import { FieldController } from '../field/field_controller'; -import { RowMetaPB, RowPB } from '@/services/backend'; +import { RowMetaPB } from '@/services/backend'; export class DatabaseViewCache { private readonly rowsObserver: DatabaseViewRowsObserver; 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 index 7bc66f9115..2b7a67e4f1 100644 --- 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 @@ -47,6 +47,7 @@ export class DatabaseViewRowsObserver { } else { this.rowsVisibilityNotifier.notify(result); } + break; case DatabaseNotification.DidUpdateViewRows: if (result.ok) { @@ -54,6 +55,7 @@ export class DatabaseViewRowsObserver { } else { this.rowsNotifier.notify(result); } + break; case DatabaseNotification.DidReorderRows: if (result.ok) { @@ -61,6 +63,7 @@ export class DatabaseViewRowsObserver { } else { this.reorderRowsNotifier.notify(result); } + break; case DatabaseNotification.DidReorderSingleRow: if (result.ok) { @@ -68,6 +71,7 @@ export class DatabaseViewRowsObserver { } else { this.reorderSingleRowNotifier.notify(result); } + break; default: break; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/observer.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/observer.ts index ee149e5718..0d7a3d97d1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/observer.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/observer.ts @@ -11,6 +11,7 @@ export class DocumentNotificationObserver extends AFNotificationObserver<Documen callback: params.parserHandler, id: params.viewId, }); + super(parser); } } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/parser.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/parser.ts index e29956803b..8609148153 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/parser.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/document/notifications/parser.ts @@ -10,6 +10,7 @@ export class DocumentNotificationParser extends NotificationParser<DocumentNotif params.callback, (ty) => { const notification = DocumentNotification[ty]; + if (isDocumentNotification(notification)) { return DocumentNotification[notification]; } else { 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 index 38f5850f3f..7306fcfaea 100644 --- 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 @@ -1,8 +1,6 @@ import { nanoid } from '@reduxjs/toolkit'; import { AppearanceSettingsPB, - AuthTypePB, - ThemeModePB, UserEventGetAppearanceSetting, UserEventGetUserProfile, UserEventGetUserSetting, @@ -13,7 +11,6 @@ import { UserEventUpdateUserProfile, } from '@/services/backend/events/flowy-user'; import { - BlockActionPB, CreateWorkspacePayloadPB, SignInPayloadPB, SignUpPayloadPB, @@ -72,6 +69,7 @@ export class UserBackendService { openWorkspace = (workspaceId: string) => { const payload = WorkspaceIdPB.fromObject({ value: workspaceId }); + return FolderEventOpenWorkspace(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 index 5bdea28e39..95d0a2a5b0 100644 --- 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 @@ -11,8 +11,8 @@ export class PageController { // } - dispose = () => { - this.observer.unsubscribe(); + dispose = async () => { + await this.observer.unsubscribe(); }; createPage = async (params: { name: string; layout: ViewLayoutPB }): Promise<string> => { @@ -56,7 +56,6 @@ export class PageController { getPage = async (id?: string): Promise<Page> => { const result = await this.backendService.getPage(id || this.id); - if (result.ok) { return parserViewPBToPage(result.val); } @@ -76,8 +75,10 @@ export class PageController { const res = ViewPB.deserializeBinary(payload); const page = parserViewPBToPage(ViewPB.deserializeBinary(payload)); const childPages = res.child_views.map(parserViewPBToPage); + callbacks.onPageChanged?.(page, childPages); }; + await this.observer.subscribeView(this.id, { didUpdateView, }); 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 index 5b84faa37c..86563a0ab7 100644 --- 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 @@ -7,20 +7,20 @@ export class TrashController { private readonly backendService: TrashBackendService = new TrashBackendService(); - subscribe = (callbacks: { onTrashChanged?: (trash: TrashPB[]) => void }) => { + subscribe = async (callbacks: { onTrashChanged?: (trash: TrashPB[]) => void }) => { const didUpdateTrash = (payload: Uint8Array) => { const res = RepeatedTrashPB.deserializeBinary(payload); callbacks.onTrashChanged?.(res.items); }; - this.observer.subscribeTrash({ + await this.observer.subscribeTrash({ didUpdateTrash, }); }; - dispose = () => { - this.observer.unsubscribe(); + dispose = async () => { + await this.observer.unsubscribe(); }; getTrash = async () => { const res = await this.backendService.getTrash(); 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 index 9ff2856bfb..56b7003c42 100644 --- 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 @@ -1,6 +1,6 @@ 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 { CreateViewPayloadPB, RepeatedViewPB } from '@/services/backend'; import { PageBackendService } from '$app/stores/effects/workspace/page/page_bd_svc'; import { Page, parserViewPBToPage } from '$app_reducers/pages/slice'; @@ -13,8 +13,8 @@ export class WorkspaceController { this.backendService = new WorkspaceBackendService(); } - dispose = () => { - this.observer.unsubscribe(); + dispose = async () => { + await this.observer.unsubscribe(); }; open = async () => { @@ -37,16 +37,15 @@ export class WorkspaceController { return Promise.reject(result.err); }; - subscribe = async (callbacks: { - onChildPagesChanged?: (childPages: Page[]) => void; - }) => { - + 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 + didUpdateWorkspace, }); }; @@ -71,6 +70,4 @@ export class WorkspaceController { 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 index d717a07495..67bfdd92ee 100644 --- 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 @@ -1,5 +1,5 @@ import { WorkspaceBackendService } from './workspace_bd_svc'; -import { CreateWorkspacePayloadPB, RepeatedWorkspacePB } from '@/services/backend'; +import { CreateWorkspacePayloadPB } from '@/services/backend'; import { WorkspaceItem } from '$app_reducers/workspace/slice'; import { WorkspaceObserver } from '$app/stores/effects/workspace/workspace_observer'; @@ -51,6 +51,7 @@ export class WorkspaceManagerController { if (result.ok) { const workspace = result.val; + return { id: workspace.id, name: workspace.name, @@ -64,8 +65,8 @@ export class WorkspaceManagerController { await this.observer.unsubscribe(); }; - private didCreateWorkspace = (payload: Uint8Array) => { - const data = RepeatedWorkspacePB.deserializeBinary(payload); + private didCreateWorkspace = () => { + // const data = RepeatedWorkspacePB.deserializeBinary(payload); // onWorkspacesChanged(data.toObject().items); }; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/block-draggable/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/block-draggable/slice.ts index f5c9462095..123225a64d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/block-draggable/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/block-draggable/slice.ts @@ -81,17 +81,21 @@ export const blockDraggableSlice = createSlice({ state.draggingPosition = draggingPosition; state.dropContext = dropContext; - const moveDistance = Math.sqrt( - Math.pow(draggingPosition.x - state.startDraggingPosition!.x, 2) + - Math.pow(draggingPosition.y - state.startDraggingPosition!.y, 2) - ); + const { startDraggingPosition } = state; + + const moveDistance = startDraggingPosition + ? Math.sqrt( + Math.pow(draggingPosition.x - startDraggingPosition.x, 2) + + Math.pow(draggingPosition.y - startDraggingPosition.y, 2) + ) + : 0; state.dropId = dropId; state.insertType = insertType; state.dragShadowVisible = moveDistance > DRAG_DISTANCE_THRESHOLD; }, - endDrag: (state) => { + endDrag: () => { return initialState; }, }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts index f7a245ad60..267c3e68f1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts @@ -1,5 +1,4 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { nanoid } from 'nanoid'; import { WorkspaceSettingPB } from '@/services/backend/models/flowy-folder2/workspace'; import { UserSetting } from '$app/interfaces'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/duplicate.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/duplicate.ts index cb6334aaf7..7a95846059 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/duplicate.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/duplicate.ts @@ -21,7 +21,7 @@ export const duplicateBelowNodeThunk = createAsyncThunk( if (!duplicateActions) return; await controller.applyActions(duplicateActions.actions); - dispatch( + await dispatch( setRectSelectionThunk({ docId, selection: [duplicateActions.newNodeId], diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/insert.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/insert.ts index 76cdca31ba..5f19743be3 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/insert.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/insert.ts @@ -14,7 +14,7 @@ export const insertAfterNodeThunk = createAsyncThunk( id: string; controller: DocumentController; type: BlockType; - data?: BlockData<any>; + data?: BlockData; defaultDelta?: Delta; }, thunkAPI @@ -22,8 +22,7 @@ export const insertAfterNodeThunk = createAsyncThunk( const { controller, id, type, data, defaultDelta } = payload; const { getState } = thunkAPI; const state = getState() as RootState; - const docId = controller.documentId; - const documentState = state[DOCUMENT_NAME][docId]; + const documentState = state[DOCUMENT_NAME][controller.documentId]; const node = documentState.nodes[id]; if (!node) return; @@ -34,8 +33,9 @@ export const insertAfterNodeThunk = createAsyncThunk( const actions = []; let newNodeId; const deltaOperator = new BlockDeltaOperator(documentState, controller); + if (type === BlockType.DividerBlock) { - const newNode = newBlock<any>(type, parentId, data); + const newNode = newBlock(type, parentId, data); actions.push(controller.getInsertAction(newNode, node.id)); newNodeId = newNode.id; @@ -64,7 +64,7 @@ export const insertAfterNodeThunk = createAsyncThunk( }) ); } else { - const newNode = newBlock<any>(type, parentId, data); + const newNode = newBlock(type, parentId, data); actions.push(controller.getInsertAction(newNode, node.id)); newNodeId = newNode.id; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/update.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/update.ts index 72f68e1edd..6e211cc728 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/update.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/blocks/update.ts @@ -8,7 +8,7 @@ import { updatePageName } from '$app_reducers/pages/async_actions'; import { getDeltaText } from '$app/utils/document/delta'; import { BlockDeltaOperator } from '$app/utils/document/block_delta'; import { openMention, closeMention } from '$app_reducers/document/async-actions/mention'; -import {slashCommandActions} from "$app_reducers/document/slice"; +import { slashCommandActions } from '$app_reducers/document/slice'; const updateNodeDeltaAfterThunk = createAsyncThunk( 'document/updateNodeDeltaAfter', @@ -27,9 +27,11 @@ const updateNodeDeltaAfterThunk = createAsyncThunk( if (insertOps.length === 1) { const char = insertOps[0].insert; + if (char === '@' && (oldText.endsWith(' ') || oldText === '')) { - dispatch(openMention({ docId })); + await dispatch(openMention({ docId })); } + if (char === '/') { dispatch( slashCommandActions.openSlashCommand({ @@ -42,15 +44,13 @@ const updateNodeDeltaAfterThunk = createAsyncThunk( if (deleteOps.length === 1) { if (deleteText === '@') { - dispatch(closeMention({ docId })); + await dispatch(closeMention({ docId })); } + if (deleteText === '/') { - dispatch( - slashCommandActions.closeSlashCommand(docId) - ); + dispatch(slashCommandActions.closeSlashCommand(docId)); } } - } ); @@ -92,7 +92,7 @@ export const updateNodeDataThunk = createAsyncThunk< void, { id: string; - data: Partial<BlockData<any>>; + data: Partial<BlockData>; controller: DocumentController; } >('document/updateNodeDataExceptDelta', async (payload, thunkAPI) => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/copy_paste.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/copy_paste.ts index 1c7b7d10bf..97392462ff 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/copy_paste.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/copy_paste.ts @@ -9,7 +9,7 @@ export const copyThunk = createAsyncThunk< controller: DocumentController; setClipboardData: (data: BlockCopyData) => void; } ->('document/copy', async (payload, thunkAPI) => { +>('document/copy', async () => { // TODO: Migrate to Rust implementation. }); @@ -29,6 +29,6 @@ export const pasteThunk = createAsyncThunk< data: BlockCopyData; controller: DocumentController; } ->('document/paste', async (payload, thunkAPI) => { +>('document/paste', async () => { // TODO: Migrate to Rust implementation. }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/keydown.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/keydown.ts index 343001d88e..449438b559 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/keydown.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/keydown.ts @@ -71,7 +71,7 @@ export const backspaceDeleteActionForBlockThunk = createAsyncThunk( length: 0, }; - dispatch( + await dispatch( setCursorRangeThunk({ docId, blockId: caret.id, @@ -115,7 +115,6 @@ export const enterActionForBlockThunk = createAsyncThunk( ); }); const isDocumentTitle = !node.parent; - let newLineId; const delta = deltaOperator.getDeltaWithBlockId(node.id); @@ -126,7 +125,7 @@ export const enterActionForBlockThunk = createAsyncThunk( return; } - newLineId = await deltaOperator.splitText( + const newLineId = await deltaOperator.splitText( { id: node.id, index: caret.index, @@ -138,7 +137,7 @@ export const enterActionForBlockThunk = createAsyncThunk( ); if (!newLineId) return; - dispatch( + await dispatch( setCursorRangeThunk({ docId, blockId: newLineId, diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/mention.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/mention.ts index 4ec94ea731..f9379a1b78 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/mention.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/mention.ts @@ -70,11 +70,15 @@ export const formatMention = createAsyncThunk( const nodeDelta = deltaOperator.getDeltaWithBlockId(blockId); if (!nodeDelta) return; - const diffDelta = new Delta().retain(index).delete(charLength).insert('@',{ mention: { type, [type]: value } }); + const diffDelta = new Delta() + .retain(index) + .delete(charLength) + .insert('@', { mention: { type, [type]: value } }); const applyTextDeltaAction = deltaOperator.getApplyDeltaAction(blockId, diffDelta); + if (!applyTextDeltaAction) return; await controller.applyActions([applyTextDeltaAction]); - dispatch( + await dispatch( setCursorRangeThunk({ docId, blockId, diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/menu.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/menu.ts index 7c75ea6a77..1f1f50321c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/menu.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/menu.ts @@ -1,15 +1,10 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { BlockData, BlockType } from '$app/interfaces/document'; +import { BlockType } from '$app/interfaces/document'; import { insertAfterNodeThunk } from '$app_reducers/document/async-actions/blocks'; import { DocumentController } from '$app/stores/effects/document/document_controller'; import { rangeActions, slashCommandActions } from '$app_reducers/document/slice'; -import { turnToBlockThunk } from '$app_reducers/document/async-actions/turn_to'; -import { blockConfig } from '$app/constants/document/config'; -import Delta, { Op } from 'quill-delta'; -import { getDeltaText } from '$app/utils/document/delta'; +import Delta from 'quill-delta'; import { RootState } from '$app/stores/store'; -import { DOCUMENT_NAME, RANGE_NAME } from '$app/constants/document/name'; -import { blockEditActions } from '$app_reducers/document/block_edit_slice'; import { BlockDeltaOperator } from '$app/utils/document/block_delta'; /** diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/range.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/range.ts index fb980d16cd..14b8e1e00d 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/range.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/range.ts @@ -156,7 +156,7 @@ export const deleteRangeAndInsertThunk = createAsyncThunk( ); if (!id) return; - dispatch( + await dispatch( setCursorRangeThunk({ docId, blockId: id, @@ -211,7 +211,7 @@ export const deleteRangeAndInsertEnterThunk = createAsyncThunk( ); if (!newLineId) return; - dispatch( + await dispatch( setCursorRangeThunk({ docId, blockId: newLineId, diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/temporary.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/temporary.ts index fbc33dc739..7864c66387 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/temporary.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/temporary.ts @@ -1,7 +1,6 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; import { RootState } from '$app/stores/store'; import { DOCUMENT_NAME, EQUATION_PLACEHOLDER, RANGE_NAME, TEMPORARY_NAME } from '$app/constants/document/name'; -import { getDeltaByRange, getDeltaText } from '$app/utils/document/delta'; import Delta from 'quill-delta'; import { TemporaryState, TemporaryType } from '$app/interfaces/document'; import { temporaryActions } from '$app_reducers/document/temporary_slice'; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/turn_to.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/turn_to.ts index 18d368727b..dd66cfd2ab 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/turn_to.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/document/async-actions/turn_to.ts @@ -20,7 +20,7 @@ import { DOCUMENT_NAME, RANGE_NAME } from '$app/constants/document/name'; */ export const turnToBlockThunk = createAsyncThunk( 'document/turnToBlock', - async (payload: { id: string; controller: DocumentController; type: BlockType; data: BlockData<any> }, thunkAPI) => { + async (payload: { id: string; controller: DocumentController; type: BlockType; data: BlockData }, thunkAPI) => { const { id, controller, type, data } = payload; const docId = controller.documentId; const { dispatch, getState } = thunkAPI; @@ -46,13 +46,13 @@ export const turnToBlockThunk = createAsyncThunk( if (type === BlockType.EquationBlock) { data.formula = deltaOperator.getDeltaText(delta); - const block = newBlock<any>(type, parent.id, data); + const block = newBlock(type, parent.id, data); insertActions.push(controller.getInsertAction(block, node.id)); caretId = block.id; caretIndex = 0; } else if (type === BlockType.DividerBlock) { - const block = newBlock<any>(type, parent.id, data); + const block = newBlock(type, parent.id, data); insertActions.push(controller.getInsertAction(block, node.id)); const nodeId = generateId(); @@ -97,7 +97,7 @@ export const turnToBlockThunk = createAsyncThunk( // submit actions await controller.applyActions([...insertActions, ...moveChildrenActions, deleteAction]); - dispatch( + await dispatch( setCursorRangeThunk({ docId, blockId: caretId, 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 08aed71990..901a7a65eb 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 @@ -58,35 +58,17 @@ export const movePageThunk = createAsyncThunk( } ); -export const updatePageName = createAsyncThunk( - 'pages/updateName', - async ( - payload: { - id: string; - name: string; - }, - thunkAPI - ) => { - const controller = new PageController(payload.id); +export const updatePageName = createAsyncThunk('pages/updateName', async (payload: { id: string; name: string }) => { + const controller = new PageController(payload.id); - await controller.updatePage({ - id: payload.id, - name: payload.name, - }); - } -); + await controller.updatePage({ + id: payload.id, + name: payload.name, + }); +}); -export const updatePageIcon = createAsyncThunk( - 'pages/updateIcon', - async ( - payload: { - id: string; - icon?: PageIcon; - }, - thunkAPI - ) => { - const controller = new PageController(payload.id); +export const updatePageIcon = createAsyncThunk('pages/updateIcon', async (payload: { id: string; icon?: PageIcon }) => { + const controller = new PageController(payload.id); - await controller.updatePageIcon(payload.icon); - } -); + await controller.updatePageIcon(payload.icon); +}); diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts index 1aa0c4aa79..5d7d63e003 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/reducers/pages/slice.ts @@ -88,8 +88,10 @@ export const pagesSlice = createSlice({ expandPage(state, action: PayloadAction<string>) { const id = action.payload; + state.expandedIdMap[id] = true; const ids = Object.keys(state.expandedIdMap).filter(id => state.expandedIdMap[id]); + storeExpandedPageIds(ids); }, @@ -98,6 +100,7 @@ export const pagesSlice = createSlice({ state.expandedIdMap[id] = false; const ids = Object.keys(state.expandedIdMap).filter(id => state.expandedIdMap[id]); + storeExpandedPageIds(ids); }, }, diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts index a058032fed..7e673506de 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/async_queue.ts @@ -21,6 +21,10 @@ export class AsyncQueue<T = unknown> { const item = this.queue.shift(); + if (!item) { + return; + } + this.isProcessing = true; const executeFn = async (item: T) => { @@ -34,7 +38,7 @@ export class AsyncQueue<T = unknown> { } }; - executeFn(item!); + void executeFn(item); } private async processItem(item: T): Promise<void> { diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts index df81d11c47..57d9f2a370 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/change_notifier.ts @@ -12,6 +12,7 @@ export class ChangeNotifier<T> { if (this.isUnsubscribe) { return null; } + return this.subject.asObservable(); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/action.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/action.ts index 8921e2b4d8..3152246999 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/action.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/action.ts @@ -1,29 +1,14 @@ -import { - BlockType, - ControllerAction, - DocumentState, - NestedBlock, - RangeState, - RangeStatic, - SplitRelationship, -} from '$app/interfaces/document'; -import { getNextLineId, getPrevLineId, newBlock } from '$app/utils/document/block'; -import Delta from 'quill-delta'; -import { RootState } from '$app/stores/store'; +import { ControllerAction, DocumentState, RangeState, RangeStatic } from '$app/interfaces/document'; +import { getNextLineId, newBlock } from '$app/utils/document/block'; import { DocumentController } from '$app/stores/effects/document/document_controller'; -import { blockConfig } from '$app/constants/document/config'; import { caretInBottomEdgeByDelta, caretInTopEdgeByDelta, - getAfterExtentDeltaByRange, - getBeofreExtentDeltaByRange, - getDeltaText, getIndexRelativeEnter, getLastLineIndex, transformIndexToNextLine, transformIndexToPrevLine, } from '$app/utils/document/delta'; -import { DOCUMENT_NAME, RANGE_NAME } from '$app/constants/document/name'; import { BlockDeltaOperator } from '$app/utils/document/block_delta'; export function getMiddleIds(document: DocumentState, startId: string, endId: string) { @@ -80,7 +65,7 @@ export function getLeftCaretByRange(rangeState: RangeState) { } export function getRightCaretByRange(rangeState: RangeState) { - const { anchor, focus, ranges, caret } = rangeState; + const { anchor, focus, ranges } = rangeState; if (!anchor || !focus) return; const isForward = anchor.point.y < focus.point.y; @@ -180,7 +165,7 @@ export function getDuplicateActions( if (!node) return; // duplicate new node - const newNode = newBlock<any>(node.type, parentId, { + const newNode = newBlock(node.type, parentId, { ...node.data, }); diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/block_delta.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/block_delta.ts index cef85413d8..4b10d9c9f6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/block_delta.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/block_delta.ts @@ -1,4 +1,4 @@ -import { BlockData, BlockType, DocumentState, NestedBlock, SplitRelationship } from '$app/interfaces/document'; +import { BlockData, BlockType, DocumentState, SplitRelationship } from '$app/interfaces/document'; import { generateId, getNextLineId, getPrevLineId } from '$app/utils/document/block'; import { DocumentController } from '$app/stores/effects/document/document_controller'; import Delta, { Op } from 'quill-delta'; @@ -92,7 +92,7 @@ export class BlockDeltaOperator { parentId: string; type: BlockType; prevId: string | null; - data?: BlockData<any>; + data?: BlockData; }) => { const externalId = generateId(); const block = { diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/format.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/format.ts index d156b6a066..66fe95caaf 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/format.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/format.ts @@ -24,5 +24,6 @@ export function parseFormat(e: KeyboardEvent | React.KeyboardEvent<HTMLDivElemen } else if (isHotkey(Keyboard.keys.FORMAT.CODE, e)) { return TextAction.Code; } + return null; } diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/node.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/node.ts index d50cb04b2b..5d7dc8a565 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/node.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/node.ts @@ -25,8 +25,8 @@ export function findFirstTextNode(node: Node): Node | null { const children = node.childNodes; - for (let i = 0; i < children.length; i++) { - const textNode = findFirstTextNode(children[i]); + for (const child of children) { + const textNode = findFirstTextNode(child); if (textNode) { return textNode; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/quill_editor.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/quill_editor.ts index e29c0fd72d..7b9690b894 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/quill_editor.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/quill_editor.ts @@ -46,6 +46,7 @@ export function adaptDeltaForQuill(inputOps: Op[], isOutput = false): Op[] { if (isOutput) { const newText = text.slice(0, -1); + if (newText !== '') { newOps[lastOpIndex] = { ...lastOp, insert: newText }; } else { diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/slate_editor.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/slate_editor.ts index 2f8cfac126..619dcf06f0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/slate_editor.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/slate_editor.ts @@ -107,6 +107,7 @@ export function convertToSlateValue(delta: Delta): Descendant[] { export function convertToDelta(slateValue: Descendant[]) { const ops = (slateValue[0] as Element).children.map((child) => { const { text, ...attributes } = child as Text; + return { insert: text, attributes, diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/document/toolbar.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/document/toolbar.ts index 93546f33b2..fa8318ac42 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/document/toolbar.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/document/toolbar.ts @@ -1,6 +1,7 @@ export function calcToolbarPosition(toolbarDom: HTMLDivElement, node: Element, container: HTMLDivElement) { const domSelection = window.getSelection(); let domRange; + if (domSelection?.rangeCount === 0) { return; } else { @@ -18,6 +19,7 @@ export function calcToolbarPosition(toolbarDom: HTMLDivElement, node: Element, c const rightBound = containerRect.right; const rightThreshold = 20; + if (left < leftBound) { left = leftBound; } else if (left + nodeRect.left + toolbarDom.offsetWidth > rightBound) { diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/draggable.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/draggable.ts index e0769899ca..090ab871d7 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/draggable.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/draggable.ts @@ -1,8 +1,6 @@ import { BlockDraggableType, DragInsertType } from '$app_reducers/block-draggable/slice'; import { findParent } from '$app/utils/document/node'; import { nanoid } from 'nanoid'; -import { getBlock } from '$app/components/document/_shared/SubscribeNode.hooks'; -import { blockConfig } from '$app/constants/document/config'; export function getDraggableIdByPoint(target: HTMLElement | null) { let node = target; diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/region_grid.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/region_grid.ts index 3922c67329..75129c3bea 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/region_grid.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/region_grid.ts @@ -46,7 +46,7 @@ export class RegionGrid { this.grid.set(key, []); } - this.grid.get(key)!.push(block); + this.grid.get(key)?.push(block); } } @@ -58,6 +58,7 @@ export class RegionGrid { if (this.hasBlock(block.id)) { this.removeBlock(block); } + this.addBlock(block); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts index de291b980d..3f408738de 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/utils/tool.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + export function debounce(fn: (...args: any[]) => void, delay: number) { let timeout: NodeJS.Timeout; const debounceFn = (...args: any[]) => { @@ -17,7 +19,7 @@ export function debounce(fn: (...args: any[]) => void, delay: number) { export function throttle<T extends (...args: any[]) => void = (...args: any[]) => void>( fn: T, delay: number, - immediate = true, + immediate = true ): T { let timeout: NodeJS.Timeout | null = null; @@ -156,7 +158,7 @@ export function chunkArray<T>(array: T[], chunkSize: number) { export function interval<T extends (...args: any[]) => any = (...args: any[]) => any>( fn: T, delay?: number, - options?: { immediate?: boolean }, + options?: { immediate?: boolean } ): T & { cancel: () => void } { const { immediate = true } = options || {}; let intervalId: NodeJS.Timer | null = null; diff --git a/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx b/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx index 61f86d22f0..cb0dbd5ba0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/views/DatabasePage.tsx @@ -1,5 +1,25 @@ -import { Database } from '../components/database'; +import { useParams } from 'react-router-dom'; +import { ViewIdProvider } from '$app/hooks'; +import { Database, DatabaseTitle, VerticalScrollElementProvider } from '../components/database'; +import { useRef } from 'react'; export const DatabasePage = () => { - return <Database />; + const viewId = useParams().id; + + const ref = useRef<HTMLDivElement>(null); + + if (!viewId) { + return null; + } + + return ( + <div className="h-full overflow-y-auto" ref={ref}> + <VerticalScrollElementProvider value={ref}> + <ViewIdProvider value={viewId}> + <DatabaseTitle /> + <Database /> + </ViewIdProvider> + </VerticalScrollElementProvider> + </div> + ); };