diff --git a/src/application/database-yjs/dispatch.ts b/src/application/database-yjs/dispatch.ts index 725ad219..72c9fbdc 100644 --- a/src/application/database-yjs/dispatch.ts +++ b/src/application/database-yjs/dispatch.ts @@ -35,7 +35,7 @@ import { createCheckboxCell } from '@/application/database-yjs/fields/checkbox/u import { createRelationField } from '@/application/database-yjs/fields/relation/utils'; import { createRollupField } from '@/application/database-yjs/fields/rollup/utils'; import { createSelectOptionCell } from '@/application/database-yjs/fields/select-option/utils'; -import { createDateTimeField, createTextField } from '@/application/database-yjs/fields/text/utils'; +import { createDateTimeField } from '@/application/database-yjs/fields/text/utils'; import { dateFilterFillData, filterFillData, getDefaultFilterCondition } from '@/application/database-yjs/filter'; import { getOptionsFromRow, initialDatabaseRow } from '@/application/database-yjs/row'; import { generateRowMeta, getMetaIdMap, getMetaJSON, getRowKey } from '@/application/database-yjs/row_meta'; @@ -874,13 +874,90 @@ export function useUpdatePropertyNameDispatch(fieldId: string) { } function createField(type: FieldType, fieldId: string) { + const createSimpleField = ( + fieldType: FieldType, + initTypeOption?: (typeOption: YMapFieldTypeOption) => void + ) => { + const field = new Y.Map() as YDatabaseField; + const typeOptionMap = new Y.Map() as YDatabaseFieldTypeOption; + const typeOption = new Y.Map() as YMapFieldTypeOption; + const timestamp = String(dayjs().unix()); + + field.set(YjsDatabaseKey.name, getFieldName(fieldType)); + field.set(YjsDatabaseKey.id, fieldId); + field.set(YjsDatabaseKey.type, fieldType); + field.set(YjsDatabaseKey.created_at, timestamp); + field.set(YjsDatabaseKey.last_modified, timestamp); + field.set(YjsDatabaseKey.is_primary, false); + field.set(YjsDatabaseKey.icon, ''); + + initTypeOption?.(typeOption); + typeOptionMap.set(String(fieldType), typeOption); + field.set(YjsDatabaseKey.type_option, typeOptionMap); + + return field; + }; + switch (type) { case FieldType.RichText: - return createTextField(fieldId); + return createSimpleField(FieldType.RichText); + case FieldType.Number: + return createSimpleField(FieldType.Number, (typeOption) => { + typeOption.set(YjsDatabaseKey.format, NumberFormat.Num); + }); case FieldType.DateTime: return createDateTimeField(fieldId); + case FieldType.SingleSelect: + return createSimpleField(FieldType.SingleSelect, (typeOption) => { + typeOption.set( + YjsDatabaseKey.content, + JSON.stringify({ + disable_color: false, + options: [], + }) + ); + }); + case FieldType.MultiSelect: + return createSimpleField(FieldType.MultiSelect, (typeOption) => { + typeOption.set( + YjsDatabaseKey.content, + JSON.stringify({ + disable_color: false, + options: [], + }) + ); + }); + case FieldType.Checkbox: + return createSimpleField(FieldType.Checkbox); + case FieldType.URL: + return createSimpleField(FieldType.URL); + case FieldType.Checklist: + return createSimpleField(FieldType.Checklist); + case FieldType.LastEditedTime: + return createSimpleField(FieldType.LastEditedTime); + case FieldType.CreatedTime: + return createSimpleField(FieldType.CreatedTime); case FieldType.Relation: return createRelationField(fieldId); + case FieldType.AISummaries: + return createSimpleField(FieldType.AISummaries); + case FieldType.AITranslations: + return createSimpleField(FieldType.AITranslations, (typeOption) => { + typeOption.set(YjsDatabaseKey.language, AITranslateLanguage.English); + }); + case FieldType.Time: + return createSimpleField(FieldType.Time); + case FieldType.FileMedia: + return createSimpleField(FieldType.FileMedia, (typeOption) => { + typeOption.set( + YjsDatabaseKey.content, + JSON.stringify({ + hide_file_names: true, + }) + ); + }); + case FieldType.Person: + return createSimpleField(FieldType.Person); case FieldType.Rollup: return createRollupField(fieldId); default: @@ -902,14 +979,19 @@ export function useNewPropertyDispatch() { (view) => { const fields = database?.get(YjsDatabaseKey.fields); const fieldOrders = view?.get(YjsDatabaseKey.field_orders); + const fieldSettings = view?.get(YjsDatabaseKey.field_settings); - if (!fields || !fieldOrders) { + if (!fields || !fieldOrders || !fieldSettings) { throw new Error(`Field not found`); } const field: YDatabaseField = createField(fieldType, fieldId); fields.set(fieldId, field); + const setting = new Y.Map() as YDatabaseFieldSetting; + + setting.set(YjsDatabaseKey.visibility, FieldVisibility.AlwaysShown); + fieldSettings.set(fieldId, setting); fieldOrders.push([ { @@ -940,14 +1022,19 @@ export function useAddPropertyLeftDispatch() { (view) => { const fields = database?.get(YjsDatabaseKey.fields); const fieldOrders = view?.get(YjsDatabaseKey.field_orders); + const fieldSettings = view?.get(YjsDatabaseKey.field_settings); - if (!fields || !fieldOrders) { + if (!fields || !fieldOrders || !fieldSettings) { throw new Error(`Field not found`); } const field: YDatabaseField = createField(FieldType.RichText, newId); fields.set(newId, field); + const setting = new Y.Map() as YDatabaseFieldSetting; + + setting.set(YjsDatabaseKey.visibility, FieldVisibility.AlwaysShown); + fieldSettings.set(newId, setting); const index = fieldOrders.toArray().findIndex((field) => field.id === fieldId); @@ -981,14 +1068,19 @@ export function useAddPropertyRightDispatch() { (view) => { const fields = database?.get(YjsDatabaseKey.fields); const fieldOrders = view?.get(YjsDatabaseKey.field_orders); + const fieldSettings = view?.get(YjsDatabaseKey.field_settings); - if (!fields || !fieldOrders) { + if (!fields || !fieldOrders || !fieldSettings) { throw new Error(`Field not found`); } const field: YDatabaseField = createField(FieldType.RichText, newId); fields.set(newId, field); + const setting = new Y.Map() as YDatabaseFieldSetting; + + setting.set(YjsDatabaseKey.visibility, FieldVisibility.AlwaysShown); + fieldSettings.set(newId, setting); const index = fieldOrders.toArray().findIndex((field) => field.id === fieldId); @@ -1933,12 +2025,25 @@ export function useUpdateCellDispatch(rowId: string, fieldId: string) { const rowDoc = rowDocMap?.[rowId]; if (!rowDoc) { - throw new Error(`Row not found`); + Log.warn('[useUpdateCellDispatch] Row doc not found', { rowId, fieldId }); + return; } const rowSharedRoot = rowDoc.getMap(YjsEditorKey.data_section) as YSharedRoot; const row = rowSharedRoot.get(YjsEditorKey.database_row); + + if (!row) { + Log.warn('[useUpdateCellDispatch] Row data not found', { rowId, fieldId }); + return; + } + const cells = row.get(YjsDatabaseKey.cells); + + if (!cells) { + Log.warn('[useUpdateCellDispatch] Row cells not found', { rowId, fieldId }); + return; + } + const cell = cells.get(fieldId); const type = Number(field.get(YjsDatabaseKey.type)); @@ -2146,12 +2251,22 @@ function useEnhanceCalendarLayoutByFieldExists() { database, (view) => { const fieldOrders = view?.get(YjsDatabaseKey.field_orders); + const fieldSettings = view?.get(YjsDatabaseKey.field_settings); + + if (!fieldSettings) { + throw new Error(`Field settings not found`); + } fieldOrders.push([ { id: fieldId, }, ]); + + const setting = new Y.Map() as YDatabaseFieldSetting; + + setting.set(YjsDatabaseKey.visibility, FieldVisibility.AlwaysShown); + fieldSettings.set(fieldId, setting); }, 'newDateTimeField' ); @@ -2437,10 +2552,14 @@ export function useSwitchPropertyType() { FieldType.Number, FieldType.SingleSelect, FieldType.MultiSelect, + FieldType.Checklist, + FieldType.Checkbox, + FieldType.URL, FieldType.DateTime, FieldType.CreatedTime, FieldType.LastEditedTime, FieldType.FileMedia, + FieldType.AITranslations, FieldType.Rollup, ].includes(fieldType) ) { @@ -2527,6 +2646,10 @@ export function useSwitchPropertyType() { // Set the content for the type option newTypeOption.set(YjsDatabaseKey.content, content); + } else if (fieldType === FieldType.URL) { + newTypeOption.set(YjsDatabaseKey.content, ''); + } else if (fieldType === FieldType.AITranslations) { + newTypeOption.set(YjsDatabaseKey.language, AITranslateLanguage.English); } else if (fieldType === FieldType.FileMedia) { // to FileMedia const content = JSON.stringify({ @@ -2549,13 +2672,17 @@ export function useSwitchPropertyType() { field.set(YjsDatabaseKey.type, fieldType); const lastModified = field.get(YjsDatabaseKey.last_modified); + const createdAt = field.get(YjsDatabaseKey.created_at); + const currentName = field.get(YjsDatabaseKey.name); + const oldDefaultName = getFieldName(oldFieldType); + const isNewField = + createdAt !== undefined && + lastModified !== undefined && + String(createdAt) === String(lastModified); - // Before update-last modified time, check if the field is created - if (!lastModified) { - const fieldName = getFieldName(fieldType); - - // Set the default name for the field if it is created - field.set(YjsDatabaseKey.name, fieldName); + // Only auto-rename for untouched default fields (desktop parity). + if (isNewField && (!currentName || currentName === oldDefaultName)) { + field.set(YjsDatabaseKey.name, getFieldName(fieldType)); } field.set(YjsDatabaseKey.last_modified, String(dayjs().unix())); diff --git a/src/application/database-yjs/fields/relation/utils.ts b/src/application/database-yjs/fields/relation/utils.ts index 09944fc0..9ba8830a 100644 --- a/src/application/database-yjs/fields/relation/utils.ts +++ b/src/application/database-yjs/fields/relation/utils.ts @@ -1,3 +1,4 @@ +import dayjs from 'dayjs'; import * as Y from 'yjs'; import { FieldType } from '@/application/database-yjs'; @@ -12,10 +13,15 @@ export function createRelationField(fieldId: string) { const field = new Y.Map() as YDatabaseField; const typeOptionMap = new Y.Map() as YDatabaseFieldTypeOption; const typeOption = new Y.Map() as YMapFieldTypeOption; + const timestamp = String(dayjs().unix()); field.set(YjsDatabaseKey.name, 'Relation'); field.set(YjsDatabaseKey.id, fieldId); field.set(YjsDatabaseKey.type, FieldType.Relation); + field.set(YjsDatabaseKey.created_at, timestamp); + field.set(YjsDatabaseKey.last_modified, timestamp); + field.set(YjsDatabaseKey.is_primary, false); + field.set(YjsDatabaseKey.icon, ''); typeOption.set(YjsDatabaseKey.database_id, ''); typeOptionMap.set(String(FieldType.Relation), typeOption); diff --git a/src/application/database-yjs/fields/rollup/utils.ts b/src/application/database-yjs/fields/rollup/utils.ts index 2b23516f..b35ef34e 100644 --- a/src/application/database-yjs/fields/rollup/utils.ts +++ b/src/application/database-yjs/fields/rollup/utils.ts @@ -1,3 +1,4 @@ +import dayjs from 'dayjs'; import * as Y from 'yjs'; import { CalculationType, FieldType, RollupDisplayMode } from '@/application/database-yjs/database.type'; @@ -12,10 +13,15 @@ export function createRollupField(fieldId: string) { const field = new Y.Map() as YDatabaseField; const typeOptionMap = new Y.Map() as YDatabaseFieldTypeOption; const typeOption = new Y.Map() as YMapFieldTypeOption; + const timestamp = String(dayjs().unix()); field.set(YjsDatabaseKey.name, 'Rollup'); field.set(YjsDatabaseKey.id, fieldId); field.set(YjsDatabaseKey.type, FieldType.Rollup); + field.set(YjsDatabaseKey.created_at, timestamp); + field.set(YjsDatabaseKey.last_modified, timestamp); + field.set(YjsDatabaseKey.is_primary, false); + field.set(YjsDatabaseKey.icon, ''); typeOption.set(YjsDatabaseKey.relation_field_id, ''); typeOption.set(YjsDatabaseKey.target_field_id, ''); diff --git a/src/application/database-yjs/fields/text/utils.ts b/src/application/database-yjs/fields/text/utils.ts index b5c6f8ee..aca24c8c 100644 --- a/src/application/database-yjs/fields/text/utils.ts +++ b/src/application/database-yjs/fields/text/utils.ts @@ -1,25 +1,56 @@ +import dayjs from 'dayjs'; import * as Y from 'yjs'; import { FieldType } from '@/application/database-yjs'; -import { YDatabaseField, YjsDatabaseKey } from '@/application/types'; +import { + DateFormat, + TimeFormat, + YDatabaseField, + YDatabaseFieldTypeOption, + YMapFieldTypeOption, + YjsDatabaseKey, +} from '@/application/types'; export function createTextField (id: string) { const field = new Y.Map() as YDatabaseField; + const typeOptionMap = new Y.Map() as YDatabaseFieldTypeOption; + const typeOption = new Y.Map() as YMapFieldTypeOption; + const timestamp = String(dayjs().unix()); field.set(YjsDatabaseKey.name, 'Text'); field.set(YjsDatabaseKey.id, id); field.set(YjsDatabaseKey.type, FieldType.RichText); + field.set(YjsDatabaseKey.created_at, timestamp); + field.set(YjsDatabaseKey.last_modified, timestamp); + field.set(YjsDatabaseKey.is_primary, false); + field.set(YjsDatabaseKey.icon, ''); + + typeOptionMap.set(String(FieldType.RichText), typeOption); + field.set(YjsDatabaseKey.type_option, typeOptionMap); return field; } export function createDateTimeField(fieldId: string) { const field = new Y.Map() as YDatabaseField; + const typeOptionMap = new Y.Map() as YDatabaseFieldTypeOption; + const typeOption = new Y.Map() as YMapFieldTypeOption; + const timestamp = String(dayjs().unix()); field.set(YjsDatabaseKey.name, 'Date'); field.set(YjsDatabaseKey.id, fieldId); field.set(YjsDatabaseKey.type, FieldType.DateTime); - + field.set(YjsDatabaseKey.created_at, timestamp); + field.set(YjsDatabaseKey.last_modified, timestamp); + field.set(YjsDatabaseKey.is_primary, false); + field.set(YjsDatabaseKey.icon, ''); + + typeOption.set(YjsDatabaseKey.time_format, TimeFormat.TwentyFourHour); + typeOption.set(YjsDatabaseKey.date_format, DateFormat.Friendly); + typeOption.set(YjsDatabaseKey.include_time, true); + typeOptionMap.set(String(FieldType.DateTime), typeOption); + field.set(YjsDatabaseKey.type_option, typeOptionMap); + return field; } diff --git a/src/application/types.ts b/src/application/types.ts index f57d9d6a..1a6fec74 100644 --- a/src/application/types.ts +++ b/src/application/types.ts @@ -730,7 +730,7 @@ export interface YDatabaseField extends Y.Map { get(key: YjsDatabaseKey.is_primary): boolean; - get(key: YjsDatabaseKey.last_modified): LastModified; + get(key: YjsDatabaseKey.created_at | YjsDatabaseKey.last_modified): LastModified; } export interface YDatabaseFieldTypeOption extends Y.Map { diff --git a/vite.config.ts b/vite.config.ts index 4b4b96f0..57c07af1 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -170,6 +170,13 @@ export default defineConfig({ watch: { ignored: ['node_modules'], }, + proxy: { + '/gotrue': { + target: 'http://localhost:9999', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/gotrue/, ''), + }, + }, cors: false, sourcemapIgnoreList: false, },