mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 03:37:51 +01:00
Use tanstackdb for record updates
This commit is contained in:
@@ -46,11 +46,8 @@ import { NodeInteractionOpenedMutationHandler } from './nodes/node-interaction-o
|
||||
import { NodeInteractionSeenMutationHandler } from './nodes/node-interaction-seen';
|
||||
import { NodeReactionCreateMutationHandler } from './nodes/node-reaction-create';
|
||||
import { NodeReactionDeleteMutationHandler } from './nodes/node-reaction-delete';
|
||||
import { NodeUpdateMutationHandler } from './nodes/node-update';
|
||||
import { PageUpdateMutationHandler } from './pages/page-update';
|
||||
import { RecordAvatarUpdateMutationHandler } from './records/record-avatar-update';
|
||||
import { RecordFieldValueDeleteMutationHandler } from './records/record-field-value-delete';
|
||||
import { RecordFieldValueSetMutationHandler } from './records/record-field-value-set';
|
||||
import { RecordNameUpdateMutationHandler } from './records/record-name-update';
|
||||
import { ServerCreateMutationHandler } from './servers/server-create';
|
||||
import { ServerDeleteMutationHandler } from './servers/server-delete';
|
||||
import { ServerSyncMutationHandler } from './servers/server-sync';
|
||||
@@ -78,6 +75,7 @@ export const buildMutationHandlerMap = (
|
||||
'view.create': new ViewCreateMutationHandler(app),
|
||||
'node.delete': new NodeDeleteMutationHandler(app),
|
||||
'node.create': new NodeCreateMutationHandler(app),
|
||||
'node.update': new NodeUpdateMutationHandler(app),
|
||||
'chat.create': new ChatCreateMutationHandler(app),
|
||||
'database.create': new DatabaseCreateMutationHandler(app),
|
||||
'database.name.field.update': new DatabaseNameFieldUpdateMutationHandler(
|
||||
@@ -94,10 +92,6 @@ export const buildMutationHandlerMap = (
|
||||
'node.interaction.seen': new NodeInteractionSeenMutationHandler(app),
|
||||
'node.reaction.create': new NodeReactionCreateMutationHandler(app),
|
||||
'node.reaction.delete': new NodeReactionDeleteMutationHandler(app),
|
||||
'record.avatar.update': new RecordAvatarUpdateMutationHandler(app),
|
||||
'record.name.update': new RecordNameUpdateMutationHandler(app),
|
||||
'record.field.value.delete': new RecordFieldValueDeleteMutationHandler(app),
|
||||
'record.field.value.set': new RecordFieldValueSetMutationHandler(app),
|
||||
'select.option.create': new SelectOptionCreateMutationHandler(app),
|
||||
'select.option.delete': new SelectOptionDeleteMutationHandler(app),
|
||||
'select.option.update': new SelectOptionUpdateMutationHandler(app),
|
||||
|
||||
24
packages/client/src/handlers/mutations/nodes/node-update.ts
Normal file
24
packages/client/src/handlers/mutations/nodes/node-update.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { WorkspaceMutationHandlerBase } from '@colanode/client/handlers/mutations/workspace-mutation-handler-base';
|
||||
import { MutationHandler } from '@colanode/client/lib/types';
|
||||
import {
|
||||
NodeUpdateMutationInput,
|
||||
NodeUpdateMutationOutput,
|
||||
} from '@colanode/client/mutations/nodes/node-update';
|
||||
|
||||
export class NodeUpdateMutationHandler
|
||||
extends WorkspaceMutationHandlerBase
|
||||
implements MutationHandler<NodeUpdateMutationInput>
|
||||
{
|
||||
async handleMutation(
|
||||
input: NodeUpdateMutationInput
|
||||
): Promise<NodeUpdateMutationOutput> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
await workspace.nodes.updateNode(input.nodeId, () => {
|
||||
return input.attributes;
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import { WorkspaceMutationHandlerBase } from '@colanode/client/handlers/mutations/workspace-mutation-handler-base';
|
||||
import { MutationHandler } from '@colanode/client/lib/types';
|
||||
import { MutationError, MutationErrorCode } from '@colanode/client/mutations';
|
||||
import {
|
||||
RecordAvatarUpdateMutationInput,
|
||||
RecordAvatarUpdateMutationOutput,
|
||||
} from '@colanode/client/mutations/records/record-avatar-update';
|
||||
import { RecordAttributes } from '@colanode/core';
|
||||
|
||||
export class RecordAvatarUpdateMutationHandler
|
||||
extends WorkspaceMutationHandlerBase
|
||||
implements MutationHandler<RecordAvatarUpdateMutationInput>
|
||||
{
|
||||
async handleMutation(
|
||||
input: RecordAvatarUpdateMutationInput
|
||||
): Promise<RecordAvatarUpdateMutationOutput> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const result = await workspace.nodes.updateNode<RecordAttributes>(
|
||||
input.recordId,
|
||||
(attributes) => {
|
||||
attributes.avatar = input.avatar;
|
||||
return attributes;
|
||||
}
|
||||
);
|
||||
|
||||
if (result === 'unauthorized') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateForbidden,
|
||||
"You don't have permission to update this record."
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { WorkspaceMutationHandlerBase } from '@colanode/client/handlers/mutations/workspace-mutation-handler-base';
|
||||
import { MutationHandler } from '@colanode/client/lib/types';
|
||||
import { MutationError, MutationErrorCode } from '@colanode/client/mutations';
|
||||
import {
|
||||
RecordFieldValueDeleteMutationInput,
|
||||
RecordFieldValueDeleteMutationOutput,
|
||||
} from '@colanode/client/mutations/records/record-field-value-delete';
|
||||
import { RecordAttributes } from '@colanode/core';
|
||||
|
||||
export class RecordFieldValueDeleteMutationHandler
|
||||
extends WorkspaceMutationHandlerBase
|
||||
implements MutationHandler<RecordFieldValueDeleteMutationInput>
|
||||
{
|
||||
async handleMutation(
|
||||
input: RecordFieldValueDeleteMutationInput
|
||||
): Promise<RecordFieldValueDeleteMutationOutput> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const result = await workspace.nodes.updateNode<RecordAttributes>(
|
||||
input.recordId,
|
||||
(attributes) => {
|
||||
delete attributes.fields[input.fieldId];
|
||||
return attributes;
|
||||
}
|
||||
);
|
||||
|
||||
if (result === 'unauthorized') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateForbidden,
|
||||
"You don't have permission to delete this field value."
|
||||
);
|
||||
}
|
||||
|
||||
if (result !== 'success') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateFailed,
|
||||
'Something went wrong while deleting the field value. Please try again later.'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { WorkspaceMutationHandlerBase } from '@colanode/client/handlers/mutations/workspace-mutation-handler-base';
|
||||
import { MutationHandler } from '@colanode/client/lib/types';
|
||||
import { MutationError, MutationErrorCode } from '@colanode/client/mutations';
|
||||
import {
|
||||
RecordFieldValueSetMutationInput,
|
||||
RecordFieldValueSetMutationOutput,
|
||||
} from '@colanode/client/mutations/records/record-field-value-set';
|
||||
import { RecordAttributes } from '@colanode/core';
|
||||
|
||||
export class RecordFieldValueSetMutationHandler
|
||||
extends WorkspaceMutationHandlerBase
|
||||
implements MutationHandler<RecordFieldValueSetMutationInput>
|
||||
{
|
||||
async handleMutation(
|
||||
input: RecordFieldValueSetMutationInput
|
||||
): Promise<RecordFieldValueSetMutationOutput> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const result = await workspace.nodes.updateNode<RecordAttributes>(
|
||||
input.recordId,
|
||||
(attributes) => {
|
||||
attributes.fields[input.fieldId] = input.value;
|
||||
return attributes;
|
||||
}
|
||||
);
|
||||
|
||||
if (result === 'unauthorized') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateForbidden,
|
||||
"You don't have permission to set this field value."
|
||||
);
|
||||
}
|
||||
|
||||
if (result !== 'success') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateFailed,
|
||||
'Something went wrong while setting the field value.'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { WorkspaceMutationHandlerBase } from '@colanode/client/handlers/mutations/workspace-mutation-handler-base';
|
||||
import { MutationHandler } from '@colanode/client/lib/types';
|
||||
import { MutationError, MutationErrorCode } from '@colanode/client/mutations';
|
||||
import {
|
||||
RecordNameUpdateMutationInput,
|
||||
RecordNameUpdateMutationOutput,
|
||||
} from '@colanode/client/mutations/records/record-name-update';
|
||||
import { RecordAttributes } from '@colanode/core';
|
||||
|
||||
export class RecordNameUpdateMutationHandler
|
||||
extends WorkspaceMutationHandlerBase
|
||||
implements MutationHandler<RecordNameUpdateMutationInput>
|
||||
{
|
||||
async handleMutation(
|
||||
input: RecordNameUpdateMutationInput
|
||||
): Promise<RecordNameUpdateMutationOutput> {
|
||||
const workspace = this.getWorkspace(input.userId);
|
||||
|
||||
const result = await workspace.nodes.updateNode<RecordAttributes>(
|
||||
input.recordId,
|
||||
(attributes) => {
|
||||
attributes.name = input.name;
|
||||
return attributes;
|
||||
}
|
||||
);
|
||||
|
||||
if (result === 'unauthorized') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateForbidden,
|
||||
"You don't have permission to update this record."
|
||||
);
|
||||
}
|
||||
|
||||
if (result !== 'success') {
|
||||
throw new MutationError(
|
||||
MutationErrorCode.RecordUpdateFailed,
|
||||
'Something went wrong while updating the record name. Please try again later.'
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,6 @@ export * from './nodes/node-interaction-seen';
|
||||
export * from './nodes/node-reaction-create';
|
||||
export * from './nodes/node-reaction-delete';
|
||||
export * from './pages/page-update';
|
||||
export * from './records/record-avatar-update';
|
||||
export * from './records/record-field-value-delete';
|
||||
export * from './records/record-field-value-set';
|
||||
export * from './records/record-name-update';
|
||||
export * from './servers/server-create';
|
||||
export * from './servers/server-delete';
|
||||
export * from './spaces/space-update';
|
||||
@@ -58,6 +54,7 @@ export * from './servers/server-sync';
|
||||
export * from './apps/tab-delete';
|
||||
export * from './nodes/node-delete';
|
||||
export * from './nodes/node-create';
|
||||
export * from './nodes/node-update';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
export interface MutationMap {}
|
||||
|
||||
21
packages/client/src/mutations/nodes/node-update.ts
Normal file
21
packages/client/src/mutations/nodes/node-update.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { NodeAttributes } from '@colanode/core';
|
||||
|
||||
export type NodeUpdateMutationInput = {
|
||||
type: 'node.update';
|
||||
userId: string;
|
||||
nodeId: string;
|
||||
attributes: NodeAttributes;
|
||||
};
|
||||
|
||||
export type NodeUpdateMutationOutput = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/mutations' {
|
||||
interface MutationMap {
|
||||
'node.update': {
|
||||
input: NodeUpdateMutationInput;
|
||||
output: NodeUpdateMutationOutput;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
export type RecordAvatarUpdateMutationInput = {
|
||||
type: 'record.avatar.update';
|
||||
userId: string;
|
||||
recordId: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
export type RecordAvatarUpdateMutationOutput = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/mutations' {
|
||||
interface MutationMap {
|
||||
'record.avatar.update': {
|
||||
input: RecordAvatarUpdateMutationInput;
|
||||
output: RecordAvatarUpdateMutationOutput;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
export type RecordFieldValueDeleteMutationInput = {
|
||||
type: 'record.field.value.delete';
|
||||
userId: string;
|
||||
recordId: string;
|
||||
fieldId: string;
|
||||
};
|
||||
|
||||
export type RecordFieldValueDeleteMutationOutput = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/mutations' {
|
||||
interface MutationMap {
|
||||
'record.field.value.delete': {
|
||||
input: RecordFieldValueDeleteMutationInput;
|
||||
output: RecordFieldValueDeleteMutationOutput;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { FieldValue } from '@colanode/core';
|
||||
|
||||
export type RecordFieldValueSetMutationInput = {
|
||||
type: 'record.field.value.set';
|
||||
userId: string;
|
||||
recordId: string;
|
||||
fieldId: string;
|
||||
value: FieldValue;
|
||||
};
|
||||
|
||||
export type RecordFieldValueSetMutationOutput = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/mutations' {
|
||||
interface MutationMap {
|
||||
'record.field.value.set': {
|
||||
input: RecordFieldValueSetMutationInput;
|
||||
output: RecordFieldValueSetMutationOutput;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
export type RecordNameUpdateMutationInput = {
|
||||
type: 'record.name.update';
|
||||
userId: string;
|
||||
recordId: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type RecordNameUpdateMutationOutput = {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
declare module '@colanode/client/mutations' {
|
||||
interface MutationMap {
|
||||
'record.name.update': {
|
||||
input: RecordNameUpdateMutationInput;
|
||||
output: RecordNameUpdateMutationOutput;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createCollection } from '@tanstack/react-db';
|
||||
// import { cloneDeep } from 'lodash-es';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import { mapNodeAttributes } from '@colanode/client/lib';
|
||||
import { LocalNode } from '@colanode/client/types';
|
||||
@@ -73,17 +73,18 @@ export const createNodesCollection = (userId: string) => {
|
||||
});
|
||||
}
|
||||
},
|
||||
// onUpdate: async ({ transaction }) => {
|
||||
// for (const mutation of transaction.mutations) {
|
||||
// const attributes = cloneDeep(mutation.modified.attributes);
|
||||
// await window.colanode.executeMutation({
|
||||
// type: 'node.update',
|
||||
// userId,
|
||||
// nodeId: mutation.key,
|
||||
// attributes,
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
onUpdate: async ({ transaction }) => {
|
||||
for (const mutation of transaction.mutations) {
|
||||
const node = cloneDeep(mutation.modified);
|
||||
const attributes = mapNodeAttributes(node);
|
||||
await window.colanode.executeMutation({
|
||||
type: 'node.update',
|
||||
userId,
|
||||
nodeId: mutation.key,
|
||||
attributes,
|
||||
});
|
||||
}
|
||||
},
|
||||
onDelete: async ({ transaction }) => {
|
||||
for (const mutation of transaction.mutations) {
|
||||
await window.colanode.executeMutation({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { eq, useLiveQuery as useLiveQueryTanstack } from '@tanstack/react-db';
|
||||
import { CircleAlert, CircleDashed } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import {
|
||||
CollaboratorFieldAttributes,
|
||||
@@ -81,17 +80,16 @@ export const BoardViewColumnsCollaborator = ({
|
||||
),
|
||||
canDrag: (record) => record.canEdit,
|
||||
onDragEnd: async (record, value) => {
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!value) {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
} else {
|
||||
if (value.type !== 'string_array') {
|
||||
return;
|
||||
@@ -114,17 +112,13 @@ export const BoardViewColumnsCollaborator = ({
|
||||
};
|
||||
}
|
||||
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value: newValue,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
draft.fields[field.id] = newValue;
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
@@ -150,29 +144,24 @@ export const BoardViewColumnsCollaborator = ({
|
||||
),
|
||||
canDrag: () => true,
|
||||
onDragEnd: async (record, value) => {
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!value) {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
} else {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
draft.fields[field.id] = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CircleDashed } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import {
|
||||
DatabaseViewFilterAttributes,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
MultiSelectFieldAttributes,
|
||||
SelectOptionAttributes,
|
||||
} from '@colanode/core';
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { BoardViewColumn } from '@colanode/ui/components/databases/boards/board-view-column';
|
||||
import { SelectOptionBadge } from '@colanode/ui/components/databases/fields/select-option-badge';
|
||||
import { BoardViewContext } from '@colanode/ui/contexts/board-view';
|
||||
@@ -90,17 +90,16 @@ export const BoardViewColumnsMultiSelect = ({
|
||||
),
|
||||
canDrag: (record) => record.canEdit,
|
||||
onDragEnd: async (record, value) => {
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!value) {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
} else {
|
||||
if (value.type !== 'string_array') {
|
||||
return;
|
||||
@@ -122,17 +121,13 @@ export const BoardViewColumnsMultiSelect = ({
|
||||
};
|
||||
}
|
||||
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value: newValue,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
draft.fields[field.id] = newValue;
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
@@ -159,29 +154,24 @@ export const BoardViewColumnsMultiSelect = ({
|
||||
dragOverClass: noValueDraggingClass,
|
||||
canDrag: () => true,
|
||||
onDragEnd: async (record, value) => {
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!value) {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
} else {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
draft.fields[field.id] = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { CircleDashed } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import {
|
||||
DatabaseViewFilterAttributes,
|
||||
SelectFieldAttributes,
|
||||
SelectOptionAttributes,
|
||||
} from '@colanode/core';
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { BoardViewColumn } from '@colanode/ui/components/databases/boards/board-view-column';
|
||||
import { SelectOptionBadge } from '@colanode/ui/components/databases/fields/select-option-badge';
|
||||
import { BoardViewContext } from '@colanode/ui/contexts/board-view';
|
||||
@@ -89,29 +89,24 @@ export const BoardViewColumnsSelect = ({
|
||||
),
|
||||
canDrag: (record) => record.canEdit,
|
||||
onDragEnd: async (record, value) => {
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!value) {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
} else {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
draft.fields[field.id] = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
@@ -138,29 +133,24 @@ export const BoardViewColumnsSelect = ({
|
||||
dragOverClass: noValueDraggingClass,
|
||||
canDrag: () => true,
|
||||
onDragEnd: async (record, value) => {
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!value) {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
} else {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
}
|
||||
draft.fields[field.id] = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import isHotkey from 'is-hotkey';
|
||||
import { SquareArrowOutUpRight } from 'lucide-react';
|
||||
import React, { Fragment } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { RecordNode } from '@colanode/core';
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { Link } from '@colanode/ui/components/ui/link';
|
||||
import { Spinner } from '@colanode/ui/components/ui/spinner';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
||||
|
||||
interface NameEditorProps {
|
||||
initialValue: string;
|
||||
@@ -61,27 +59,21 @@ export const TableViewNameCell = ({ record }: TableViewNameCellProps) => {
|
||||
const workspace = useWorkspace();
|
||||
const [isEditing, setIsEditing] = React.useState(false);
|
||||
|
||||
const { mutate, isPending } = useMutation();
|
||||
const canEdit = true;
|
||||
const hasName = record.name && record.name.length > 0;
|
||||
|
||||
const handleSave = (newName: string) => {
|
||||
if (newName === record.name) return;
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'record.name.update',
|
||||
name: newName,
|
||||
recordId: record.id,
|
||||
userId: workspace.userId,
|
||||
},
|
||||
onSuccess() {
|
||||
setIsEditing(false);
|
||||
},
|
||||
onError(error) {
|
||||
toast.error(error.message);
|
||||
},
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
draft.name = newName;
|
||||
});
|
||||
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -112,11 +104,6 @@ export const TableViewNameCell = ({ record }: TableViewNameCellProps) => {
|
||||
>
|
||||
<SquareArrowOutUpRight className="mr-1 size-4" /> <p>Open</p>
|
||||
</Link>
|
||||
{isPending && (
|
||||
<span className="absolute right-2 text-muted-foreground">
|
||||
<Spinner size="small" />
|
||||
</span>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||
import { AvatarPopover } from '@colanode/ui/components/avatars/avatar-popover';
|
||||
import { Button } from '@colanode/ui/components/ui/button';
|
||||
import { useRecord } from '@colanode/ui/contexts/record';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
||||
|
||||
export const RecordAvatar = () => {
|
||||
const workspace = useWorkspace();
|
||||
const record = useRecord();
|
||||
|
||||
const { mutate, isPending } = useMutation();
|
||||
|
||||
if (!record.canEdit) {
|
||||
return (
|
||||
<Button type="button" variant="outline" size="icon">
|
||||
@@ -29,19 +25,14 @@ export const RecordAvatar = () => {
|
||||
return (
|
||||
<AvatarPopover
|
||||
onPick={(avatar) => {
|
||||
if (isPending) return;
|
||||
if (avatar === record.avatar) return;
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'record.avatar.update',
|
||||
recordId: record.id,
|
||||
avatar,
|
||||
userId: workspace.userId,
|
||||
},
|
||||
onError(error) {
|
||||
toast.error(error.message);
|
||||
},
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
draft.avatar = avatar;
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { SmartTextInput } from '@colanode/ui/components/ui/smart-text-input';
|
||||
import { useRecord } from '@colanode/ui/contexts/record';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
||||
|
||||
export const RecordName = () => {
|
||||
const workspace = useWorkspace();
|
||||
const record = useRecord();
|
||||
const { mutate, isPending } = useMutation();
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -29,24 +27,17 @@ export const RecordName = () => {
|
||||
readOnly={!record.canEdit}
|
||||
ref={inputRef}
|
||||
onChange={(value) => {
|
||||
if (isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === record.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
mutate({
|
||||
input: {
|
||||
type: 'record.name.update',
|
||||
recordId: record.id,
|
||||
name: value,
|
||||
userId: workspace.userId,
|
||||
},
|
||||
onError(error) {
|
||||
toast.error(error.message);
|
||||
},
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
draft.name = value;
|
||||
});
|
||||
}}
|
||||
className="font-heading border-b border-none pl-1 text-4xl font-bold shadow-none focus-visible:ring-0"
|
||||
|
||||
@@ -2,6 +2,7 @@ import { toast } from 'sonner';
|
||||
|
||||
import { LocalRecordNode } from '@colanode/client/types';
|
||||
import { NodeRole, hasNodeRole } from '@colanode/core';
|
||||
import { collections } from '@colanode/ui/collections';
|
||||
import { RecordContext } from '@colanode/ui/contexts/record';
|
||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||
|
||||
@@ -34,29 +35,35 @@ export const RecordProvider = ({
|
||||
localRevision: record.localRevision,
|
||||
canEdit,
|
||||
updateFieldValue: async (field, value) => {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.set',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
value,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!nodes.has(record.id)) {
|
||||
toast.error('Record not found');
|
||||
return;
|
||||
}
|
||||
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
draft.fields[field.id] = value;
|
||||
});
|
||||
},
|
||||
removeFieldValue: async (field) => {
|
||||
const result = await window.colanode.executeMutation({
|
||||
type: 'record.field.value.delete',
|
||||
recordId: record.id,
|
||||
fieldId: field.id,
|
||||
userId: workspace.userId,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error.message);
|
||||
const nodes = collections.workspace(workspace.userId).nodes;
|
||||
if (!nodes.has(record.id)) {
|
||||
toast.error('Record not found');
|
||||
return;
|
||||
}
|
||||
|
||||
nodes.update(record.id, (draft) => {
|
||||
if (draft.type !== 'record') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||
draft.fields = rest;
|
||||
});
|
||||
},
|
||||
getBooleanValue: (field) => {
|
||||
const fieldValue = record.fields[field.id];
|
||||
|
||||
Reference in New Issue
Block a user