Feat/email settings v2 (#23180)
Settings pages changes - Add `displayName` - Unsubscribers Page <img width="1496" height="844" alt="Screenshot 2026-07-22 at 8 52 15 PM" src="https://github.com/user-attachments/assets/69bc1993-4547-4a64-83a6-b47fef1a4e40" /> <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/23180?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
+13
-1
@@ -1,5 +1,11 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsEmail, IsNotEmpty, IsString, MaxLength } from 'class-validator';
|
||||
import {
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
MaxLength,
|
||||
} from 'class-validator';
|
||||
|
||||
@InputType('CreateEmailGroupChannelInput')
|
||||
export class CreateEmailGroupChannelInput {
|
||||
@@ -9,4 +15,10 @@ export class CreateEmailGroupChannelInput {
|
||||
@IsEmail()
|
||||
@MaxLength(254)
|
||||
handle: string;
|
||||
|
||||
@Field({ nullable: true })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(255)
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
+5
@@ -39,6 +39,11 @@ export class MessageChannelDTO {
|
||||
@Field()
|
||||
handle: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
displayName: string | null;
|
||||
|
||||
@IsEnum(MessageChannelType)
|
||||
@IsNotEmpty()
|
||||
@Field(() => MessageChannelType)
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
import { IsOptional, IsString, MaxLength } from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@InputType('UpdateEmailGroupChannelInput')
|
||||
export class UpdateEmailGroupChannelInput {
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(255)
|
||||
displayName?: string | null;
|
||||
}
|
||||
+3
@@ -68,6 +68,9 @@ export class MessageChannelEntity extends WorkspaceRelatedEntity {
|
||||
@Column({ type: 'varchar', nullable: false })
|
||||
handle: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
displayName: string | null;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: MessageChannelType,
|
||||
|
||||
+49
@@ -213,10 +213,12 @@ export class MessageChannelMetadataService {
|
||||
|
||||
async createEmailGroupChannel({
|
||||
handle,
|
||||
displayName,
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
}: {
|
||||
handle: string;
|
||||
displayName?: string;
|
||||
userWorkspaceId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<CreateEmailGroupChannelOutput> {
|
||||
@@ -268,9 +270,14 @@ export class MessageChannelMetadataService {
|
||||
visibility: 'workspace',
|
||||
});
|
||||
|
||||
const trimmedDisplayName = displayName?.trim();
|
||||
|
||||
const messageChannel = await this.create({
|
||||
workspaceId,
|
||||
handle: forwardingAddress,
|
||||
displayName: isNonEmptyString(trimmedDisplayName)
|
||||
? trimmedDisplayName
|
||||
: null,
|
||||
connectedAccountId: connectedAccount.id,
|
||||
type: MessageChannelType.EMAIL_GROUP,
|
||||
visibility: MessageChannelVisibility.SHARE_EVERYTHING,
|
||||
@@ -340,6 +347,48 @@ export class MessageChannelMetadataService {
|
||||
return messageChannel;
|
||||
}
|
||||
|
||||
async updateEmailGroupChannel({
|
||||
id,
|
||||
displayName,
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
}: {
|
||||
id: string;
|
||||
displayName?: string | null;
|
||||
userWorkspaceId: string;
|
||||
workspaceId: string;
|
||||
}): Promise<MessageChannelDTO> {
|
||||
const messageChannel = await this.verifyOwnership({
|
||||
id,
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (messageChannel.type !== MessageChannelType.EMAIL_GROUP) {
|
||||
throw new MessageChannelException(
|
||||
`Message channel ${id} is not an email group`,
|
||||
MessageChannelExceptionCode.INVALID_MESSAGE_CHANNEL_INPUT,
|
||||
);
|
||||
}
|
||||
|
||||
// An omitted displayName leaves the current one untouched; an explicit null clears it
|
||||
if (displayName === undefined) {
|
||||
return messageChannel;
|
||||
}
|
||||
|
||||
const trimmedDisplayName = displayName?.trim();
|
||||
|
||||
return this.update({
|
||||
id,
|
||||
workspaceId,
|
||||
data: {
|
||||
displayName: isNonEmptyString(trimmedDisplayName)
|
||||
? trimmedDisplayName
|
||||
: null,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async deleteEmailGroupChannel({
|
||||
id,
|
||||
userWorkspaceId,
|
||||
|
||||
+17
@@ -17,6 +17,7 @@ import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { ConnectedAccountMetadataService } from 'src/engine/metadata-modules/connected-account/connected-account-metadata.service';
|
||||
import { ConnectedAccountPublicDTO } from 'src/engine/metadata-modules/connected-account/dtos/connected-account-public.dto';
|
||||
import { CreateEmailGroupChannelInput } from 'src/engine/metadata-modules/message-channel/dtos/create-email-group-channel.input';
|
||||
import { UpdateEmailGroupChannelInput } from 'src/engine/metadata-modules/message-channel/dtos/update-email-group-channel.input';
|
||||
import { CreateEmailGroupChannelOutput } from 'src/engine/metadata-modules/message-channel/dtos/create-email-group-channel.output';
|
||||
import { MessageChannelDTO } from 'src/engine/metadata-modules/message-channel/dtos/message-channel.dto';
|
||||
import { UpdateMessageChannelInput } from 'src/engine/metadata-modules/message-channel/dtos/update-message-channel.input';
|
||||
@@ -174,6 +175,22 @@ export class MessageChannelResolver {
|
||||
): Promise<CreateEmailGroupChannelOutput> {
|
||||
return this.messageChannelMetadataService.createEmailGroupChannel({
|
||||
handle: input.handle,
|
||||
displayName: input.displayName,
|
||||
userWorkspaceId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
@Mutation(() => MessageChannelDTO)
|
||||
@UseGuards(NoPermissionGuard)
|
||||
async updateEmailGroupChannel(
|
||||
@Args('input') input: UpdateEmailGroupChannelInput,
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId() userWorkspaceId: string,
|
||||
): Promise<MessageChannelDTO> {
|
||||
return this.messageChannelMetadataService.updateEmailGroupChannel({
|
||||
id: input.id,
|
||||
displayName: input.displayName,
|
||||
userWorkspaceId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user