diff --git a/src/application/database-yjs/cell.parse.ts b/src/application/database-yjs/cell.parse.ts index 4e764625..b27762a0 100644 --- a/src/application/database-yjs/cell.parse.ts +++ b/src/application/database-yjs/cell.parse.ts @@ -1,16 +1,12 @@ import * as Y from 'yjs'; import { FieldType } from '@/application/database-yjs/database.type'; -import { - getDateCellStr, - parseChecklistData, - parseSelectOptionTypeOptions, -} from '@/application/database-yjs/fields'; +import { getDateCellStr, parseChecklistData, parseSelectOptionTypeOptions } from '@/application/database-yjs/fields'; import { YDatabaseCell, YDatabaseField, YjsDatabaseKey } from '@/application/types'; import { Cell, DateTimeCell, FileMediaCell, FileMediaCellData } from './cell.type'; -export function parseYDatabaseCommonCellToCell (cell: YDatabaseCell): Cell { +export function parseYDatabaseCommonCellToCell(cell: YDatabaseCell): Cell { return { createdAt: Number(cell.get(YjsDatabaseKey.created_at)), lastModified: Number(cell.get(YjsDatabaseKey.last_modified)), @@ -19,7 +15,7 @@ export function parseYDatabaseCommonCellToCell (cell: YDatabaseCell): Cell { }; } -export function parseYDatabaseCellToCell (cell: YDatabaseCell): Cell { +export function parseYDatabaseCellToCell(cell: YDatabaseCell): Cell { const cellType = parseInt(cell.get(YjsDatabaseKey.field_type)); let value = parseYDatabaseCommonCellToCell(cell); @@ -39,7 +35,7 @@ export function parseYDatabaseCellToCell (cell: YDatabaseCell): Cell { return value; } -export function parseYDatabaseDateTimeCellToCell (cell: YDatabaseCell): DateTimeCell { +export function parseYDatabaseDateTimeCellToCell(cell: YDatabaseCell): DateTimeCell { let data = cell.get(YjsDatabaseKey.data); if (typeof data !== 'string' && typeof data !== 'number') { @@ -59,7 +55,7 @@ export function parseYDatabaseDateTimeCellToCell (cell: YDatabaseCell): DateTime }; } -export function parseYDatabaseFileMediaCellToCell (cell: YDatabaseCell): FileMediaCell { +export function parseYDatabaseFileMediaCellToCell(cell: YDatabaseCell): FileMediaCell { const data = cell.get(YjsDatabaseKey.data) as Y.Array; if (!data || !(data instanceof Y.Array)) { @@ -80,7 +76,7 @@ export function parseYDatabaseFileMediaCellToCell (cell: YDatabaseCell): FileMed }; } -export function parseYDatabaseRelationCellToCell (cell: YDatabaseCell): Cell { +export function parseYDatabaseRelationCellToCell(cell: YDatabaseCell): Cell { const data = cell.get(YjsDatabaseKey.data) as Y.Array; if (!data || !(data instanceof Y.Array)) { @@ -98,7 +94,7 @@ export function parseYDatabaseRelationCellToCell (cell: YDatabaseCell): Cell { }; } -export function getCellDataText (cell: YDatabaseCell, field: YDatabaseField): string { +export function getCellDataText(cell: YDatabaseCell, field: YDatabaseField): string { const type = parseInt(field.get(YjsDatabaseKey.type)); switch (type) { @@ -108,9 +104,15 @@ export function getCellDataText (cell: YDatabaseCell, field: YDatabaseField): st const options = parseSelectOptionTypeOptions(field)?.options || []; if (typeof data === 'string') { - return data.split(',').map((item) => { - return options?.find((option) => option.id === item)?.name; - }).filter(item => item).join(',') || ''; + return ( + data + .split(',') + .map((item) => { + return options?.find((option) => option.id === item)?.name; + }) + .filter((item) => item) + .join(',') || '' + ); } return ''; @@ -120,34 +122,33 @@ export function getCellDataText (cell: YDatabaseCell, field: YDatabaseField): st const cellData = cell.get(YjsDatabaseKey.data); if (typeof cellData === 'string') { - const { - options = [], - selectedOptionIds = [], - } = parseChecklistData(cellData) || {}; + const { options = [], selectedOptionIds = [] } = parseChecklistData(cellData) || {}; - const completed = options - .filter((option) => selectedOptionIds.includes(option.id)) - .map((option, index) => `${index + 1}. ${option.name}`) - .join(',') || ''; - const incomplete = options.filter((option) => !selectedOptionIds.includes(option.id)).map((option, index) => `${index + 1}. ${option.name}`) - .join(',') || ''; - - return `Completed:${completed};Incomplete:${incomplete}`; + return JSON.stringify({ + tasks: options.map((option) => ({ + id: option.id, + name: option.name, + checked: selectedOptionIds.includes(option.id), + })), + progress: { + completed: selectedOptionIds.length, + total: options.length, + }, + }); } return ''; } - case FieldType.DateTime: - {const dateCell = parseYDatabaseDateTimeCellToCell(cell); + case FieldType.DateTime: { + const dateCell = parseYDatabaseDateTimeCellToCell(cell); - return getDateCellStr({ cell: dateCell, field });} + return getDateCellStr({ cell: dateCell, field }); + } case FieldType.CreatedTime: case FieldType.LastEditedTime: case FieldType.Relation: - case FieldType.AITranslations: - case FieldType.AISummaries: return ''; default: { @@ -160,4 +161,4 @@ export function getCellDataText (cell: YDatabaseCell, field: YDatabaseField): st return ''; } } -} \ No newline at end of file +} diff --git a/src/application/database-yjs/fields/select-option/parse.ts b/src/application/database-yjs/fields/select-option/parse.ts index 83446338..a9077844 100644 --- a/src/application/database-yjs/fields/select-option/parse.ts +++ b/src/application/database-yjs/fields/select-option/parse.ts @@ -1,16 +1,23 @@ import { YDatabaseField, YjsDatabaseKey } from '@/application/types'; + import { getTypeOptions } from '../type_option'; + import { SelectTypeOption } from './select_option.type'; export function parseSelectOptionTypeOptions(field: YDatabaseField) { const content = getTypeOptions(field)?.get(YjsDatabaseKey.content); - if (!content) return null; + if (!content) + return { + options: [], + }; try { return JSON.parse(content) as SelectTypeOption; } catch (e) { - return null; + return { + options: [], + }; } } diff --git a/src/application/database-yjs/group.ts b/src/application/database-yjs/group.ts index 73ea261f..fa7d3563 100644 --- a/src/application/database-yjs/group.ts +++ b/src/application/database-yjs/group.ts @@ -1,26 +1,36 @@ -import { RowId, YDatabaseField, YDoc, YjsDatabaseKey } from '@/application/types'; import { getCellData } from '@/application/database-yjs/const'; import { FieldType } from '@/application/database-yjs/database.type'; -import { parseSelectOptionTypeOptions } from '@/application/database-yjs/fields'; -import { Row } from '@/application/database-yjs/selector'; +import { + CheckboxFilterCondition, + parseSelectOptionTypeOptions, + SelectOptionFilterCondition, +} from '@/application/database-yjs/fields'; import { getChecked } from '@/application/database-yjs/fields/checkbox/utils'; +import { checkboxFilterCheck, selectOptionFilterCheck } from '@/application/database-yjs/filter'; +import { Row } from '@/application/database-yjs/selector'; +import { RowId, YDatabaseField, YDatabaseFilter, YDoc, YjsDatabaseKey } from '@/application/types'; -export function groupByField (rows: Row[], rowMetas: Record, field: YDatabaseField) { +export function groupByField( + rows: Row[], + rowMetas: Record, + field: YDatabaseField, + filter?: YDatabaseFilter +) { const fieldType = Number(field.get(YjsDatabaseKey.type)); const isSelectOptionField = [FieldType.SingleSelect, FieldType.MultiSelect].includes(fieldType); if (isSelectOptionField) { - return groupBySelectOption(rows, rowMetas, field); + return groupBySelectOption(rows, rowMetas, field, filter); } if (fieldType === FieldType.Checkbox) { - return groupByCheckbox(rows, rowMetas, field); + return groupByCheckbox(rows, rowMetas, field, filter); } return; } -export function getGroupColumns (field: YDatabaseField) { +export function getGroupColumns(field: YDatabaseField) { const fieldType = Number(field.get(YjsDatabaseKey.type)); const isSelectOptionField = [FieldType.SingleSelect, FieldType.MultiSelect].includes(fieldType); @@ -35,19 +45,20 @@ export function getGroupColumns (field: YDatabaseField) { id: option.id, })); - return [ - { id: field.get(YjsDatabaseKey.id) }, - ...options, - ]; + return [{ id: field.get(YjsDatabaseKey.id) }, ...options]; } if (fieldType === FieldType.Checkbox) { return [{ id: 'Yes' }, { id: 'No' }]; } - } -export function groupByCheckbox (rows: Row[], rowMetas: Record, field: YDatabaseField) { +export function groupByCheckbox( + rows: Row[], + rowMetas: Record, + field: YDatabaseField, + filter?: YDatabaseFilter +) { const fieldId = field.get(YjsDatabaseKey.id); const result = new Map(); @@ -59,7 +70,19 @@ export function groupByCheckbox (rows: Row[], rowMetas: Record, fie const cellData = getCellData(row.id, fieldId, rowMetas); - const groupName = getChecked(cellData as string) ? 'Yes' : 'No'; + const checked = getChecked(cellData as string); + const groupName = checked ? 'Yes' : 'No'; + + if (filter) { + if (filter) { + const condition = Number(filter?.get(YjsDatabaseKey.condition)) as CheckboxFilterCondition; + + if (!checkboxFilterCheck(groupName, condition)) { + return; + } + } + } + const group = result.get(groupName) ?? []; group.push(row); @@ -68,16 +91,17 @@ export function groupByCheckbox (rows: Row[], rowMetas: Record, fie return result; } -export function groupBySelectOption (rows: Row[], rowMetas: Record, field: YDatabaseField) { +export function groupBySelectOption( + rows: Row[], + rowMetas: Record, + field: YDatabaseField, + filter?: YDatabaseFilter +) { const fieldId = field.get(YjsDatabaseKey.id); const result = new Map(); const typeOption = parseSelectOptionTypeOptions(field); - if (!typeOption) { - return; - } - - if (typeOption.options.length === 0) { + if (!typeOption || typeOption.options.length === 0) { result.set(fieldId, rows); return result; } @@ -92,6 +116,19 @@ export function groupBySelectOption (rows: Row[], rowMetas: Record, const selectedIds = (cellData as string)?.split(',') ?? []; + if (typeof cellData !== 'string') { + return; + } + + if (filter) { + const condition = Number(filter?.get(YjsDatabaseKey.condition)) as SelectOptionFilterCondition; + const content = filter?.get(YjsDatabaseKey.content); + + if (!selectOptionFilterCheck(cellData, content, condition)) { + return; + } + } + if (selectedIds.length === 0) { const group = result.get(fieldId) ?? []; diff --git a/src/application/database-yjs/row_meta.ts b/src/application/database-yjs/row_meta.ts index 638edc8a..3f4db025 100644 --- a/src/application/database-yjs/row_meta.ts +++ b/src/application/database-yjs/row_meta.ts @@ -1,9 +1,10 @@ import { metaIdFromRowId } from '@/application/database-yjs/const'; import { RowMetaKey } from '@/application/database-yjs/database.type'; import { RowCoverType } from '@/application/types'; + import type Y from 'yjs'; -export function generateRowMeta (rowId: string, data: Record) { +export function generateRowMeta(rowId: string, data: Record) { const map = getMetaIdMap(rowId); const iconKey = map.get(RowMetaKey.IconId) ?? ''; @@ -17,7 +18,7 @@ export function generateRowMeta (rowId: string, data: Record>(); -export function getMetaIdMap (rowId: string) { +export function getMetaIdMap(rowId: string) { const hasMetaIdMap = metaIdMapFromRowIdMap.has(rowId); if (!hasMetaIdMap) { @@ -51,7 +52,7 @@ export function getMetaIdMap (rowId: string) { return metaIdMapFromRowIdMap.get(rowId) as Map; } -export function getMetaJSON (rowId: string, meta: Y.Map) { +export function getMetaJSON(rowId: string, meta: Y.Map) { const metaKeyMap = getMetaIdMap(rowId); const iconKey = metaKeyMap.get(RowMetaKey.IconId) ?? ''; @@ -64,10 +65,12 @@ export function getMetaJSON (rowId: string, meta: Y.Map) { let cover = null; try { - cover = metaJson[coverKey] ? (JSON.parse(metaJson[coverKey]) as { - data: string, - cover_type: RowCoverType, - }) : null; + cover = metaJson[coverKey] + ? (JSON.parse(metaJson[coverKey]) as { + data: string; + cover_type: RowCoverType; + }) + : null; } catch (e) { // do nothing } @@ -80,4 +83,4 @@ export function getMetaJSON (rowId: string, meta: Y.Map) { icon: icon, isEmptyDocument: isEmptyDocument, }; -} \ No newline at end of file +} diff --git a/src/application/database-yjs/selector.ts b/src/application/database-yjs/selector.ts index 54d7e40d..4d2ea38b 100644 --- a/src/application/database-yjs/selector.ts +++ b/src/application/database-yjs/selector.ts @@ -591,6 +591,7 @@ export function useRowsByGroup(groupId: string) { const [groupResult, setGroupResult] = useState>(new Map()); const view = useDatabaseView(); const layoutSetting = view?.get(YjsDatabaseKey.layout_settings)?.get('1'); + const filters = view?.get(YjsDatabaseKey.filters); useEffect(() => { if (!fieldId || !rowOrders || !rows) return; @@ -606,6 +607,7 @@ export function useRowsByGroup(groupId: string) { return; } + const fieldType = Number(field.get(YjsDatabaseKey.type)) as FieldType; if (![FieldType.SingleSelect, FieldType.MultiSelect, FieldType.Checkbox].includes(fieldType)) { @@ -613,8 +615,10 @@ export function useRowsByGroup(groupId: string) { setGroupResult(newResult); return; } + + const filter = filters?.toArray().find((filter) => filter.get(YjsDatabaseKey.field_id) === fieldId); - const groupResult = groupByField(rowOrders, rows, field); + const groupResult = groupByField(rowOrders, rows, field, filter); if (!groupResult) { setGroupResult(newResult); @@ -627,10 +631,12 @@ export function useRowsByGroup(groupId: string) { onConditionsChange(); fields.observeDeep(onConditionsChange); + filters?.observeDeep(onConditionsChange); return () => { fields.unobserveDeep(onConditionsChange); + filters?.unobserveDeep(onConditionsChange); }; - }, [fieldId, fields, rowOrders, rows]); + }, [fieldId, fields, rowOrders, rows, filters]); useEffect(() => { const observeEvent = () => { @@ -680,7 +686,6 @@ export function useRowOrdersSelector() { if (sorts?.length) { rowOrders = sortBy(originalRowOrders, sorts, fields, rows); - } if (filters?.length) { diff --git a/src/application/database-yjs/sort.ts b/src/application/database-yjs/sort.ts index 66153a10..99622497 100644 --- a/src/application/database-yjs/sort.ts +++ b/src/application/database-yjs/sort.ts @@ -2,6 +2,7 @@ import * as Y from 'yjs'; import { FieldType, SortCondition } from '@/application/database-yjs/database.type'; import { parseChecklistData, parseSelectOptionCellData } from '@/application/database-yjs/fields'; +import { getChecked } from '@/application/database-yjs/fields/checkbox/utils'; import { Row } from '@/application/database-yjs/selector'; import { RowId, @@ -13,7 +14,6 @@ import { YjsDatabaseKey, YjsEditorKey, } from '@/application/types'; -import { getChecked } from '@/application/database-yjs/fields/checkbox/utils'; type SortableValue = string | number | object | boolean | undefined; @@ -24,8 +24,8 @@ export function sortBy(rows: Row[], sorts: YDatabaseSorts, fields: YDatabaseFiel // Define collator for Unicode string comparison // Can adjust parameters based on application needs, such as locale, sensitivity, etc. - const collator = new Intl.Collator(undefined, { - sensitivity: 'base', // Do not distinguish between uppercase and lowercase letters and accents + const collator = new Intl.Collator('en', { + sensitivity: 'variant', numeric: true, // Use numeric sorting, such as "2" before "10" usage: 'sort', // Used specifically for sorting }); diff --git a/src/assets/icons/add_to_page.svg b/src/assets/icons/add_to_page.svg index a74800b1..e3c0489b 100644 --- a/src/assets/icons/add_to_page.svg +++ b/src/assets/icons/add_to_page.svg @@ -1,3 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/assets/icons/page.svg b/src/assets/icons/page.svg index 90cdbea6..6c0076e1 100644 --- a/src/assets/icons/page.svg +++ b/src/assets/icons/page.svg @@ -1,3 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/assets/icons/ref_page.svg b/src/assets/icons/ref_page.svg index c3afb5d2..6797cfb9 100644 --- a/src/assets/icons/ref_page.svg +++ b/src/assets/icons/ref_page.svg @@ -1,3 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/components/_shared/color-picker/ColorPicker.tsx b/src/components/_shared/color-picker/ColorPicker.tsx index 8be0ea16..b62cbfe2 100644 --- a/src/components/_shared/color-picker/ColorPicker.tsx +++ b/src/components/_shared/color-picker/ColorPicker.tsx @@ -41,7 +41,7 @@ export function ColorPicker({ onEscape, onChange, disableFocus }: ColorPickerPro
diff --git a/src/components/_shared/notify/InfoSnackbar.tsx b/src/components/_shared/notify/InfoSnackbar.tsx index c1a243ae..0161037d 100644 --- a/src/components/_shared/notify/InfoSnackbar.tsx +++ b/src/components/_shared/notify/InfoSnackbar.tsx @@ -1,12 +1,12 @@ -import { notify } from '@/components/_shared/notify/index'; -import { forwardRef } from 'react'; -import { Button, IconButton, Paper } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import { ReactComponent as CloseIcon } from '@/assets/icons/close.svg'; -import { CustomContentProps, SnackbarContent } from 'notistack'; import { ReactComponent as CheckCircle } from '@/assets/icons/check_circle.svg'; +import { ReactComponent as CloseIcon } from '@/assets/icons/close.svg'; import { ReactComponent as ErrorIcon } from '@/assets/icons/error.svg'; import { ReactComponent as WarningAmber } from '@/assets/icons/warning.svg'; +import { notify } from '@/components/_shared/notify/index'; +import { Button, IconButton, Paper } from '@mui/material'; +import { CustomContentProps, SnackbarContent } from 'notistack'; +import { forwardRef } from 'react'; +import { useTranslation } from 'react-i18next'; export interface InfoProps { onOk?: () => void; @@ -33,7 +33,7 @@ const InfoSnackbar = forwardRef( return ( -
+
{getIcon(type)}
{title}
diff --git a/src/components/_shared/view-icon/PageIcon.tsx b/src/components/_shared/view-icon/PageIcon.tsx index 9830dada..e5272b1b 100644 --- a/src/components/_shared/view-icon/PageIcon.tsx +++ b/src/components/_shared/view-icon/PageIcon.tsx @@ -1,15 +1,17 @@ +import DOMPurify from 'dompurify'; import React, { useEffect, useMemo } from 'react'; + import { ViewIcon, ViewIconType, ViewLayout } from '@/application/types'; +import { ReactComponent as ChatSvg } from '@/assets/icons/ai_chat.svg'; import { ReactComponent as BoardSvg } from '@/assets/icons/board.svg'; import { ReactComponent as CalendarSvg } from '@/assets/icons/calendar.svg'; -import { ReactComponent as DocumentSvg } from '@/assets/icons/page.svg'; import { ReactComponent as GridSvg } from '@/assets/icons/grid.svg'; -import { ReactComponent as ChatSvg } from '@/assets/icons/ai_chat.svg'; -import { getIcon, isFlagEmoji } from '@/utils/emoji'; -import DOMPurify from 'dompurify'; +import { ReactComponent as DocumentSvg } from '@/assets/icons/page.svg'; +import { cn } from '@/lib/utils'; import { renderColor } from '@/utils/color'; +import { getIcon, isFlagEmoji } from '@/utils/emoji'; -function PageIcon ({ +function PageIcon({ view, className, iconSize, @@ -33,11 +35,7 @@ function PageIcon ({ const img = useMemo(() => { if (view.icon && view.icon.ty === ViewIconType.URL && view.icon.value) { - return icon; + return icon; } return null; @@ -54,7 +52,11 @@ function PageIcon ({ const id = `${json.groupName}/${json.iconName}`; void getIcon(id).then((item) => { - setIconContent(item?.content.replaceAll('black', json.color ? renderColor(json.color) : 'currentColor').replace('; + return ( + + ); } }, [iconContent, iconSize, className]); if (emoji) { - return <> - {emoji} - ; + return ( + <> + {emoji} + + ); } if (img) { diff --git a/src/components/_shared/view-icon/utils.tsx b/src/components/_shared/view-icon/utils.tsx index 4caeb390..99f1ef27 100644 --- a/src/components/_shared/view-icon/utils.tsx +++ b/src/components/_shared/view-icon/utils.tsx @@ -1,10 +1,7 @@ import { stringAvatar } from '@/utils/color'; import { isFlagEmoji } from '@/utils/emoji'; -export function getAvatar (item: { - icon?: string; - name: string; -}) { +export function getAvatar(item: { icon?: string; name: string }) { if (item.icon) { const isFlag = isFlagEmoji(item.icon); @@ -12,10 +9,10 @@ export function getAvatar (item: { children: {item.icon} , sx: { bgcolor: 'var(--bg-body)', - color: 'var(--text-title)', + color: 'var(--text-primary)', }, }; } return stringAvatar(item.name || ''); -} \ No newline at end of file +} diff --git a/src/components/app/landing-pages/ApproveRequestPage.tsx b/src/components/app/landing-pages/ApproveRequestPage.tsx index affb60c2..a8981b97 100644 --- a/src/components/app/landing-pages/ApproveRequestPage.tsx +++ b/src/components/app/landing-pages/ApproveRequestPage.tsx @@ -138,7 +138,7 @@ function ApproveRequestPage() {
diff --git a/src/components/app/outline/ViewItem.tsx b/src/components/app/outline/ViewItem.tsx index cc0626aa..0789f209 100644 --- a/src/components/app/outline/ViewItem.tsx +++ b/src/components/app/outline/ViewItem.tsx @@ -1,11 +1,12 @@ +import React, { useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { toast } from 'sonner'; + import { View, ViewIconType } from '@/application/types'; import { useAppHandlers, useAppViewId } from '@/components/app/app.hooks'; import { CustomIconPopover } from '@/components/_shared/cutsom-icon'; import OutlineIcon from '@/components/_shared/outline/OutlineIcon'; import PageIcon from '@/components/_shared/view-icon/PageIcon'; -import React, { useCallback, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; -import { toast } from 'sonner'; function ViewItem({ view, @@ -127,7 +128,7 @@ function ViewItem({ >
diff --git a/src/components/app/search/Search.tsx b/src/components/app/search/Search.tsx index 34d34950..d23fc1f6 100644 --- a/src/components/app/search/Search.tsx +++ b/src/components/app/search/Search.tsx @@ -130,7 +130,7 @@ export function Search() { setSearchTypeAnchorEl(e.currentTarget); }} className={ - 'flex cursor-pointer items-center gap-2 overflow-hidden rounded-[8px] border border-border-primary p-2 text-sm hover:border-text-title' + 'flex cursor-pointer items-center gap-2 overflow-hidden rounded-[8px] border border-border-primary p-2 text-sm hover:border-text-primary' } > diff --git a/src/components/app/workspaces/WorkspaceList.tsx b/src/components/app/workspaces/WorkspaceList.tsx index 52c8a865..8529f6a1 100644 --- a/src/components/app/workspaces/WorkspaceList.tsx +++ b/src/components/app/workspaces/WorkspaceList.tsx @@ -1,12 +1,13 @@ +import { Avatar, Tooltip } from '@mui/material'; +import CircularProgress from '@mui/material/CircularProgress'; +import React, { useCallback, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; + import { Workspace } from '@/application/types'; import { ReactComponent as SelectedSvg } from '@/assets/icons/tick.svg'; import MoreActions from '@/components/app/workspaces/MoreActions'; import { getAvatarProps } from '@/components/app/workspaces/utils'; import { useService } from '@/components/main/app.hooks'; -import { Avatar, Tooltip } from '@mui/material'; -import CircularProgress from '@mui/material/CircularProgress'; -import React, { useCallback, useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; function WorkspaceList({ defaultWorkspaces, @@ -105,7 +106,9 @@ function WorkspaceList({ />
-
{workspace.name}
+
+ {workspace.name} +
{t('invitation.membersCount', { count: workspace.memberCount || 0 })} diff --git a/src/components/database/DatabaseRowModal.tsx b/src/components/database/DatabaseRowModal.tsx index 9a236c51..d4daf739 100644 --- a/src/components/database/DatabaseRowModal.tsx +++ b/src/components/database/DatabaseRowModal.tsx @@ -54,7 +54,7 @@ function DatabaseRowModal({ }} > - + - {state.type === DragState.IS_OVER && state.closestEdge && ( - - )} + {state.type === DragState.IS_OVER && state.closestEdge && }
- ); } -export default Property; \ No newline at end of file +export default Property; diff --git a/src/components/database/components/tabs/DatabaseTabs.tsx b/src/components/database/components/tabs/DatabaseTabs.tsx index fb2edc5c..fcbf2d92 100644 --- a/src/components/database/components/tabs/DatabaseTabs.tsx +++ b/src/components/database/components/tabs/DatabaseTabs.tsx @@ -128,6 +128,14 @@ export const DatabaseTabs = forwardRef( return meta?.children.find((v) => v.view_id === menuViewId); }, [iidIndex, menuViewId, meta]); + const visibleViewIds = useMemo(() => { + return viewIds.filter((viewId) => { + const databaseView = views?.get(viewId) as YDatabaseView | null; + + return !!databaseView; + }); + }, [viewIds, views]); + const handleAddView = useCallback( async (layout: DatabaseViewLayout) => { setAddLoading(true); @@ -360,7 +368,7 @@ export const DatabaseTabs = forwardRef( onOpenRenameModal={(viewId: string) => { setRenameViewId(viewId); }} - deleteDisabled={viewId === iidIndex} + deleteDisabled={viewId === iidIndex && visibleViewIds.length > 1} view={menuView} onUpdatedIcon={reloadView} /> diff --git a/src/components/database/components/tabs/TextButton.tsx b/src/components/database/components/tabs/TextButton.tsx index 7bbf91cf..31081806 100644 --- a/src/components/database/components/tabs/TextButton.tsx +++ b/src/components/database/components/tabs/TextButton.tsx @@ -5,7 +5,7 @@ export const TextButton = styled((props: ButtonProps) => ( {...props} sx={{ '&.MuiButton-colorInherit': { - color: 'var(--text-caption)', + color: 'var(--text-secondary)', }, }} /> diff --git a/src/components/editor/EditorContext.tsx b/src/components/editor/EditorContext.tsx index ebb09230..2ac50ce4 100644 --- a/src/components/editor/EditorContext.tsx +++ b/src/components/editor/EditorContext.tsx @@ -55,6 +55,7 @@ export interface EditorContextState { onWordCountChange?: (viewId: string, props: TextCount) => void; uploadFile?: (file: File) => Promise; requestInstance?: AxiosInstance | null; + getMoreAIContext?: () => string; } export const EditorContext = createContext({ diff --git a/src/components/editor/components/blocks/gallery/carousel.scss b/src/components/editor/components/blocks/gallery/carousel.scss index 53f8697f..1a8ae2ca 100644 --- a/src/components/editor/components/blocks/gallery/carousel.scss +++ b/src/components/editor/components/blocks/gallery/carousel.scss @@ -36,10 +36,10 @@ .lg-next, .lg-prev { background: transparent; - color: var(--text-caption); + color: var(--text-secondary); @apply max-md:hidden; &:hover:not(.disabled) { - color: var(--text-title); + color: var(--text-primary); } } } diff --git a/src/components/editor/components/leaf/mention/MentionDate.tsx b/src/components/editor/components/leaf/mention/MentionDate.tsx index c9549a0d..ccd0fb30 100644 --- a/src/components/editor/components/leaf/mention/MentionDate.tsx +++ b/src/components/editor/components/leaf/mention/MentionDate.tsx @@ -1,7 +1,7 @@ -import { renderDate } from '@/utils/time'; -import { useMemo } from 'react'; import { ReactComponent as DateSvg } from '@/assets/icons/date.svg'; import { ReactComponent as ReminderSvg } from '@/assets/icons/reminder_clock.svg'; +import { renderDate } from '@/utils/time'; +import { useMemo } from 'react'; function MentionDate({ date, reminder }: { date: string; reminder?: { id: string; option: string } }) { const dateFormat = useMemo(() => { @@ -12,7 +12,7 @@ function MentionDate({ date, reminder }: { date: string; reminder?: { id: string diff --git a/src/components/editor/components/panels/slash-panel/SlashPanel.tsx b/src/components/editor/components/panels/slash-panel/SlashPanel.tsx index 54035869..8ca1795b 100644 --- a/src/components/editor/components/panels/slash-panel/SlashPanel.tsx +++ b/src/components/editor/components/panels/slash-panel/SlashPanel.tsx @@ -1,3 +1,10 @@ +import { useAIWriter } from '@appflowyinc/ai-chat'; +import { Button } from '@mui/material'; +import { PopoverOrigin } from '@mui/material/Popover/Popover'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ReactEditor, useSlateStatic } from 'slate-react'; + import { YjsEditor } from '@/application/slate-yjs'; import { CustomEditor } from '@/application/slate-yjs/command'; import { isEmbedBlockTypes } from '@/application/slate-yjs/command/const'; @@ -14,12 +21,8 @@ import { VideoBlockData, ViewLayout, } from '@/application/types'; -import { ReactComponent as DocumentIcon } from '@/assets/icons/page.svg'; // import { ReactComponent as AIWriterIcon } from '@/assets/slash_menu_icon_ai_writer.svg'; import { ReactComponent as EmojiIcon } from '@/assets/icons/add_emoji.svg'; -import { ReactComponent as BulletedListIcon } from '@/assets/icons/bulleted_list.svg'; -import { ReactComponent as CalloutIcon } from '@/assets/icons/callout.svg'; -import { ReactComponent as DividerIcon } from '@/assets/icons/divider.svg'; import { ReactComponent as FileIcon } from '@/assets/icons/file.svg'; import { ReactComponent as CodeIcon } from '@/assets/icons/inline_code.svg'; import { ReactComponent as RefDocumentIcon } from '@/assets/icons/ref_page.svg'; @@ -28,7 +31,10 @@ import { ReactComponent as TodoListIcon } from '@/assets/icons/todo.svg'; // import { ReactComponent as BoardIcon } from '@/assets/slash_menu_icon_kanban.svg'; // import { ReactComponent as CalendarIcon } from '@/assets/slash_menu_icon_calendar.svg'; import { ReactComponent as AskAIIcon } from '@/assets/icons/ai.svg'; +import { ReactComponent as BulletedListIcon } from '@/assets/icons/bulleted_list.svg'; +import { ReactComponent as CalloutIcon } from '@/assets/icons/callout.svg'; import { ReactComponent as ContinueWritingIcon } from '@/assets/icons/continue_writing.svg'; +import { ReactComponent as DividerIcon } from '@/assets/icons/divider.svg'; import { ReactComponent as OutlineIcon } from '@/assets/icons/doc.svg'; import { ReactComponent as FormulaIcon } from '@/assets/icons/formula.svg'; import { ReactComponent as Heading1Icon } from '@/assets/icons/h1.svg'; @@ -36,6 +42,7 @@ import { ReactComponent as Heading2Icon } from '@/assets/icons/h2.svg'; import { ReactComponent as Heading3Icon } from '@/assets/icons/h3.svg'; import { ReactComponent as ImageIcon } from '@/assets/icons/image.svg'; import { ReactComponent as NumberedListIcon } from '@/assets/icons/numbered_list.svg'; +import { ReactComponent as DocumentIcon } from '@/assets/icons/page.svg'; import { ReactComponent as QuoteIcon } from '@/assets/icons/quote.svg'; import { ReactComponent as TextIcon } from '@/assets/icons/text.svg'; import { ReactComponent as ToggleHeading1Icon } from '@/assets/icons/toggle_h1.svg'; @@ -43,7 +50,6 @@ import { ReactComponent as ToggleHeading2Icon } from '@/assets/icons/toggle_h2.s import { ReactComponent as ToggleHeading3Icon } from '@/assets/icons/toggle_h3.svg'; import { ReactComponent as ToggleListIcon } from '@/assets/icons/toggle_list.svg'; import { ReactComponent as VideoIcon } from '@/assets/icons/video.svg'; - import { usePopoverContext } from '@/components/editor/components/block-popover/BlockPopoverContext'; import { usePanelContext } from '@/components/editor/components/panels/Panels.hooks'; import { PanelType } from '@/components/editor/components/panels/PanelsContext'; @@ -52,12 +58,6 @@ import { useEditorContext } from '@/components/editor/EditorContext'; import { notify } from '@/components/_shared/notify'; import { calculateOptimalOrigins, Popover } from '@/components/_shared/popover'; import { getCharacters } from '@/utils/word'; -import { useAIWriter } from '@appflowyinc/ai-chat'; -import { Button } from '@mui/material'; -import { PopoverOrigin } from '@mui/material/Popover/Popover'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { ReactEditor, useSlateStatic } from 'slate-react'; export function SlashPanel({ setEmojiPosition, @@ -65,7 +65,7 @@ export function SlashPanel({ setEmojiPosition: (position: { top: number; left: number }) => void; }) { const { isPanelOpen, panelPosition, closePanel, searchText, removeContent } = usePanelContext(); - const { addPage, openPageModal, viewId, loadViewMeta } = useEditorContext(); + const { addPage, openPageModal, viewId, loadViewMeta, getMoreAIContext } = useEditorContext(); const [viewName, setViewName] = useState(''); const editor = useSlateStatic() as YjsEditor; @@ -100,15 +100,18 @@ export function SlashPanel({ const end = editor.end(selection); + const moreContext = getMoreAIContext?.(); + return ( viewName + '\n' + + (moreContext ? `More context: ${moreContext} \n` : '') + CustomEditor.getSelectionContent(editor, { anchor: start, focus: end, }) ); - }, [editor, viewName]); + }, [editor, viewName, getMoreAIContext]); const chars = useMemo(() => { if (!open) return 0; diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx index 4c7f4847..d3f17679 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Color.tsx @@ -159,14 +159,14 @@ function Color() { className={'relative flex h-6 w-6 items-center justify-center'} onClick={() => handlePickedColor(EditorMarkFormat.FontColor, color.color)} style={{ - color: color.color || 'var(--text-title)', + color: color.color || 'var(--text-primary)', }} >
diff --git a/src/components/editor/editor.scss b/src/components/editor/editor.scss index 1d973ead..0ca78d13 100644 --- a/src/components/editor/editor.scss +++ b/src/components/editor/editor.scss @@ -165,15 +165,15 @@ .block-element[data-block-type="todo_list"] .checked > .text-element .text-content { text-decoration: line-through; - color: var(--text-caption); + color: var(--text-secondary); .mention-content { - color: var(--text-caption); + color: var(--text-secondary); text-decoration: line-through; } .text-color span { - color: var(--text-caption) !important; + color: var(--text-secondary) !important; } } diff --git a/src/components/global-comment/GlobalComment.hooks.tsx b/src/components/global-comment/GlobalComment.hooks.tsx index b6486994..b60d5880 100644 --- a/src/components/global-comment/GlobalComment.hooks.tsx +++ b/src/components/global-comment/GlobalComment.hooks.tsx @@ -32,11 +32,11 @@ export const GlobalCommentContext = React.createContext<{ highLightCommentId: null, }); -export function useGlobalCommentContext () { +export function useGlobalCommentContext() { return useContext(GlobalCommentContext); } -export function useLoadReactions () { +export function useLoadReactions() { const viewId = useContext(PublishContext)?.viewMeta?.view_id; const service = useContext(AFConfigContext)?.service; const currentUser = useContext(AFConfigContext)?.currentUser; @@ -135,13 +135,13 @@ export function useLoadReactions () { console.error(e); } }, - [currentUser, service, viewId], + [currentUser, service, viewId] ); return { reactions, toggleReaction }; } -export function useLoadComments () { +export function useLoadComments() { const viewId = useContext(PublishContext)?.viewMeta?.view_id; const service = useContext(AFConfigContext)?.service; @@ -170,7 +170,7 @@ export function useLoadComments () { return { comments, loading, reload: fetchComments }; } -export function getAvatar (comment: GlobalComment) { +export function getAvatar(comment: GlobalComment) { if (comment.user?.avatarUrl) { const isFlag = isFlagEmoji(comment.user.avatarUrl); @@ -178,7 +178,7 @@ export function getAvatar (comment: GlobalComment) { children: {comment.user.avatarUrl}, sx: { bgcolor: 'transparent', - color: 'var(--text-title)', + color: 'var(--text-primary)', }, }; } @@ -186,7 +186,7 @@ export function getAvatar (comment: GlobalComment) { return stringAvatar(comment.user?.name || ''); } -export function useCommentRender (comment: GlobalComment) { +export function useCommentRender(comment: GlobalComment) { const { t } = useTranslation(); const avatar = useMemo(() => { return getAvatar(comment); diff --git a/src/pages/AcceptInvitationPage.tsx b/src/pages/AcceptInvitationPage.tsx index 3c75c75b..1f3015c0 100644 --- a/src/pages/AcceptInvitationPage.tsx +++ b/src/pages/AcceptInvitationPage.tsx @@ -90,7 +90,7 @@ function AcceptInvitationPage() {
diff --git a/src/styles/app.scss b/src/styles/app.scss index 8b082449..3a5b2c9e 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -28,7 +28,7 @@ .sketch-picker [id^='rc-editable-input'] { background-color: var(--bg-body) !important; border-color: var(--border-primary) !important; - color: var(--text-title) !important; + color: var(--text-primary) !important; box-shadow: var(--line-border) 0px 0px 0px 1px inset !important; }