mirror of
https://github.com/colanode/colanode.git
synced 2025-12-16 11:47:47 +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 { NodeInteractionSeenMutationHandler } from './nodes/node-interaction-seen';
|
||||||
import { NodeReactionCreateMutationHandler } from './nodes/node-reaction-create';
|
import { NodeReactionCreateMutationHandler } from './nodes/node-reaction-create';
|
||||||
import { NodeReactionDeleteMutationHandler } from './nodes/node-reaction-delete';
|
import { NodeReactionDeleteMutationHandler } from './nodes/node-reaction-delete';
|
||||||
|
import { NodeUpdateMutationHandler } from './nodes/node-update';
|
||||||
import { PageUpdateMutationHandler } from './pages/page-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 { ServerCreateMutationHandler } from './servers/server-create';
|
||||||
import { ServerDeleteMutationHandler } from './servers/server-delete';
|
import { ServerDeleteMutationHandler } from './servers/server-delete';
|
||||||
import { ServerSyncMutationHandler } from './servers/server-sync';
|
import { ServerSyncMutationHandler } from './servers/server-sync';
|
||||||
@@ -78,6 +75,7 @@ export const buildMutationHandlerMap = (
|
|||||||
'view.create': new ViewCreateMutationHandler(app),
|
'view.create': new ViewCreateMutationHandler(app),
|
||||||
'node.delete': new NodeDeleteMutationHandler(app),
|
'node.delete': new NodeDeleteMutationHandler(app),
|
||||||
'node.create': new NodeCreateMutationHandler(app),
|
'node.create': new NodeCreateMutationHandler(app),
|
||||||
|
'node.update': new NodeUpdateMutationHandler(app),
|
||||||
'chat.create': new ChatCreateMutationHandler(app),
|
'chat.create': new ChatCreateMutationHandler(app),
|
||||||
'database.create': new DatabaseCreateMutationHandler(app),
|
'database.create': new DatabaseCreateMutationHandler(app),
|
||||||
'database.name.field.update': new DatabaseNameFieldUpdateMutationHandler(
|
'database.name.field.update': new DatabaseNameFieldUpdateMutationHandler(
|
||||||
@@ -94,10 +92,6 @@ export const buildMutationHandlerMap = (
|
|||||||
'node.interaction.seen': new NodeInteractionSeenMutationHandler(app),
|
'node.interaction.seen': new NodeInteractionSeenMutationHandler(app),
|
||||||
'node.reaction.create': new NodeReactionCreateMutationHandler(app),
|
'node.reaction.create': new NodeReactionCreateMutationHandler(app),
|
||||||
'node.reaction.delete': new NodeReactionDeleteMutationHandler(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.create': new SelectOptionCreateMutationHandler(app),
|
||||||
'select.option.delete': new SelectOptionDeleteMutationHandler(app),
|
'select.option.delete': new SelectOptionDeleteMutationHandler(app),
|
||||||
'select.option.update': new SelectOptionUpdateMutationHandler(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-create';
|
||||||
export * from './nodes/node-reaction-delete';
|
export * from './nodes/node-reaction-delete';
|
||||||
export * from './pages/page-update';
|
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-create';
|
||||||
export * from './servers/server-delete';
|
export * from './servers/server-delete';
|
||||||
export * from './spaces/space-update';
|
export * from './spaces/space-update';
|
||||||
@@ -58,6 +54,7 @@ export * from './servers/server-sync';
|
|||||||
export * from './apps/tab-delete';
|
export * from './apps/tab-delete';
|
||||||
export * from './nodes/node-delete';
|
export * from './nodes/node-delete';
|
||||||
export * from './nodes/node-create';
|
export * from './nodes/node-create';
|
||||||
|
export * from './nodes/node-update';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||||
export interface MutationMap {}
|
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 { createCollection } from '@tanstack/react-db';
|
||||||
// import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import { mapNodeAttributes } from '@colanode/client/lib';
|
import { mapNodeAttributes } from '@colanode/client/lib';
|
||||||
import { LocalNode } from '@colanode/client/types';
|
import { LocalNode } from '@colanode/client/types';
|
||||||
@@ -73,17 +73,18 @@ export const createNodesCollection = (userId: string) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// onUpdate: async ({ transaction }) => {
|
onUpdate: async ({ transaction }) => {
|
||||||
// for (const mutation of transaction.mutations) {
|
for (const mutation of transaction.mutations) {
|
||||||
// const attributes = cloneDeep(mutation.modified.attributes);
|
const node = cloneDeep(mutation.modified);
|
||||||
// await window.colanode.executeMutation({
|
const attributes = mapNodeAttributes(node);
|
||||||
// type: 'node.update',
|
await window.colanode.executeMutation({
|
||||||
// userId,
|
type: 'node.update',
|
||||||
// nodeId: mutation.key,
|
userId,
|
||||||
// attributes,
|
nodeId: mutation.key,
|
||||||
// });
|
attributes,
|
||||||
// }
|
});
|
||||||
// },
|
}
|
||||||
|
},
|
||||||
onDelete: async ({ transaction }) => {
|
onDelete: async ({ transaction }) => {
|
||||||
for (const mutation of transaction.mutations) {
|
for (const mutation of transaction.mutations) {
|
||||||
await window.colanode.executeMutation({
|
await window.colanode.executeMutation({
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { eq, useLiveQuery as useLiveQueryTanstack } from '@tanstack/react-db';
|
import { eq, useLiveQuery as useLiveQueryTanstack } from '@tanstack/react-db';
|
||||||
import { CircleAlert, CircleDashed } from 'lucide-react';
|
import { CircleAlert, CircleDashed } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CollaboratorFieldAttributes,
|
CollaboratorFieldAttributes,
|
||||||
@@ -81,17 +80,16 @@ export const BoardViewColumnsCollaborator = ({
|
|||||||
),
|
),
|
||||||
canDrag: (record) => record.canEdit,
|
canDrag: (record) => record.canEdit,
|
||||||
onDragEnd: async (record, value) => {
|
onDragEnd: async (record, value) => {
|
||||||
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.delete',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
toast.error(result.error.message);
|
draft.fields = rest;
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
if (value.type !== 'string_array') {
|
if (value.type !== 'string_array') {
|
||||||
return;
|
return;
|
||||||
@@ -114,17 +112,13 @@ export const BoardViewColumnsCollaborator = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.set',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
value: newValue,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
draft.fields[field.id] = newValue;
|
||||||
toast.error(result.error.message);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@@ -150,29 +144,24 @@ export const BoardViewColumnsCollaborator = ({
|
|||||||
),
|
),
|
||||||
canDrag: () => true,
|
canDrag: () => true,
|
||||||
onDragEnd: async (record, value) => {
|
onDragEnd: async (record, value) => {
|
||||||
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.delete',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
toast.error(result.error.message);
|
draft.fields = rest;
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.set',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
value,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
draft.fields[field.id] = value;
|
||||||
toast.error(result.error.message);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { CircleDashed } from 'lucide-react';
|
import { CircleDashed } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DatabaseViewFilterAttributes,
|
DatabaseViewFilterAttributes,
|
||||||
@@ -7,6 +6,7 @@ import {
|
|||||||
MultiSelectFieldAttributes,
|
MultiSelectFieldAttributes,
|
||||||
SelectOptionAttributes,
|
SelectOptionAttributes,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
import { collections } from '@colanode/ui/collections';
|
||||||
import { BoardViewColumn } from '@colanode/ui/components/databases/boards/board-view-column';
|
import { BoardViewColumn } from '@colanode/ui/components/databases/boards/board-view-column';
|
||||||
import { SelectOptionBadge } from '@colanode/ui/components/databases/fields/select-option-badge';
|
import { SelectOptionBadge } from '@colanode/ui/components/databases/fields/select-option-badge';
|
||||||
import { BoardViewContext } from '@colanode/ui/contexts/board-view';
|
import { BoardViewContext } from '@colanode/ui/contexts/board-view';
|
||||||
@@ -90,17 +90,16 @@ export const BoardViewColumnsMultiSelect = ({
|
|||||||
),
|
),
|
||||||
canDrag: (record) => record.canEdit,
|
canDrag: (record) => record.canEdit,
|
||||||
onDragEnd: async (record, value) => {
|
onDragEnd: async (record, value) => {
|
||||||
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.delete',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
toast.error(result.error.message);
|
draft.fields = rest;
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
if (value.type !== 'string_array') {
|
if (value.type !== 'string_array') {
|
||||||
return;
|
return;
|
||||||
@@ -122,17 +121,13 @@ export const BoardViewColumnsMultiSelect = ({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.set',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
value: newValue,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
draft.fields[field.id] = newValue;
|
||||||
toast.error(result.error.message);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@@ -159,29 +154,24 @@ export const BoardViewColumnsMultiSelect = ({
|
|||||||
dragOverClass: noValueDraggingClass,
|
dragOverClass: noValueDraggingClass,
|
||||||
canDrag: () => true,
|
canDrag: () => true,
|
||||||
onDragEnd: async (record, value) => {
|
onDragEnd: async (record, value) => {
|
||||||
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.delete',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
toast.error(result.error.message);
|
draft.fields = rest;
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.set',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
value,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
draft.fields[field.id] = value;
|
||||||
toast.error(result.error.message);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { CircleDashed } from 'lucide-react';
|
import { CircleDashed } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DatabaseViewFilterAttributes,
|
DatabaseViewFilterAttributes,
|
||||||
SelectFieldAttributes,
|
SelectFieldAttributes,
|
||||||
SelectOptionAttributes,
|
SelectOptionAttributes,
|
||||||
} from '@colanode/core';
|
} from '@colanode/core';
|
||||||
|
import { collections } from '@colanode/ui/collections';
|
||||||
import { BoardViewColumn } from '@colanode/ui/components/databases/boards/board-view-column';
|
import { BoardViewColumn } from '@colanode/ui/components/databases/boards/board-view-column';
|
||||||
import { SelectOptionBadge } from '@colanode/ui/components/databases/fields/select-option-badge';
|
import { SelectOptionBadge } from '@colanode/ui/components/databases/fields/select-option-badge';
|
||||||
import { BoardViewContext } from '@colanode/ui/contexts/board-view';
|
import { BoardViewContext } from '@colanode/ui/contexts/board-view';
|
||||||
@@ -89,29 +89,24 @@ export const BoardViewColumnsSelect = ({
|
|||||||
),
|
),
|
||||||
canDrag: (record) => record.canEdit,
|
canDrag: (record) => record.canEdit,
|
||||||
onDragEnd: async (record, value) => {
|
onDragEnd: async (record, value) => {
|
||||||
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.delete',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
toast.error(result.error.message);
|
draft.fields = rest;
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.set',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
value,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
draft.fields[field.id] = value;
|
||||||
toast.error(result.error.message);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
@@ -138,29 +133,24 @@ export const BoardViewColumnsSelect = ({
|
|||||||
dragOverClass: noValueDraggingClass,
|
dragOverClass: noValueDraggingClass,
|
||||||
canDrag: () => true,
|
canDrag: () => true,
|
||||||
onDragEnd: async (record, value) => {
|
onDragEnd: async (record, value) => {
|
||||||
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
if (!value) {
|
if (!value) {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.delete',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
toast.error(result.error.message);
|
draft.fields = rest;
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
const result = await window.colanode.executeMutation({
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.field.value.set',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
fieldId: field.id,
|
}
|
||||||
value,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
draft.fields[field.id] = value;
|
||||||
toast.error(result.error.message);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
import isHotkey from 'is-hotkey';
|
import isHotkey from 'is-hotkey';
|
||||||
import { SquareArrowOutUpRight } from 'lucide-react';
|
import { SquareArrowOutUpRight } from 'lucide-react';
|
||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { toast } from 'sonner';
|
|
||||||
|
|
||||||
import { RecordNode } from '@colanode/core';
|
import { RecordNode } from '@colanode/core';
|
||||||
|
import { collections } from '@colanode/ui/collections';
|
||||||
import { Link } from '@colanode/ui/components/ui/link';
|
import { Link } from '@colanode/ui/components/ui/link';
|
||||||
import { Spinner } from '@colanode/ui/components/ui/spinner';
|
|
||||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
|
||||||
|
|
||||||
interface NameEditorProps {
|
interface NameEditorProps {
|
||||||
initialValue: string;
|
initialValue: string;
|
||||||
@@ -61,27 +59,21 @@ export const TableViewNameCell = ({ record }: TableViewNameCellProps) => {
|
|||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const [isEditing, setIsEditing] = React.useState(false);
|
const [isEditing, setIsEditing] = React.useState(false);
|
||||||
|
|
||||||
const { mutate, isPending } = useMutation();
|
|
||||||
const canEdit = true;
|
const canEdit = true;
|
||||||
const hasName = record.name && record.name.length > 0;
|
const hasName = record.name && record.name.length > 0;
|
||||||
|
|
||||||
const handleSave = (newName: string) => {
|
const handleSave = (newName: string) => {
|
||||||
if (newName === record.name) return;
|
if (newName === record.name) return;
|
||||||
|
|
||||||
mutate({
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
input: {
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.name.update',
|
if (draft.type !== 'record') {
|
||||||
name: newName,
|
return;
|
||||||
recordId: record.id,
|
}
|
||||||
userId: workspace.userId,
|
draft.name = newName;
|
||||||
},
|
|
||||||
onSuccess() {
|
|
||||||
setIsEditing(false);
|
|
||||||
},
|
|
||||||
onError(error) {
|
|
||||||
toast.error(error.message);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setIsEditing(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -112,11 +104,6 @@ export const TableViewNameCell = ({ record }: TableViewNameCellProps) => {
|
|||||||
>
|
>
|
||||||
<SquareArrowOutUpRight className="mr-1 size-4" /> <p>Open</p>
|
<SquareArrowOutUpRight className="mr-1 size-4" /> <p>Open</p>
|
||||||
</Link>
|
</Link>
|
||||||
{isPending && (
|
|
||||||
<span className="absolute right-2 text-muted-foreground">
|
|
||||||
<Spinner size="small" />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import { toast } from 'sonner';
|
import { collections } from '@colanode/ui/collections';
|
||||||
|
|
||||||
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
import { Avatar } from '@colanode/ui/components/avatars/avatar';
|
||||||
import { AvatarPopover } from '@colanode/ui/components/avatars/avatar-popover';
|
import { AvatarPopover } from '@colanode/ui/components/avatars/avatar-popover';
|
||||||
import { Button } from '@colanode/ui/components/ui/button';
|
import { Button } from '@colanode/ui/components/ui/button';
|
||||||
import { useRecord } from '@colanode/ui/contexts/record';
|
import { useRecord } from '@colanode/ui/contexts/record';
|
||||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
|
||||||
|
|
||||||
export const RecordAvatar = () => {
|
export const RecordAvatar = () => {
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
|
|
||||||
const { mutate, isPending } = useMutation();
|
|
||||||
|
|
||||||
if (!record.canEdit) {
|
if (!record.canEdit) {
|
||||||
return (
|
return (
|
||||||
<Button type="button" variant="outline" size="icon">
|
<Button type="button" variant="outline" size="icon">
|
||||||
@@ -29,19 +25,14 @@ export const RecordAvatar = () => {
|
|||||||
return (
|
return (
|
||||||
<AvatarPopover
|
<AvatarPopover
|
||||||
onPick={(avatar) => {
|
onPick={(avatar) => {
|
||||||
if (isPending) return;
|
|
||||||
if (avatar === record.avatar) return;
|
if (avatar === record.avatar) return;
|
||||||
|
|
||||||
mutate({
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
input: {
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.avatar.update',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
avatar,
|
}
|
||||||
userId: workspace.userId,
|
draft.avatar = avatar;
|
||||||
},
|
|
||||||
onError(error) {
|
|
||||||
toast.error(error.message);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
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 { SmartTextInput } from '@colanode/ui/components/ui/smart-text-input';
|
||||||
import { useRecord } from '@colanode/ui/contexts/record';
|
import { useRecord } from '@colanode/ui/contexts/record';
|
||||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||||
import { useMutation } from '@colanode/ui/hooks/use-mutation';
|
|
||||||
|
|
||||||
export const RecordName = () => {
|
export const RecordName = () => {
|
||||||
const workspace = useWorkspace();
|
const workspace = useWorkspace();
|
||||||
const record = useRecord();
|
const record = useRecord();
|
||||||
const { mutate, isPending } = useMutation();
|
|
||||||
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@@ -29,24 +27,17 @@ export const RecordName = () => {
|
|||||||
readOnly={!record.canEdit}
|
readOnly={!record.canEdit}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (isPending) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === record.name) {
|
if (value === record.name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutate({
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
input: {
|
nodes.update(record.id, (draft) => {
|
||||||
type: 'record.name.update',
|
if (draft.type !== 'record') {
|
||||||
recordId: record.id,
|
return;
|
||||||
name: value,
|
}
|
||||||
userId: workspace.userId,
|
|
||||||
},
|
draft.name = value;
|
||||||
onError(error) {
|
|
||||||
toast.error(error.message);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="font-heading border-b border-none pl-1 text-4xl font-bold shadow-none focus-visible:ring-0"
|
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 { LocalRecordNode } from '@colanode/client/types';
|
||||||
import { NodeRole, hasNodeRole } from '@colanode/core';
|
import { NodeRole, hasNodeRole } from '@colanode/core';
|
||||||
|
import { collections } from '@colanode/ui/collections';
|
||||||
import { RecordContext } from '@colanode/ui/contexts/record';
|
import { RecordContext } from '@colanode/ui/contexts/record';
|
||||||
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
import { useWorkspace } from '@colanode/ui/contexts/workspace';
|
||||||
|
|
||||||
@@ -34,29 +35,35 @@ export const RecordProvider = ({
|
|||||||
localRevision: record.localRevision,
|
localRevision: record.localRevision,
|
||||||
canEdit,
|
canEdit,
|
||||||
updateFieldValue: async (field, value) => {
|
updateFieldValue: async (field, value) => {
|
||||||
const result = await window.colanode.executeMutation({
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
type: 'record.field.value.set',
|
if (!nodes.has(record.id)) {
|
||||||
recordId: record.id,
|
toast.error('Record not found');
|
||||||
fieldId: field.id,
|
return;
|
||||||
value,
|
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
toast.error(result.error.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodes.update(record.id, (draft) => {
|
||||||
|
if (draft.type !== 'record') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
draft.fields[field.id] = value;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
removeFieldValue: async (field) => {
|
removeFieldValue: async (field) => {
|
||||||
const result = await window.colanode.executeMutation({
|
const nodes = collections.workspace(workspace.userId).nodes;
|
||||||
type: 'record.field.value.delete',
|
if (!nodes.has(record.id)) {
|
||||||
recordId: record.id,
|
toast.error('Record not found');
|
||||||
fieldId: field.id,
|
return;
|
||||||
userId: workspace.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
toast.error(result.error.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodes.update(record.id, (draft) => {
|
||||||
|
if (draft.type !== 'record') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { [field.id]: _removed, ...rest } = draft.fields;
|
||||||
|
draft.fields = rest;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
getBooleanValue: (field) => {
|
getBooleanValue: (field) => {
|
||||||
const fieldValue = record.fields[field.id];
|
const fieldValue = record.fields[field.id];
|
||||||
|
|||||||
Reference in New Issue
Block a user