mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Fix/tauri board events (#2678)
* chore: focus name input on field edit * fix: remove random id for duplicated view * fix: use alias for imports * fix: folder, grid, board bugs * chore: remove log * fix: update options list on add * chore: close on delete option * chore: show and hide field * chore: add field with specific type * chore: small cleanup * fix: create view on another folder and views notifier reorganize --------- Co-authored-by: qinluhe <qinluhe.twodog@gmail.com>
This commit is contained in:
parent
4f2585baed
commit
6a43dd871d
@ -53,6 +53,7 @@ export const EditCheckListPopup = ({
|
|||||||
const onDeleteOptionClick = async () => {
|
const onDeleteOptionClick = async () => {
|
||||||
const svc = new SelectOptionCellBackendService(cellIdentifier);
|
const svc = new SelectOptionCellBackendService(cellIdentifier);
|
||||||
await svc.deleteOption([editingSelectOption]);
|
await svc.deleteOption([editingSelectOption]);
|
||||||
|
onOutsideClick();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MouseEventHandler, useEffect, useRef, useState } from 'react';
|
import { FocusEventHandler, MouseEventHandler, useEffect, useRef, useState } from 'react';
|
||||||
import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon';
|
import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon';
|
||||||
import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName';
|
import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -36,12 +36,24 @@ export const EditFieldPopup = ({
|
|||||||
const databaseStore = useAppSelector((state) => state.database);
|
const databaseStore = useAppSelector((state) => state.database);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const changeTypeButtonRef = useRef<HTMLDivElement>(null);
|
const changeTypeButtonRef = useRef<HTMLDivElement>(null);
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setName(databaseStore.fields[cellIdentifier.fieldId].title);
|
setName(databaseStore.fields[cellIdentifier.fieldId].title);
|
||||||
}, [databaseStore, cellIdentifier]);
|
}, [databaseStore, cellIdentifier]);
|
||||||
|
|
||||||
|
// focus input on mount
|
||||||
|
useEffect(() => {
|
||||||
|
if (!inputRef.current || !name) return;
|
||||||
|
inputRef.current.focus();
|
||||||
|
}, [inputRef, name]);
|
||||||
|
|
||||||
|
const selectAll: FocusEventHandler<HTMLInputElement> = (e) => {
|
||||||
|
e.target.selectionStart = 0;
|
||||||
|
e.target.selectionEnd = e.target.value.length;
|
||||||
|
};
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
if (!fieldInfo) return;
|
if (!fieldInfo) return;
|
||||||
const controller = new TypeOptionController(viewId, Some(fieldInfo));
|
const controller = new TypeOptionController(viewId, Some(fieldInfo));
|
||||||
@ -80,6 +92,8 @@ export const EditFieldPopup = ({
|
|||||||
>
|
>
|
||||||
<div className={'flex flex-col gap-2'}>
|
<div className={'flex flex-col gap-2'}>
|
||||||
<input
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
onFocus={selectAll}
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
onBlur={() => save()}
|
onBlur={() => save()}
|
||||||
|
@ -22,6 +22,7 @@ import { EditCheckListPopup } from '$app/components/_shared/EditRow/CheckList/Ed
|
|||||||
import { PropertiesPanel } from '$app/components/_shared/EditRow/PropertiesPanel';
|
import { PropertiesPanel } from '$app/components/_shared/EditRow/PropertiesPanel';
|
||||||
import { ImageSvg } from '$app/components/_shared/svg/ImageSvg';
|
import { ImageSvg } from '$app/components/_shared/svg/ImageSvg';
|
||||||
import { PromptWindow } from '$app/components/_shared/PromptWindow';
|
import { PromptWindow } from '$app/components/_shared/PromptWindow';
|
||||||
|
import { useAppSelector } from '$app/stores/store';
|
||||||
|
|
||||||
export const EditRow = ({
|
export const EditRow = ({
|
||||||
onClose,
|
onClose,
|
||||||
@ -34,6 +35,7 @@ export const EditRow = ({
|
|||||||
controller: DatabaseController;
|
controller: DatabaseController;
|
||||||
rowInfo: RowInfo;
|
rowInfo: RowInfo;
|
||||||
}) => {
|
}) => {
|
||||||
|
const databaseStore = useAppSelector((state) => state.database);
|
||||||
const { cells, onNewColumnClick } = useRow(viewId, controller, rowInfo);
|
const { cells, onNewColumnClick } = useRow(viewId, controller, rowInfo);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [unveil, setUnveil] = useState(false);
|
const [unveil, setUnveil] = useState(false);
|
||||||
@ -226,19 +228,21 @@ export const EditRow = ({
|
|||||||
showFieldEditor || showChangeOptionsPopup || showDatePicker ? 'overflow-hidden' : 'overflow-auto'
|
showFieldEditor || showChangeOptionsPopup || showDatePicker ? 'overflow-hidden' : 'overflow-auto'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{cells.map((cell, cellIndex) => (
|
{cells
|
||||||
<EditCellWrapper
|
.filter((cell) => databaseStore.fields[cell.cellIdentifier.fieldId].visible)
|
||||||
index={cellIndex}
|
.map((cell, cellIndex) => (
|
||||||
key={cellIndex}
|
<EditCellWrapper
|
||||||
cellIdentifier={cell.cellIdentifier}
|
index={cellIndex}
|
||||||
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
key={cellIndex}
|
||||||
fieldController={controller.fieldController}
|
cellIdentifier={cell.cellIdentifier}
|
||||||
onEditFieldClick={onEditFieldClick}
|
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||||
onEditOptionsClick={onEditOptionsClick}
|
fieldController={controller.fieldController}
|
||||||
onEditDateClick={onEditDateClick}
|
onEditFieldClick={onEditFieldClick}
|
||||||
onEditCheckListClick={onEditCheckListClick}
|
onEditOptionsClick={onEditOptionsClick}
|
||||||
></EditCellWrapper>
|
onEditDateClick={onEditDateClick}
|
||||||
))}
|
onEditCheckListClick={onEditCheckListClick}
|
||||||
|
></EditCellWrapper>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Droppable>
|
</Droppable>
|
||||||
@ -261,6 +265,7 @@ export const EditRow = ({
|
|||||||
controller={controller}
|
controller={controller}
|
||||||
rowInfo={rowInfo}
|
rowInfo={rowInfo}
|
||||||
onDeletePropertyClick={onDeletePropertyClick}
|
onDeletePropertyClick={onDeletePropertyClick}
|
||||||
|
onNewColumnClick={onNewColumnClick}
|
||||||
></PropertiesPanel>
|
></PropertiesPanel>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -292,7 +297,7 @@ export const EditRow = ({
|
|||||||
cellIdentifier={editingCell}
|
cellIdentifier={editingCell}
|
||||||
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||||
fieldController={controller.fieldController}
|
fieldController={controller.fieldController}
|
||||||
onOutsideClick={() => setShowChangeOptionsPopup(false)}
|
onOutsideClick={() => !showEditCellOption && setShowChangeOptionsPopup(false)}
|
||||||
openOptionDetail={onOpenOptionDetailClick}
|
openOptionDetail={onOpenOptionDetailClick}
|
||||||
></CellOptionsPopup>
|
></CellOptionsPopup>
|
||||||
)}
|
)}
|
||||||
@ -335,7 +340,7 @@ export const EditRow = ({
|
|||||||
cellIdentifier={editingCell}
|
cellIdentifier={editingCell}
|
||||||
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||||
fieldController={controller.fieldController}
|
fieldController={controller.fieldController}
|
||||||
onOutsideClick={() => setShowCheckListPopup(false)}
|
onOutsideClick={() => !showEditCheckList && setShowCheckListPopup(false)}
|
||||||
openCheckListDetail={onOpenCheckListDetailClick}
|
openCheckListDetail={onOpenCheckListDetailClick}
|
||||||
></CheckListPopup>
|
></CheckListPopup>
|
||||||
)}
|
)}
|
||||||
|
@ -67,6 +67,7 @@ export const EditCellOptionPopup = ({
|
|||||||
const onDeleteOptionClick = async () => {
|
const onDeleteOptionClick = async () => {
|
||||||
const svc = new SelectOptionCellBackendService(cellIdentifier);
|
const svc = new SelectOptionCellBackendService(cellIdentifier);
|
||||||
await svc.deleteOption([editingSelectOption]);
|
await svc.deleteOption([editingSelectOption]);
|
||||||
|
onOutsideClick();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg';
|
import { DropDownShowSvg } from '$app/components/_shared/svg/DropDownShowSvg';
|
||||||
import { useEffect, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useRow } from '$app/components/_shared/database-hooks/useRow';
|
import { useRow } from '$app/components/_shared/database-hooks/useRow';
|
||||||
import { DatabaseController } from '$app/stores/effects/database/database_controller';
|
import { DatabaseController } from '$app/stores/effects/database/database_controller';
|
||||||
import { RowInfo } from '$app/stores/effects/database/row/row_cache';
|
import { RowInfo } from '$app/stores/effects/database/row/row_cache';
|
||||||
@ -12,6 +12,9 @@ import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
|
|||||||
import { MultiSelectTypeSvg } from '$app/components/_shared/svg/MultiSelectTypeSvg';
|
import { MultiSelectTypeSvg } from '$app/components/_shared/svg/MultiSelectTypeSvg';
|
||||||
import { DocumentSvg } from '$app/components/_shared/svg/DocumentSvg';
|
import { DocumentSvg } from '$app/components/_shared/svg/DocumentSvg';
|
||||||
import { SingleSelectTypeSvg } from '$app/components/_shared/svg/SingleSelectTypeSvg';
|
import { SingleSelectTypeSvg } from '$app/components/_shared/svg/SingleSelectTypeSvg';
|
||||||
|
import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller';
|
||||||
|
import { Some } from 'ts-results';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const typesOrder: FieldType[] = [
|
const typesOrder: FieldType[] = [
|
||||||
FieldType.RichText,
|
FieldType.RichText,
|
||||||
@ -29,28 +32,67 @@ export const PropertiesPanel = ({
|
|||||||
controller,
|
controller,
|
||||||
rowInfo,
|
rowInfo,
|
||||||
onDeletePropertyClick,
|
onDeletePropertyClick,
|
||||||
|
onNewColumnClick,
|
||||||
}: {
|
}: {
|
||||||
viewId: string;
|
viewId: string;
|
||||||
controller: DatabaseController;
|
controller: DatabaseController;
|
||||||
rowInfo: RowInfo;
|
rowInfo: RowInfo;
|
||||||
onDeletePropertyClick: (fieldId: string) => void;
|
onDeletePropertyClick: (fieldId: string) => void;
|
||||||
|
onNewColumnClick: (initialFieldType: FieldType, name?: string) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const { cells } = useRow(viewId, controller, rowInfo);
|
const { cells } = useRow(viewId, controller, rowInfo);
|
||||||
const databaseStore = useAppSelector((state) => state.database);
|
const databaseStore = useAppSelector((state) => state.database);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [showAddedProperties, setShowAddedProperties] = useState(true);
|
const [showAddedProperties, setShowAddedProperties] = useState(true);
|
||||||
const [showBasicProperties, setShowBasicProperties] = useState(false);
|
const [showBasicProperties, setShowBasicProperties] = useState(false);
|
||||||
const [showAdvancedProperties, setShowAdvancedProperties] = useState(false);
|
const [showAdvancedProperties, setShowAdvancedProperties] = useState(false);
|
||||||
|
|
||||||
const [hoveredPropertyIndex, setHoveredPropertyIndex] = useState(-1);
|
const [hoveredPropertyIndex, setHoveredPropertyIndex] = useState(-1);
|
||||||
const [hiddenProperties, setHiddenProperties] = useState<boolean[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const toggleHideProperty = async (v: boolean, index: number) => {
|
||||||
setHiddenProperties(cells.map(() => false));
|
const fieldInfo = controller.fieldController.getField(cells[index].fieldId);
|
||||||
}, [cells]);
|
if (fieldInfo) {
|
||||||
|
const typeController = new TypeOptionController(viewId, Some(fieldInfo));
|
||||||
|
await typeController.initialize();
|
||||||
|
if (fieldInfo.field.visibility) {
|
||||||
|
await typeController.hideField();
|
||||||
|
} else {
|
||||||
|
await typeController.showField();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const toggleHideProperty = (v: boolean, index: number) => {
|
const addSelectedFieldType = async (fieldType: FieldType) => {
|
||||||
setHiddenProperties(hiddenProperties.map((h, i) => (i === index ? !v : h)));
|
let name = 'New Field';
|
||||||
|
switch (fieldType) {
|
||||||
|
case FieldType.RichText:
|
||||||
|
name = t('grid.field.textFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.Number:
|
||||||
|
name = t('grid.field.numberFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.DateTime:
|
||||||
|
name = t('grid.field.dateFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.SingleSelect:
|
||||||
|
name = t('grid.field.singleSelectFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.MultiSelect:
|
||||||
|
name = t('grid.field.multiSelectFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.Checklist:
|
||||||
|
name = t('grid.field.checklistFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.URL:
|
||||||
|
name = t('grid.field.urlFieldName');
|
||||||
|
break;
|
||||||
|
case FieldType.Checkbox:
|
||||||
|
name = t('grid.field.checkboxFieldName');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await onNewColumnClick(fieldType, name);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -91,7 +133,10 @@ export const PropertiesPanel = ({
|
|||||||
>
|
>
|
||||||
<TrashSvg></TrashSvg>
|
<TrashSvg></TrashSvg>
|
||||||
</i>
|
</i>
|
||||||
<Switch value={!hiddenProperties[cellIndex]} setValue={(v) => toggleHideProperty(v, cellIndex)}></Switch>
|
<Switch
|
||||||
|
value={!!databaseStore.fields[cell.cellIdentifier.fieldId]?.visible}
|
||||||
|
setValue={(v) => toggleHideProperty(v, cellIndex)}
|
||||||
|
></Switch>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -108,17 +153,17 @@ export const PropertiesPanel = ({
|
|||||||
<div className={'flex flex-col gap-2 text-xs'}>
|
<div className={'flex flex-col gap-2 text-xs'}>
|
||||||
{showBasicProperties && (
|
{showBasicProperties && (
|
||||||
<div className={'flex flex-col'}>
|
<div className={'flex flex-col'}>
|
||||||
{typesOrder.map((t, i) => (
|
{typesOrder.map((type, i) => (
|
||||||
<button
|
<button
|
||||||
onClick={() => console.log('type clicked')}
|
onClick={() => addSelectedFieldType(type)}
|
||||||
key={i}
|
key={i}
|
||||||
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
|
className={'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 pr-8 hover:bg-main-secondary'}
|
||||||
>
|
>
|
||||||
<i className={'h-5 w-5'}>
|
<i className={'h-5 w-5'}>
|
||||||
<FieldTypeIcon fieldType={t}></FieldTypeIcon>
|
<FieldTypeIcon fieldType={type}></FieldTypeIcon>
|
||||||
</i>
|
</i>
|
||||||
<span>
|
<span>
|
||||||
<FieldTypeName fieldType={t}></FieldTypeName>
|
<FieldTypeName fieldType={type}></FieldTypeName>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { TypeOptionController } from '../../../stores/effects/database/field/type_option/type_option_controller';
|
import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller';
|
||||||
import { Some } from 'ts-results';
|
import { Some } from 'ts-results';
|
||||||
import { IDatabaseField, ISelectOption } from '../../../stores/reducers/database/slice';
|
import { IDatabaseField, ISelectOption } from '$app_reducers/database/slice';
|
||||||
import { ChecklistTypeOptionPB, FieldType, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB } from '@/services/backend';
|
import { ChecklistTypeOptionPB, FieldType, MultiSelectTypeOptionPB, SingleSelectTypeOptionPB } from '@/services/backend';
|
||||||
import {
|
import {
|
||||||
makeChecklistTypeOptionContext,
|
makeChecklistTypeOptionContext,
|
||||||
@ -8,10 +8,10 @@ import {
|
|||||||
makeMultiSelectTypeOptionContext,
|
makeMultiSelectTypeOptionContext,
|
||||||
makeNumberTypeOptionContext,
|
makeNumberTypeOptionContext,
|
||||||
makeSingleSelectTypeOptionContext,
|
makeSingleSelectTypeOptionContext,
|
||||||
} from '../../../stores/effects/database/field/type_option/type_option_context';
|
} from '$app/stores/effects/database/field/type_option/type_option_context';
|
||||||
import { boardActions } from '../../../stores/reducers/board/slice';
|
import { boardActions } from '$app_reducers/board/slice';
|
||||||
import { FieldInfo } from '../../../stores/effects/database/field/field_controller';
|
import { FieldInfo } from '$app/stores/effects/database/field/field_controller';
|
||||||
import { AppDispatch } from '../../../stores/store';
|
import { AppDispatch } from '$app/stores/store';
|
||||||
|
|
||||||
export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?: AppDispatch): Promise<IDatabaseField> {
|
export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?: AppDispatch): Promise<IDatabaseField> {
|
||||||
const field = fieldInfo.field;
|
const field = fieldInfo.field;
|
||||||
@ -53,6 +53,7 @@ export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?:
|
|||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
title: field.name,
|
title: field.name,
|
||||||
fieldType: field.field_type,
|
fieldType: field.field_type,
|
||||||
|
visible: field.visibility,
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
selectOptions,
|
selectOptions,
|
||||||
},
|
},
|
||||||
@ -64,6 +65,7 @@ export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?:
|
|||||||
return {
|
return {
|
||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
title: field.name,
|
title: field.name,
|
||||||
|
visible: field.visibility,
|
||||||
fieldType: field.field_type,
|
fieldType: field.field_type,
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
numberFormat: typeOption.format,
|
numberFormat: typeOption.format,
|
||||||
@ -76,6 +78,7 @@ export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?:
|
|||||||
return {
|
return {
|
||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
title: field.name,
|
title: field.name,
|
||||||
|
visible: field.visibility,
|
||||||
fieldType: field.field_type,
|
fieldType: field.field_type,
|
||||||
fieldOptions: {
|
fieldOptions: {
|
||||||
dateFormat: typeOption.date_format,
|
dateFormat: typeOption.date_format,
|
||||||
@ -88,6 +91,7 @@ export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?:
|
|||||||
return {
|
return {
|
||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
title: field.name,
|
title: field.name,
|
||||||
|
visible: field.visibility,
|
||||||
fieldType: field.field_type,
|
fieldType: field.field_type,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,17 @@ import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
|||||||
import { CellCache } from '$app/stores/effects/database/cell/cell_cache';
|
import { CellCache } from '$app/stores/effects/database/cell/cell_cache';
|
||||||
import { FieldController } from '$app/stores/effects/database/field/field_controller';
|
import { FieldController } from '$app/stores/effects/database/field/field_controller';
|
||||||
import { CellControllerBuilder } from '$app/stores/effects/database/cell/controller_builder';
|
import { CellControllerBuilder } from '$app/stores/effects/database/cell/controller_builder';
|
||||||
import { DateCellDataPB, SelectOptionCellDataPB, URLCellDataPB } from '$app/../services/backend';
|
import { DateCellDataPB, SelectOptionCellDataPB, URLCellDataPB } from '@/services/backend';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { CellController } from '$app/stores/effects/database/cell/cell_controller';
|
import { CellController } from '$app/stores/effects/database/cell/cell_controller';
|
||||||
|
import { useAppDispatch, useAppSelector } from '$app/stores/store';
|
||||||
|
import { databaseActions, ISelectOptionType } from '$app_reducers/database/slice';
|
||||||
|
|
||||||
export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => {
|
export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => {
|
||||||
const [data, setData] = useState<DateCellDataPB | URLCellDataPB | SelectOptionCellDataPB | string | undefined>();
|
const [data, setData] = useState<DateCellDataPB | URLCellDataPB | SelectOptionCellDataPB | string | undefined>();
|
||||||
const [cellController, setCellController] = useState<CellController<any, any>>();
|
const [cellController, setCellController] = useState<CellController<any, any>>();
|
||||||
|
const databaseStore = useAppSelector((state) => state.database);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!cellIdentifier || !cellCache || !fieldController) return;
|
if (!cellIdentifier || !cellCache || !fieldController) return;
|
||||||
@ -19,7 +23,34 @@ export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fi
|
|||||||
c.subscribeChanged({
|
c.subscribeChanged({
|
||||||
onCellChanged: (cellData) => {
|
onCellChanged: (cellData) => {
|
||||||
if (cellData.some) {
|
if (cellData.some) {
|
||||||
setData(cellData.val);
|
const value = cellData.val;
|
||||||
|
setData(value);
|
||||||
|
|
||||||
|
// update redux store for database field if there are new select options
|
||||||
|
if (
|
||||||
|
value instanceof SelectOptionCellDataPB &&
|
||||||
|
(databaseStore.fields[cellIdentifier.fieldId].fieldOptions as ISelectOptionType).selectOptions.length !==
|
||||||
|
value.options.length
|
||||||
|
) {
|
||||||
|
const field = { ...databaseStore.fields[cellIdentifier.fieldId] };
|
||||||
|
const selectOptions = value.options.map((option) => ({
|
||||||
|
selectOptionId: option.id,
|
||||||
|
title: option.name,
|
||||||
|
color: option.color,
|
||||||
|
}));
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
databaseActions.updateField({
|
||||||
|
field: {
|
||||||
|
...field,
|
||||||
|
fieldOptions: {
|
||||||
|
...field.fieldOptions,
|
||||||
|
selectOptions: selectOptions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -45,12 +45,12 @@ export const useDatabase = (viewId: string, type?: ViewLayoutPB) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!controller) return;
|
|
||||||
|
|
||||||
void (async () => {
|
void (async () => {
|
||||||
|
if (!controller) return;
|
||||||
controller.subscribe({
|
controller.subscribe({
|
||||||
onRowsChanged: (rowInfos) => {
|
onRowsChanged: (rowInfos) => {
|
||||||
setRows(rowInfos);
|
// TODO: this is a hack to make sure that the row cache is updated
|
||||||
|
setRows([...rowInfos]);
|
||||||
},
|
},
|
||||||
onFieldsChanged: (fieldInfos) => {
|
onFieldsChanged: (fieldInfos) => {
|
||||||
void loadFields(fieldInfos);
|
void loadFields(fieldInfos);
|
||||||
@ -72,6 +72,10 @@ export const useDatabase = (viewId: string, type?: ViewLayoutPB) => {
|
|||||||
setGroups(controller.groups.value);
|
setGroups(controller.groups.value);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
void controller?.dispose();
|
||||||
|
};
|
||||||
}, [controller]);
|
}, [controller]);
|
||||||
|
|
||||||
const onNewRowClick = async (index: number) => {
|
const onNewRowClick = async (index: number) => {
|
||||||
|
@ -4,8 +4,9 @@ import { RowInfo } from '$app/stores/effects/database/row/row_cache';
|
|||||||
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller';
|
import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller';
|
||||||
import { None } from 'ts-results';
|
|
||||||
import { useAppSelector } from '$app/stores/store';
|
import { useAppSelector } from '$app/stores/store';
|
||||||
|
import { FieldType } from '@/services/backend';
|
||||||
|
import { None } from 'ts-results';
|
||||||
|
|
||||||
export const useRow = (viewId: string, databaseController: DatabaseController, rowInfo: RowInfo) => {
|
export const useRow = (viewId: string, databaseController: DatabaseController, rowInfo: RowInfo) => {
|
||||||
const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]);
|
const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]);
|
||||||
@ -42,10 +43,13 @@ export const useRow = (viewId: string, databaseController: DatabaseController, r
|
|||||||
})();
|
})();
|
||||||
}, [rowController, databaseStore.columns]);
|
}, [rowController, databaseStore.columns]);
|
||||||
|
|
||||||
const onNewColumnClick = async () => {
|
const onNewColumnClick = async (initialFieldType: FieldType = FieldType.RichText, name?: string) => {
|
||||||
if (!databaseController) return;
|
if (!databaseController) return;
|
||||||
const controller = new TypeOptionController(viewId, None);
|
const controller = new TypeOptionController(viewId, None, initialFieldType);
|
||||||
await controller.initialize();
|
await controller.initialize();
|
||||||
|
if (name) {
|
||||||
|
await controller.setFieldName(name);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -9,7 +9,7 @@ import { EditRow } from '$app/components/_shared/EditRow/EditRow';
|
|||||||
import { BoardToolbar } from '$app/components/board/BoardToolbar';
|
import { BoardToolbar } from '$app/components/board/BoardToolbar';
|
||||||
|
|
||||||
export const Board = ({ viewId, title }: { viewId: string; title: string }) => {
|
export const Board = ({ viewId, title }: { viewId: string; title: string }) => {
|
||||||
const { controller, rows, groups, groupByFieldId, onNewRowClick, onDragEnd } = useDatabase(viewId, ViewLayoutPB.Board);
|
const { controller, groups, groupByFieldId, onNewRowClick, onDragEnd } = useDatabase(viewId, ViewLayoutPB.Board);
|
||||||
const [showBoardRow, setShowBoardRow] = useState(false);
|
const [showBoardRow, setShowBoardRow] = useState(false);
|
||||||
const [boardRowInfo, setBoardRowInfo] = useState<RowInfo>();
|
const [boardRowInfo, setBoardRowInfo] = useState<RowInfo>();
|
||||||
|
|
||||||
@ -38,7 +38,6 @@ export const Board = ({ viewId, title }: { viewId: string; title: string }) => {
|
|||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
controller={controller}
|
controller={controller}
|
||||||
group={group}
|
group={group}
|
||||||
allRows={rows}
|
|
||||||
groupByFieldId={groupByFieldId}
|
groupByFieldId={groupByFieldId}
|
||||||
onNewRowClick={() => onNewRowClick(index)}
|
onNewRowClick={() => onNewRowClick(index)}
|
||||||
onOpenRow={onOpenRow}
|
onOpenRow={onOpenRow}
|
||||||
|
@ -9,6 +9,7 @@ import { PopupWindow } from '$app/components/_shared/PopupWindow';
|
|||||||
import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
|
import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
|
||||||
import { RowBackendService } from '$app/stores/effects/database/row/row_bd_svc';
|
import { RowBackendService } from '$app/stores/effects/database/row/row_bd_svc';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useAppSelector } from '$app/stores/store';
|
||||||
|
|
||||||
export const BoardCard = ({
|
export const BoardCard = ({
|
||||||
index,
|
index,
|
||||||
@ -25,6 +26,7 @@ export const BoardCard = ({
|
|||||||
groupByFieldId: string;
|
groupByFieldId: string;
|
||||||
onOpenRow: (rowId: RowInfo) => void;
|
onOpenRow: (rowId: RowInfo) => void;
|
||||||
}) => {
|
}) => {
|
||||||
|
const databaseStore = useAppSelector((state) => state.database);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { cells } = useRow(viewId, controller, rowInfo);
|
const { cells } = useRow(viewId, controller, rowInfo);
|
||||||
@ -70,7 +72,9 @@ export const BoardCard = ({
|
|||||||
</button>
|
</button>
|
||||||
<div className={'flex flex-col gap-3'}>
|
<div className={'flex flex-col gap-3'}>
|
||||||
{cells
|
{cells
|
||||||
.filter((cell) => cell.fieldId !== groupByFieldId)
|
.filter(
|
||||||
|
(cell) => cell.fieldId !== groupByFieldId && databaseStore.fields[cell.cellIdentifier.fieldId].visible
|
||||||
|
)
|
||||||
.map((cell, cellIndex) => (
|
.map((cell, cellIndex) => (
|
||||||
<BoardCell
|
<BoardCell
|
||||||
key={cellIndex}
|
key={cellIndex}
|
||||||
|
@ -6,11 +6,11 @@ import { DatabaseController } from '$app/stores/effects/database/database_contro
|
|||||||
import { Droppable } from 'react-beautiful-dnd';
|
import { Droppable } from 'react-beautiful-dnd';
|
||||||
import { DatabaseGroupController } from '$app/stores/effects/database/group/group_controller';
|
import { DatabaseGroupController } from '$app/stores/effects/database/group/group_controller';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
export const BoardGroup = ({
|
export const BoardGroup = ({
|
||||||
viewId,
|
viewId,
|
||||||
controller,
|
controller,
|
||||||
allRows,
|
|
||||||
groupByFieldId,
|
groupByFieldId,
|
||||||
onNewRowClick,
|
onNewRowClick,
|
||||||
onOpenRow,
|
onOpenRow,
|
||||||
@ -18,7 +18,6 @@ export const BoardGroup = ({
|
|||||||
}: {
|
}: {
|
||||||
viewId: string;
|
viewId: string;
|
||||||
controller: DatabaseController;
|
controller: DatabaseController;
|
||||||
allRows: readonly RowInfo[];
|
|
||||||
groupByFieldId: string;
|
groupByFieldId: string;
|
||||||
onNewRowClick: () => void;
|
onNewRowClick: () => void;
|
||||||
onOpenRow: (rowId: RowInfo) => void;
|
onOpenRow: (rowId: RowInfo) => void;
|
||||||
@ -26,6 +25,23 @@ export const BoardGroup = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [rows, setRows] = useState<RowInfo[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
const reloadRows = () => {
|
||||||
|
setRows(group.rows.map((rowPB) => new RowInfo(viewId, controller.fieldController.fieldInfos, rowPB)));
|
||||||
|
};
|
||||||
|
reloadRows();
|
||||||
|
group.subscribe({
|
||||||
|
onRemoveRow: reloadRows,
|
||||||
|
onInsertRow: reloadRows,
|
||||||
|
onUpdateRow: reloadRows,
|
||||||
|
onCreateRow: reloadRows,
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
group.unsubscribe();
|
||||||
|
};
|
||||||
|
}, [controller, group, viewId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex h-full w-[250px] flex-col rounded-lg bg-surface-1'}>
|
<div className={'flex h-full w-[250px] flex-col rounded-lg bg-surface-1'}>
|
||||||
<div className={'flex items-center justify-between p-4'}>
|
<div className={'flex items-center justify-between p-4'}>
|
||||||
@ -49,9 +65,8 @@ export const BoardGroup = ({
|
|||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
>
|
>
|
||||||
{group.rows.map((row_pb, index) => {
|
{rows.map((row, index) => {
|
||||||
const row = allRows.find((r) => r.row.id === row_pb.id);
|
return (
|
||||||
return row ? (
|
|
||||||
<BoardCard
|
<BoardCard
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
controller={controller}
|
controller={controller}
|
||||||
@ -61,8 +76,6 @@ export const BoardGroup = ({
|
|||||||
groupByFieldId={groupByFieldId}
|
groupByFieldId={groupByFieldId}
|
||||||
onOpenRow={onOpenRow}
|
onOpenRow={onOpenRow}
|
||||||
></BoardCard>
|
></BoardCard>
|
||||||
) : (
|
|
||||||
<span key={index}></span>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,21 +32,20 @@ export const useFolderEvents = (folder: IFolder, pages: IPage[]) => {
|
|||||||
|
|
||||||
// Backend services
|
// Backend services
|
||||||
const appBackendService = new AppBackendService(folder.id);
|
const appBackendService = new AppBackendService(folder.id);
|
||||||
const workspaceBackendService = new WorkspaceBackendService(workspace.id ?? '');
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void appObserver.subscribe({
|
void appObserver.subscribe({
|
||||||
onAppChanged: (change) => {
|
onViewsChanged: async () => {
|
||||||
if (change.ok) {
|
const result = await appBackendService.getAllViews();
|
||||||
const views = change.val;
|
if (!result.ok) return;
|
||||||
const updatedPages: IPage[] = views.items.map((view) => ({
|
const views = result.val;
|
||||||
id: view.id,
|
const updatedPages: IPage[] = views.map((view) => ({
|
||||||
folderId: view.parent_view_id,
|
id: view.id,
|
||||||
pageType: view.layout,
|
folderId: view.parent_view_id,
|
||||||
title: view.name,
|
pageType: view.layout,
|
||||||
}));
|
title: view.name,
|
||||||
appDispatch(pagesActions.didReceivePages(updatedPages));
|
}));
|
||||||
}
|
appDispatch(pagesActions.didReceivePages({ pages: updatedPages, folderId: folder.id }));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
@ -100,6 +99,7 @@ export const useFolderEvents = (folder: IFolder, pages: IPage[]) => {
|
|||||||
|
|
||||||
const duplicateFolder = async () => {
|
const duplicateFolder = async () => {
|
||||||
closePopup();
|
closePopup();
|
||||||
|
const workspaceBackendService = new WorkspaceBackendService(workspace.id ?? '');
|
||||||
const newApp = await workspaceBackendService.createApp({
|
const newApp = await workspaceBackendService.createApp({
|
||||||
name: folder.title,
|
name: folder.title,
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useAppSelector } from '../../../stores/store';
|
import { useAppSelector } from '$app/stores/store';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { IPage } from '../../../stores/reducers/pages/slice';
|
import { IPage } from '$app_reducers/pages/slice';
|
||||||
import { ViewLayoutPB } from '@/services/backend';
|
import { ViewLayoutPB } from '@/services/backend';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
@ -21,11 +21,10 @@ export const useNavigationPanelHooks = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onPageClick = (page: IPage) => {
|
const onPageClick = (page: IPage) => {
|
||||||
let pageTypeRoute = (() => {
|
const pageTypeRoute = (() => {
|
||||||
switch (page.pageType) {
|
switch (page.pageType) {
|
||||||
case ViewLayoutPB.Document:
|
case ViewLayoutPB.Document:
|
||||||
return 'document';
|
return 'document';
|
||||||
break;
|
|
||||||
case ViewLayoutPB.Grid:
|
case ViewLayoutPB.Grid:
|
||||||
return 'grid';
|
return 'grid';
|
||||||
case ViewLayoutPB.Board:
|
case ViewLayoutPB.Board:
|
||||||
|
@ -4,11 +4,11 @@ import { FolderItem } from './FolderItem';
|
|||||||
import { TrashButton } from './TrashButton';
|
import { TrashButton } from './TrashButton';
|
||||||
import { NewFolderButton } from './NewFolderButton';
|
import { NewFolderButton } from './NewFolderButton';
|
||||||
import { NavigationResizer } from './NavigationResizer';
|
import { NavigationResizer } from './NavigationResizer';
|
||||||
import { IFolder } from '../../../stores/reducers/folders/slice';
|
import { IFolder } from '$app_reducers/folders/slice';
|
||||||
import { IPage } from '../../../stores/reducers/pages/slice';
|
import { IPage } from '$app_reducers/pages/slice';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useAppSelector } from '../../../stores/store';
|
import { useAppSelector } from '$app/stores/store';
|
||||||
import {
|
import {
|
||||||
ANIMATION_DURATION,
|
ANIMATION_DURATION,
|
||||||
FOLDER_MARGIN,
|
FOLDER_MARGIN,
|
||||||
@ -59,7 +59,7 @@ export const NavigationPanel = ({
|
|||||||
let height = 0;
|
let height = 0;
|
||||||
for (let i = 0; i < folderIndex; i++) {
|
for (let i = 0; i < folderIndex; i++) {
|
||||||
height += INITIAL_FOLDER_HEIGHT + FOLDER_MARGIN;
|
height += INITIAL_FOLDER_HEIGHT + FOLDER_MARGIN;
|
||||||
if (foldersStore[i].showPages === true) {
|
if (foldersStore[i].showPages) {
|
||||||
height += pagesStore.filter((p) => p.folderId === foldersStore[i].id).length * PAGE_ITEM_HEIGHT;
|
height += pagesStore.filter((p) => p.folderId === foldersStore[i].id).length * PAGE_ITEM_HEIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useResizer } from '../../_shared/useResizer';
|
import { useResizer } from '../../_shared/useResizer';
|
||||||
import { useAppDispatch, useAppSelector } from '../../../stores/store';
|
import { useAppDispatch, useAppSelector } from '$app/stores/store';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { navigationWidthActions } from '../../../stores/reducers/navigation-width/slice';
|
import { navigationWidthActions } from '$app_reducers/navigation-width/slice';
|
||||||
|
|
||||||
export const NavigationResizer = ({ minWidth }: { minWidth: number }) => {
|
export const NavigationResizer = ({ minWidth }: { minWidth: number }) => {
|
||||||
const width = useAppSelector((state) => state.navigationWidth);
|
const width = useAppSelector((state) => state.navigationWidth);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useAppDispatch, useAppSelector } from '../../../stores/store';
|
import { useAppDispatch, useAppSelector } from '$app/stores/store';
|
||||||
import { foldersActions } from '../../../stores/reducers/folders/slice';
|
import { foldersActions } from '$app_reducers/folders/slice';
|
||||||
import { WorkspaceBackendService } from '../../../stores/effects/folder/workspace/workspace_bd_svc';
|
import { WorkspaceBackendService } from '$app/stores/effects/folder/workspace/workspace_bd_svc';
|
||||||
|
|
||||||
export const useNewFolder = () => {
|
export const useNewFolder = () => {
|
||||||
const appDispatch = useAppDispatch();
|
const appDispatch = useAppDispatch();
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { IPage, pagesActions } from '$app_reducers/pages/slice';
|
import { IPage, pagesActions } from '$app_reducers/pages/slice';
|
||||||
import { useAppDispatch } from '$app/stores/store';
|
import { useAppDispatch } from '$app/stores/store';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { nanoid } from 'nanoid';
|
|
||||||
import { ViewBackendService } from '$app/stores/effects/folder/view/view_bd_svc';
|
import { ViewBackendService } from '$app/stores/effects/folder/view/view_bd_svc';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { ViewPB } from '@/services/backend';
|
import { ViewPB } from '@/services/backend';
|
||||||
@ -43,10 +42,7 @@ export const usePageEvents = (page: IPage) => {
|
|||||||
|
|
||||||
const duplicatePage = async () => {
|
const duplicatePage = async () => {
|
||||||
closePopup();
|
closePopup();
|
||||||
await viewBackendService.duplicate(ViewPB.fromObject({}));
|
await viewBackendService.duplicate(ViewPB.fromObject(page));
|
||||||
appDispatch(
|
|
||||||
pagesActions.addPage({ id: nanoid(8), pageType: page.pageType, title: page.title, folderId: page.folderId })
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const closePopup = () => {
|
const closePopup = () => {
|
||||||
|
@ -3,7 +3,7 @@ import { BoardSvg } from '../../_shared/svg/BoardSvg';
|
|||||||
import { GridSvg } from '../../_shared/svg/GridSvg';
|
import { GridSvg } from '../../_shared/svg/GridSvg';
|
||||||
import { Details2Svg } from '../../_shared/svg/Details2Svg';
|
import { Details2Svg } from '../../_shared/svg/Details2Svg';
|
||||||
import { NavItemOptionsPopup } from './NavItemOptionsPopup';
|
import { NavItemOptionsPopup } from './NavItemOptionsPopup';
|
||||||
import { IPage } from '../../../stores/reducers/pages/slice';
|
import { IPage } from '$app_reducers/pages/slice';
|
||||||
import { Button } from '../../_shared/Button';
|
import { Button } from '../../_shared/Button';
|
||||||
import { usePageEvents } from './PageItem.hooks';
|
import { usePageEvents } from './PageItem.hooks';
|
||||||
import { RenamePopup } from './RenamePopup';
|
import { RenamePopup } from './RenamePopup';
|
||||||
|
@ -3,7 +3,7 @@ import { NavigationPanel } from './NavigationPanel/NavigationPanel';
|
|||||||
import { MainPanel } from './MainPanel';
|
import { MainPanel } from './MainPanel';
|
||||||
import { useNavigationPanelHooks } from './NavigationPanel/NavigationPanel.hooks';
|
import { useNavigationPanelHooks } from './NavigationPanel/NavigationPanel.hooks';
|
||||||
import { useWorkspace } from './Workspace.hooks';
|
import { useWorkspace } from './Workspace.hooks';
|
||||||
import { useAppSelector } from '../../stores/store';
|
import { useAppSelector } from '$app/stores/store';
|
||||||
|
|
||||||
export const Screen = ({ children }: { children: ReactNode }) => {
|
export const Screen = ({ children }: { children: ReactNode }) => {
|
||||||
const currentUser = useAppSelector((state) => state.currentUser);
|
const currentUser = useAppSelector((state) => state.currentUser);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { foldersActions } from '../../stores/reducers/folders/slice';
|
import { foldersActions } from '$app_reducers/folders/slice';
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/store';
|
import { useAppDispatch, useAppSelector } from '$app/stores/store';
|
||||||
import { pagesActions } from '../../stores/reducers/pages/slice';
|
import { pagesActions } from '$app_reducers/pages/slice';
|
||||||
import { workspaceActions } from '../../stores/reducers/workspace/slice';
|
import { workspaceActions } from '$app_reducers/workspace/slice';
|
||||||
import { UserBackendService } from '../../stores/effects/user/user_bd_svc';
|
import { UserBackendService } from '$app/stores/effects/user/user_bd_svc';
|
||||||
|
import { AppBackendService } from '$app/stores/effects/folder/app/app_bd_svc';
|
||||||
|
import { Log } from '$app/utils/log';
|
||||||
|
|
||||||
export const useWorkspace = () => {
|
export const useWorkspace = () => {
|
||||||
const currentUser = useAppSelector((state) => state.currentUser);
|
const currentUser = useAppSelector((state) => state.currentUser);
|
||||||
@ -22,10 +24,16 @@ export const useWorkspace = () => {
|
|||||||
const apps = workspace.views;
|
const apps = workspace.views;
|
||||||
for (const app of apps) {
|
for (const app of apps) {
|
||||||
appDispatch(foldersActions.addFolder({ id: app.id, title: app.name }));
|
appDispatch(foldersActions.addFolder({ id: app.id, title: app.name }));
|
||||||
|
const service = new AppBackendService(app.id);
|
||||||
const views = app.child_views;
|
const result = await service.getAllViews();
|
||||||
for (const view of views) {
|
if (result.ok) {
|
||||||
appDispatch(pagesActions.addPage({ folderId: app.id, id: view.id, pageType: view.layout, title: view.name }));
|
for (const view of result.val) {
|
||||||
|
appDispatch(
|
||||||
|
pagesActions.addPage({ folderId: app.id, id: view.id, pageType: view.layout, title: view.name })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.error('Failed to get views, folderId: ' + app.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e1) {
|
} catch (e1) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useAppSelector } from '../../stores/store';
|
import { useAppSelector } from '$app/stores/store';
|
||||||
|
|
||||||
export const WorkspaceUser = () => {
|
export const WorkspaceUser = () => {
|
||||||
const currentUser = useAppSelector((state) => state.currentUser);
|
const currentUser = useAppSelector((state) => state.currentUser);
|
||||||
|
@ -73,6 +73,22 @@ export class TypeOptionController {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
hideField = async () => {
|
||||||
|
if (this.fieldBackendSvc) {
|
||||||
|
void this.fieldBackendSvc.updateField({ visibility: false });
|
||||||
|
} else {
|
||||||
|
throw Error('Unexpected empty field backend service');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
showField = async () => {
|
||||||
|
if (this.fieldBackendSvc) {
|
||||||
|
void this.fieldBackendSvc.updateField({ visibility: true });
|
||||||
|
} else {
|
||||||
|
throw Error('Unexpected empty field backend service');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
saveTypeOption = async (data: Uint8Array) => {
|
saveTypeOption = async (data: Uint8Array) => {
|
||||||
if (this.typeOptionData.some) {
|
if (this.typeOptionData.some) {
|
||||||
this.typeOptionData.val.type_option_data = data;
|
this.typeOptionData.val.type_option_data = data;
|
||||||
|
@ -1,40 +1,34 @@
|
|||||||
import { Ok, Result } from "ts-results";
|
import { FolderNotification } from '@/services/backend';
|
||||||
import { FlowyError, FolderNotification, RepeatedViewPB } from "@/services/backend";
|
import { ChangeNotifier } from '$app/utils/change_notifier';
|
||||||
import { ChangeNotifier } from "$app/utils/change_notifier";
|
import { FolderNotificationObserver } from '../notifications/observer';
|
||||||
import { FolderNotificationObserver } from "../notifications/observer";
|
|
||||||
|
|
||||||
export type AppUpdateNotifyCallback = (value: Result<RepeatedViewPB, FlowyError>) => void;
|
|
||||||
|
|
||||||
export class AppObserver {
|
export class AppObserver {
|
||||||
_appNotifier = new ChangeNotifier<Result<RepeatedViewPB, FlowyError>>();
|
_viewsNotifier = new ChangeNotifier<void>();
|
||||||
_listener?: FolderNotificationObserver;
|
_listener?: FolderNotificationObserver;
|
||||||
|
|
||||||
constructor(public readonly appId: string) {
|
constructor(public readonly appId: string) {}
|
||||||
}
|
|
||||||
|
|
||||||
subscribe = async (callbacks: { onAppChanged: AppUpdateNotifyCallback }) => {
|
subscribe = async (callbacks: { onViewsChanged: () => void }) => {
|
||||||
this._appNotifier?.observer?.subscribe(callbacks.onAppChanged);
|
this._viewsNotifier?.observer?.subscribe(callbacks.onViewsChanged);
|
||||||
this._listener = new FolderNotificationObserver({
|
this._listener = new FolderNotificationObserver({
|
||||||
viewId: this.appId,
|
viewId: this.appId,
|
||||||
parserHandler: (notification, result) => {
|
parserHandler: (notification, result) => {
|
||||||
switch (notification) {
|
switch (notification) {
|
||||||
case FolderNotification.DidUpdateWorkspaceViews:
|
case FolderNotification.DidUpdateChildViews:
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
this._appNotifier?.notify(Ok(RepeatedViewPB.deserializeBinary(result.val)));
|
this._viewsNotifier?.notify();
|
||||||
} else {
|
|
||||||
this._appNotifier?.notify(result);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
await this._listener.start();
|
await this._listener.start();
|
||||||
};
|
};
|
||||||
|
|
||||||
unsubscribe = async () => {
|
unsubscribe = async () => {
|
||||||
this._appNotifier.unsubscribe();
|
this._viewsNotifier.unsubscribe();
|
||||||
await this._listener?.stop();
|
await this._listener?.stop();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ export interface INumberType {
|
|||||||
export interface IDatabaseField {
|
export interface IDatabaseField {
|
||||||
fieldId: string;
|
fieldId: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
visible: boolean;
|
||||||
fieldType: FieldType;
|
fieldType: FieldType;
|
||||||
fieldOptions?: ISelectOptionType | IDateType | INumberType;
|
fieldOptions?: ISelectOptionType | IDateType | INumberType;
|
||||||
}
|
}
|
||||||
@ -112,11 +113,10 @@ export const databaseSlice = createSlice({
|
|||||||
});
|
});
|
||||||
},*/
|
},*/
|
||||||
|
|
||||||
/*updateField: (state, action: PayloadAction<{ field: IDatabaseField }>) => {
|
updateField: (state, action: PayloadAction<{ field: IDatabaseField }>) => {
|
||||||
const { field } = action.payload;
|
const { field } = action.payload;
|
||||||
|
|
||||||
state.fields[field.fieldId] = field;
|
state.fields[field.fieldId] = field;
|
||||||
},*/
|
},
|
||||||
|
|
||||||
/*addFieldSelectOption: (state, action: PayloadAction<{ fieldId: string; option: ISelectOption }>) => {
|
/*addFieldSelectOption: (state, action: PayloadAction<{ fieldId: string; option: ISelectOption }>) => {
|
||||||
const { fieldId, option } = action.payload;
|
const { fieldId, option } = action.payload;
|
||||||
|
@ -14,8 +14,8 @@ export const pagesSlice = createSlice({
|
|||||||
name: 'pages',
|
name: 'pages',
|
||||||
initialState: initialState,
|
initialState: initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
didReceivePages(state, action: PayloadAction<IPage[]>) {
|
didReceivePages(state, action: PayloadAction<{ pages: IPage[]; folderId: string }>) {
|
||||||
return action.payload;
|
return state.filter((page) => page.folderId !== action.payload.folderId).concat(action.payload.pages);
|
||||||
},
|
},
|
||||||
addPage(state, action: PayloadAction<IPage>) {
|
addPage(state, action: PayloadAction<IPage>) {
|
||||||
state.push(action.payload);
|
state.push(action.payload);
|
||||||
|
Loading…
Reference in New Issue
Block a user