Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions apps/web/app/(app)/automation/RuleForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,20 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {

const conditionalOperator = watch("conditionalOperator");

const typeOptions = useMemo(() => {
return [
{ label: "Archive", value: ActionType.ARCHIVE },
{ label: "Label", value: ActionType.LABEL },
{ label: "Draft email", value: ActionType.DRAFT_EMAIL },
{ label: "Reply", value: ActionType.REPLY },
{ label: "Send email", value: ActionType.SEND_EMAIL },
{ label: "Forward", value: ActionType.FORWARD },
{ label: "Mark read", value: ActionType.MARK_READ },
{ label: "Mark spam", value: ActionType.MARK_SPAM },
{ label: "Call webhook", value: ActionType.CALL_WEBHOOK },
];
}, []);

return (
<form onSubmit={handleSubmit(onSubmit)}>
{isSubmitted && Object.keys(errors).length > 0 && (
Expand Down Expand Up @@ -520,10 +534,7 @@ export function RuleForm({ rule }: { rule: CreateRuleBody & { id?: string } }) {
<div className="sm:col-span-1">
<Select
label="Type"
options={Object.keys(ActionType).map((action) => ({
label: capitalCase(action),
value: action,
}))}
options={typeOptions}
{...register(`actions.${i}.type`)}
error={errors.actions?.[i]?.type as FieldError | undefined}
/>
Expand Down
5 changes: 5 additions & 0 deletions apps/web/components/PlanBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ export function ActionBadgeExpanded({ action }: { action: ExecutedAction }) {
return <ActionBadge type={ActionType.MARK_SPAM} />;
case ActionType.CALL_WEBHOOK:
return <ActionBadge type={ActionType.CALL_WEBHOOK} />;
case ActionType.MARK_READ:
return <ActionBadge type={ActionType.MARK_READ} />;
default:
return <ActionBadge type={action.type} />;
}
Expand Down Expand Up @@ -151,6 +153,8 @@ function getActionLabel(type: ActionType) {
return "Webhook";
case ActionType.MARK_SPAM:
return "Mark as spam";
case ActionType.MARK_READ:
return "Mark as read";
default:
return capitalCase(type);
}
Expand Down Expand Up @@ -182,6 +186,7 @@ export function getActionColor(actionType: ActionType): Color {
case ActionType.DRAFT_EMAIL:
return "green";
case ActionType.ARCHIVE:
case ActionType.MARK_READ:
return "yellow";
case ActionType.LABEL:
return "blue";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "ActionType" ADD VALUE 'MARK_READ';
3 changes: 2 additions & 1 deletion apps/web/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ model User {
rulesPrompt String?
webhookSecret String?

// categorization
// categorization
autoCategorizeSenders Boolean @default(false)

// premium can be shared among multiple users
Expand Down Expand Up @@ -381,6 +381,7 @@ enum ActionType {
DRAFT_EMAIL
MARK_SPAM
CALL_WEBHOOK
MARK_READ
// SUMMARIZE
// SNOOZE
// ADD_TO_DO
Expand Down
2 changes: 2 additions & 0 deletions apps/web/utils/action-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export const actionInputs: Record<
},
],
},
[ActionType.MARK_READ]: { fields: [] },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

LGTM! But handle the new action type in sanitizeActionFields.

The MARK_READ action is correctly added to actionInputs. However, it needs to be handled in the sanitizeActionFields function's switch statement to maintain type safety.

Add this case before the default case:

    case ActionType.CALL_WEBHOOK: {
      return {
        ...base,
        url: action.url ?? null,
      };
    }
+   case ActionType.MARK_READ:
+     return base;
    default:
      // biome-ignore lint/correctness/noSwitchDeclarations: intentional exhaustive check
      const exhaustiveCheck: never = action.type;
      return exhaustiveCheck;

Committable suggestion skipped: line range outside the PR's diff.

};

export function getActionFields(fields: Action | ExecutedAction | undefined) {
Expand Down Expand Up @@ -168,6 +169,7 @@ export function sanitizeActionFields(
switch (action.type) {
case ActionType.ARCHIVE:
case ActionType.MARK_SPAM:
case ActionType.MARK_READ:
return base;
case ActionType.LABEL: {
return {
Expand Down
1 change: 1 addition & 0 deletions apps/web/utils/actions/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const zodActionType = z.enum([
ActionType.REPLY,
ActionType.SEND_EMAIL,
ActionType.CALL_WEBHOOK,
ActionType.MARK_READ,
]);

const zodField = z
Expand Down
22 changes: 22 additions & 0 deletions apps/web/utils/ai/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
archiveThread,
getOrCreateLabel,
labelThread,
markReadThread,
} from "@/utils/gmail/label";
import { markSpam } from "@/utils/gmail/spam";
import type { Attachment } from "@/utils/types/mail";
Expand Down Expand Up @@ -261,6 +262,17 @@ const CALL_WEBHOOK: ActionFunctionDef = {
action: ActionType.CALL_WEBHOOK,
};

const MARK_READ: ActionFunctionDef = {
name: "mark_read",
description: "Mark as read.",
parameters: {
type: "object",
properties: {},
required: [],
},
action: ActionType.MARK_READ,
};

export const actionFunctionDefs: Record<ActionType, ActionFunctionDef> = {
[ActionType.ARCHIVE]: ARCHIVE,
[ActionType.LABEL]: LABEL,
Expand All @@ -270,6 +282,7 @@ export const actionFunctionDefs: Record<ActionType, ActionFunctionDef> = {
[ActionType.FORWARD]: FORWARD_EMAIL,
[ActionType.MARK_SPAM]: MARK_SPAM,
[ActionType.CALL_WEBHOOK]: CALL_WEBHOOK,
[ActionType.MARK_READ]: MARK_READ,
};

const archive: ActionFunction<Record<string, unknown>> = async (
Expand Down Expand Up @@ -421,6 +434,13 @@ const call_webhook: ActionFunction<any> = async (
});
};

const mark_read: ActionFunction<any> = async (
gmail: gmail_v1.Gmail,
email: EmailForAction,
) => {
return await markReadThread({ gmail, threadId: email.threadId, read: true });
};

export const runActionFunction = async (
gmail: gmail_v1.Gmail,
email: EmailForAction,
Expand Down Expand Up @@ -453,6 +473,8 @@ export const runActionFunction = async (
return mark_spam(gmail, email, args, userEmail, executedRule);
case ActionType.CALL_WEBHOOK:
return call_webhook(gmail, email, args, userEmail, executedRule);
case ActionType.MARK_READ:
return mark_read(gmail, email, args, userEmail, executedRule);
default:
throw new Error(`Unknown action: ${action}`);
}
Expand Down
3 changes: 3 additions & 0 deletions apps/web/utils/ai/rule/create-prompt-from-rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ export function createPromptFromRule(rule: RuleWithRelations): string {
case ActionType.CALL_WEBHOOK:
if (action.url) actions.push(`call webhook at ${action.url}`);
break;
case ActionType.MARK_READ:
actions.push("mark as read");
break;
default:
console.warn(`Unknown action type: ${action.type}`);
// biome-ignore lint/correctness/noSwitchDeclarations: intentional exhaustive check
Expand Down