From 0e20e340c0c5210fd876d30b68e27eb626e83caa Mon Sep 17 00:00:00 2001
From: Waleed Latif
Date: Thu, 6 Mar 2025 18:21:39 -0800
Subject: [PATCH] feat(oauth): modified google drive/gmail icons, added
credentials selector sub-block to gmail tool
---
.../components/sub-block/sub-block.tsx | 22 ++++++++
app/w/[id]/workflow.tsx | 21 +++++++
.../components/credentials/credentials.tsx | 26 +++++----
blocks/blocks/gmail.ts | 28 +++++++---
blocks/types.ts | 6 ++
components/icons.tsx | 55 +++++++++++++++++--
6 files changed, 133 insertions(+), 25 deletions(-)
diff --git a/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx b/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx
index f0884389d3..93b7bda2b9 100644
--- a/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx
+++ b/app/w/[id]/components/workflow-block/components/sub-block/sub-block.tsx
@@ -7,6 +7,7 @@ import { DateInput } from './components/date-input'
import { Dropdown } from './components/dropdown'
import { EvalInput } from './components/eval-input'
import { LongInput } from './components/long-input'
+import { OAuthInput } from './components/oauth-input'
import { ShortInput } from './components/short-input'
import { SliderInput } from './components/slider-input'
import { Switch } from './components/switch'
@@ -107,6 +108,27 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) {
return (
)
+ case 'oauth-input':
+ return (
+ {
+ // Use the workflow store to update the value
+ const event = new CustomEvent('update-subblock-value', {
+ detail: {
+ blockId,
+ subBlockId: config.id,
+ value,
+ },
+ })
+ window.dispatchEvent(event)
+ }}
+ provider={config.provider as any}
+ requiredScopes={config.requiredScopes || []}
+ label={config.placeholder}
+ serviceId={config.serviceId}
+ />
+ )
default:
return null
}
diff --git a/app/w/[id]/workflow.tsx b/app/w/[id]/workflow.tsx
index 0aaa4bc72e..396eef89e7 100644
--- a/app/w/[id]/workflow.tsx
+++ b/app/w/[id]/workflow.tsx
@@ -18,6 +18,7 @@ import { useNotificationStore } from '@/stores/notifications/store'
import { useGeneralStore } from '@/stores/settings/general/store'
import { getSyncManagers, initializeSyncManagers, isSyncInitialized } from '@/stores/sync-registry'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
+import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import { NotificationList } from '@/app/w/[id]/components/notifications/notifications'
import { getBlock } from '../../../blocks'
@@ -51,6 +52,7 @@ function WorkflowContent() {
const { workflows, setActiveWorkflow, createWorkflow } = useWorkflowRegistry()
const { blocks, edges, loops, addBlock, updateBlockPosition, addEdge, removeEdge } =
useWorkflowStore()
+ const { setValue: setSubBlockValue } = useSubBlockStore()
// Add OAuth error handling
const { modalState, handleOAuthError, closeModal } = useOAuthErrorHandler()
@@ -312,6 +314,25 @@ function WorkflowContent() {
return () => window.removeEventListener('keydown', handleKeyDown)
}, [selectedEdgeId, removeEdge])
+ // Handle sub-block value updates from custom events
+ useEffect(() => {
+ const handleSubBlockValueUpdate = (event: CustomEvent) => {
+ const { blockId, subBlockId, value } = event.detail
+ if (blockId && subBlockId) {
+ setSubBlockValue(blockId, subBlockId, value)
+ }
+ }
+
+ window.addEventListener('update-subblock-value', handleSubBlockValueUpdate as EventListener)
+
+ return () => {
+ window.removeEventListener(
+ 'update-subblock-value',
+ handleSubBlockValueUpdate as EventListener
+ )
+ }
+ }, [setSubBlockValue])
+
if (!isInitialized) return null
return (
diff --git a/app/w/components/sidebar/components/settings-modal/components/credentials/credentials.tsx b/app/w/components/sidebar/components/settings-modal/components/credentials/credentials.tsx
index 23d822dc4c..8325b20b01 100644
--- a/app/w/components/sidebar/components/settings-modal/components/credentials/credentials.tsx
+++ b/app/w/components/sidebar/components/settings-modal/components/credentials/credentials.tsx
@@ -3,7 +3,7 @@
import { useEffect, useState } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import { Check, ExternalLink, RefreshCw } from 'lucide-react'
-import { GithubIcon, GoogleIcon, xIcon as XIcon } from '@/components/icons'
+import { GithubIcon, GoogleDriveIcon, xIcon as XIcon } from '@/components/icons'
import { GmailIcon } from '@/components/icons'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
@@ -61,7 +61,7 @@ export function Credentials({ onOpenChange }: CredentialsProps) {
description: 'Streamline file organization and document workflows.',
provider: 'google',
providerId: 'google-drive',
- icon: ,
+ icon: ,
isConnected: false,
scopes: [],
},
@@ -276,6 +276,19 @@ export function Credentials({ onOpenChange }: CredentialsProps) {
+ {pendingService && (
+
+
+
+
+
+ Action Required: Please connect your
+ account to enable the requested features. The required service will be highlighted
+ below.
+
+
+ )}
+
{isLoading ? (
<>
@@ -326,15 +339,6 @@ export function Credentials({ onOpenChange }: CredentialsProps) {
))
)}
-
- {pendingService && (
-
-
- Note: Connect this service to use tools that
- require this authentication.
-
-
- )}
)
}
diff --git a/blocks/blocks/gmail.ts b/blocks/blocks/gmail.ts
index 4d327bfd1f..f83aa526da 100644
--- a/blocks/blocks/gmail.ts
+++ b/blocks/blocks/gmail.ts
@@ -9,7 +9,7 @@ export const GmailBlock: BlockConfig = {
longDescription:
'Integrate Gmail functionality to send, read, and search email messages within your workflow. Automate email communications and process email content using OAuth authentication.',
category: 'tools',
- bgColor: '#F14537',
+ bgColor: '#E0E0E0',
icon: GmailIcon,
subBlocks: [
// Operation selector
@@ -24,14 +24,19 @@ export const GmailBlock: BlockConfig = {
{ label: 'Search Emails', id: 'search_gmail' },
],
},
- // OAuth Token
+ // Gmail Credentials
{
- id: 'accessToken',
- title: 'Access Token',
- type: 'short-input',
+ id: 'credential',
+ title: 'Gmail Account',
+ type: 'oauth-input',
layout: 'full',
- placeholder: 'Enter Gmail OAuth token',
- password: true,
+ provider: 'google',
+ serviceId: 'gmail',
+ requiredScopes: [
+ 'https://www.googleapis.com/auth/gmail.send',
+ 'https://www.googleapis.com/auth/gmail.readonly',
+ ],
+ placeholder: 'Select Gmail account',
},
// Send Email Fields
{
@@ -100,11 +105,18 @@ export const GmailBlock: BlockConfig = {
throw new Error(`Invalid Gmail operation: ${params.operation}`)
}
},
+ params: (params) => {
+ // Add the credential ID to the params
+ return {
+ ...params,
+ _credentialId: params.credential,
+ }
+ },
},
},
inputs: {
operation: { type: 'string', required: true },
- accessToken: { type: 'string', required: true },
+ credential: { type: 'string', required: true },
// Send operation inputs
to: { type: 'string', required: false },
subject: { type: 'string', required: false },
diff --git a/blocks/types.ts b/blocks/types.ts
index d848b93a0c..4f0c35a362 100644
--- a/blocks/types.ts
+++ b/blocks/types.ts
@@ -25,6 +25,7 @@ export type SubBlockType =
| 'eval-input' // Evaluation input
| 'date-input' // Date input
| 'time-input' // Time input
+ | 'oauth-input' // OAuth credential selector
// Component width setting
export type SubBlockLayout = 'full' | 'half'
@@ -95,6 +96,10 @@ export interface SubBlockConfig {
value: string | number | boolean
}
}
+ // OAuth specific properties
+ provider?: string
+ serviceId?: string
+ requiredScopes?: string[]
}
// Main block definition
@@ -111,6 +116,7 @@ export interface BlockConfig {
access: string[]
config?: {
tool: (params: Record) => string
+ params?: (params: Record) => Record
}
}
inputs: Record
diff --git a/components/icons.tsx b/components/icons.tsx
index 38a1f09b82..b033ba8074 100644
--- a/components/icons.tsx
+++ b/components/icons.tsx
@@ -1169,25 +1169,68 @@ export const NotionIcon = (props: SVGProps) => {
export const GmailIcon = (props: SVGProps) => {
return (
-