From 557cf9a5abef5f617164bae6107924b158b59a5b Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 8 Jul 2025 19:58:20 -0700 Subject: [PATCH 1/5] force user to refresh on disconnect in order to mkae changes, add read-only offline mode --- .../connection-status/connection-status.tsx | 71 ++++--- .../user-avatar-stack/user-avatar-stack.tsx | 95 +++++----- .../components/control-bar/control-bar.tsx | 16 +- .../toolbar-block/toolbar-block.tsx | 9 +- .../toolbar-loop-block/toolbar-loop-block.tsx | 9 +- .../toolbar-parallel-block.tsx | 8 +- .../components/action-bar/action-bar.tsx | 21 ++- .../workflow-block/workflow-block.tsx | 8 +- .../workspace-permissions-provider.tsx | 177 ++++++++++++------ apps/sim/contexts/socket-context.tsx | 4 +- 10 files changed, 267 insertions(+), 151 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx index 99fcd1fb7e..153f554ec3 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx @@ -1,53 +1,64 @@ 'use client' -import { useEffect, useState } from 'react' +import { AlertTriangle, RefreshCw } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' interface ConnectionStatusProps { isConnected: boolean } export function ConnectionStatus({ isConnected }: ConnectionStatusProps) { - const [showOfflineNotice, setShowOfflineNotice] = useState(false) + const userPermissions = useUserPermissionsContext() - useEffect(() => { - let timeoutId: NodeJS.Timeout - - if (!isConnected) { - // Show offline notice after 6 seconds of being disconnected - timeoutId = setTimeout(() => { - setShowOfflineNotice(true) - }, 6000) // 6 seconds - } else { - // Hide notice immediately when reconnected - setShowOfflineNotice(false) - } - - return () => { - if (timeoutId) { - clearTimeout(timeoutId) - } - } - }, [isConnected]) + const handleRefresh = () => { + window.location.reload() + } - // Don't render anything if connected or if we haven't been disconnected long enough - if (!showOfflineNotice) { + // Don't render anything if not in offline mode + if (!userPermissions.isOfflineMode) { return null } return ( -
-
+
+
-
-
+ {!isConnected && ( +
+ )} +
- Connection lost - - Changes not saved - please refresh + + {isConnected ? 'Reconnected' : 'Connection lost - Please Refresh'} + + + {isConnected ? 'Refresh to continue editing' : 'Read-only mode active'}
+ + + + + Refresh page to continue editing +
) } + +// Hook to check if we're in offline mode (for use by other components) +// This now uses the workspace permissions context for consistent state +export function useConnectionStatus() { + const userPermissions = useUserPermissionsContext() + return { isOfflineMode: userPermissions.isOfflineMode } +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/user-avatar-stack.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/user-avatar-stack.tsx index 7d90b4e741..4aa6a3a433 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/user-avatar-stack.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/user-avatar-stack.tsx @@ -44,16 +44,6 @@ export function UserAvatarStack({ } }, [users, maxVisible]) - // Show connection status component regardless of user count - // This will handle the offline notice when disconnected for 15 seconds - const connectionStatusElement = - - // Only show presence when there are multiple users (>1) - // But always show connection status - if (users.length <= 1) { - return connectionStatusElement - } - // Determine spacing based on size const spacingClass = { sm: '-space-x-1', @@ -62,46 +52,55 @@ export function UserAvatarStack({ }[size] return ( -
- {/* Connection status - always present */} - {connectionStatusElement} +
+ {/* Connection status - always check, shows when offline */} + - {/* Render visible user avatars */} - {visibleUsers.map((user, index) => ( - -
{user.name}
- {user.info &&
{user.info}
} -
- ) : null - } - /> - ))} + {/* Only show avatar stack when there are multiple users (>1) */} + {users.length > 1 && ( +
+ {/* Render visible user avatars */} + {visibleUsers.map((user, index) => ( + +
{user.name}
+ {user.info && ( +
{user.info}
+ )} +
+ ) : null + } + /> + ))} - {/* Render overflow indicator if there are more users */} - {overflowCount > 0 && ( - -
- {overflowCount} more user{overflowCount > 1 ? 's' : ''} -
-
{users.length} total online
-
- } - /> + {/* Render overflow indicator if there are more users */} + {overflowCount > 0 && ( + +
+ {overflowCount} more user{overflowCount > 1 ? 's' : ''} +
+
+ {users.length} total online +
+
+ } + /> + )} +
)}
) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx index 855f41d307..96ae93eb54 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx @@ -670,7 +670,11 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { {!canEdit && ( - Edit permissions required to rename workflows + + {userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Edit permissions required to rename workflows'} + )} )} @@ -934,7 +938,11 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { )} - {canEdit ? 'Duplicate Workflow' : 'Admin permission required to duplicate workflows'} + {canEdit + ? 'Duplicate Workflow' + : userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Admin permission required to duplicate workflows'} ) @@ -975,7 +983,9 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { {!userPermissions.canEdit - ? 'Admin permission required to use auto-layout' + ? userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Admin permission required to use auto-layout' : 'Auto Layout'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx index b625fa4040..b447d53f79 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' import type { BlockConfig } from '@/blocks/types' export type ToolbarBlockProps = { @@ -9,6 +10,8 @@ export type ToolbarBlockProps = { } export function ToolbarBlock({ config, disabled = false }: ToolbarBlockProps) { + const userPermissions = useUserPermissionsContext() + const handleDragStart = (e: React.DragEvent) => { if (disabled) { e.preventDefault() @@ -66,7 +69,11 @@ export function ToolbarBlock({ config, disabled = false }: ToolbarBlockProps) { return ( {blockContent} - Edit permissions required to add blocks + + {userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Edit permissions required to add blocks'} + ) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx index 6097e64427..991f18224e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' import { LoopTool } from '../../../loop-node/loop-config' type LoopToolbarItemProps = { @@ -9,6 +10,8 @@ type LoopToolbarItemProps = { // Custom component for the Loop Tool export default function LoopToolbarItem({ disabled = false }: LoopToolbarItemProps) { + const userPermissions = useUserPermissionsContext() + const handleDragStart = (e: React.DragEvent) => { if (disabled) { e.preventDefault() @@ -74,7 +77,11 @@ export default function LoopToolbarItem({ disabled = false }: LoopToolbarItemPro return ( {blockContent} - Edit permissions required to add blocks + + {userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Edit permissions required to add blocks'} + ) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx index 08c732dacb..e1f8ddfc33 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx @@ -1,6 +1,7 @@ import { useCallback } from 'react' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' import { ParallelTool } from '../../../parallel-node/parallel-config' type ParallelToolbarItemProps = { @@ -9,6 +10,7 @@ type ParallelToolbarItemProps = { // Custom component for the Parallel Tool export default function ParallelToolbarItem({ disabled = false }: ParallelToolbarItemProps) { + const userPermissions = useUserPermissionsContext() const handleDragStart = (e: React.DragEvent) => { if (disabled) { e.preventDefault() @@ -75,7 +77,11 @@ export default function ParallelToolbarItem({ disabled = false }: ParallelToolba return ( {blockContent} - Edit permissions required to add blocks + + {userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Edit permissions required to add blocks'} + ) } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx index c85fc6dac4..ddc4e2f0ea 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx @@ -2,6 +2,7 @@ import { ArrowLeftRight, ArrowUpDown, Circle, CircleOff, Copy, Trash2 } from 'lu import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' +import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider' import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow' import { useWorkflowStore } from '@/stores/workflows/workflow/store' @@ -22,9 +23,17 @@ export function ActionBar({ blockId, blockType, disabled = false }: ActionBarPro const horizontalHandles = useWorkflowStore( (state) => state.blocks[blockId]?.horizontalHandles ?? false ) + const userPermissions = useUserPermissionsContext() const isStarterBlock = blockType === 'starter' + const getTooltipMessage = (defaultMessage: string) => { + if (disabled) { + return userPermissions.isOfflineMode ? 'Connection lost - Please Refresh' : 'Read-only mode' + } + return defaultMessage + } + return (
- {disabled ? 'Read-only mode' : isEnabled ? 'Disable Block' : 'Enable Block'} + {getTooltipMessage(isEnabled ? 'Disable Block' : 'Enable Block')} @@ -89,9 +98,7 @@ export function ActionBar({ blockId, blockType, disabled = false }: ActionBarPro - - {disabled ? 'Read-only mode' : 'Duplicate Block'} - + {getTooltipMessage('Duplicate Block')} )} @@ -116,7 +123,7 @@ export function ActionBar({ blockId, blockType, disabled = false }: ActionBarPro - {disabled ? 'Read-only mode' : horizontalHandles ? 'Vertical Ports' : 'Horizontal Ports'} + {getTooltipMessage(horizontalHandles ? 'Vertical Ports' : 'Horizontal Ports')} @@ -140,9 +147,7 @@ export function ActionBar({ blockId, blockType, disabled = false }: ActionBarPro - - {disabled ? 'Read-only mode' : 'Delete Block'} - + {getTooltipMessage('Delete Block')} )}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index a69b0c7458..167b406aa7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -654,7 +654,9 @@ export function WorkflowBlock({ id, data }: NodeProps) { {!userPermissions.canEdit - ? 'Read-only mode' + ? userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Read-only mode' : blockAdvancedMode ? 'Switch to Basic Mode' : 'Switch to Advanced Mode'} @@ -750,7 +752,9 @@ export function WorkflowBlock({ id, data }: NodeProps) { {!userPermissions.canEdit - ? 'Read-only mode' + ? userPermissions.isOfflineMode + ? 'Connection lost - Please Refresh' + : 'Read-only mode' : isWide ? 'Narrow Block' : 'Expand Block'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx index 9ebbd4286e..806c29d161 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx @@ -1,6 +1,7 @@ 'use client' -import React, { createContext, useContext, useMemo } from 'react' +import type React from 'react' +import { createContext, useContext, useEffect, useMemo, useState } from 'react' import { useParams } from 'next/navigation' import { createLogger } from '@/lib/logs/console-logger' import { useUserPermissions, type WorkspaceUserPermissions } from '@/hooks/use-user-permissions' @@ -8,6 +9,7 @@ import { useWorkspacePermissions, type WorkspacePermissions, } from '@/hooks/use-workspace-permissions' +import { usePresence } from '../../[workflowId]/hooks/use-presence' const logger = createLogger('WorkspacePermissionsProvider') @@ -18,88 +20,153 @@ interface WorkspacePermissionsContextType { permissionsError: string | null updatePermissions: (newPermissions: WorkspacePermissions) => void - // Computed user permissions - userPermissions: WorkspaceUserPermissions + // Computed user permissions (connection-aware) + userPermissions: WorkspaceUserPermissions & { isOfflineMode?: boolean } + + // Connection state management + setOfflineMode: (isOffline: boolean) => void } -const WorkspacePermissionsContext = createContext(null) +const WorkspacePermissionsContext = createContext({ + workspacePermissions: null, + permissionsLoading: false, + permissionsError: null, + updatePermissions: () => {}, + userPermissions: { + canRead: false, + canEdit: false, + canAdmin: false, + userPermissions: 'read', + isLoading: false, + error: null, + }, + setOfflineMode: () => {}, +}) interface WorkspacePermissionsProviderProps { children: React.ReactNode } -const WorkspacePermissionsProvider = React.memo( - ({ children }) => { - const params = useParams() - const workspaceId = params.workspaceId as string - - if (!workspaceId) { - logger.warn('Workspace ID is undefined from params:', params) +/** + * Provider that manages workspace permissions and user access + * Also provides connection-aware permissions that enforce read-only mode when offline + */ +export function WorkspacePermissionsProvider({ children }: WorkspacePermissionsProviderProps) { + const params = useParams() + const workspaceId = params?.workspaceId as string + + // Manage offline mode state locally + const [isOfflineMode, setIsOfflineMode] = useState(false) + const [hasBeenConnected, setHasBeenConnected] = useState(false) + + // Fetch workspace permissions and loading state + const { + permissions: workspacePermissions, + loading: permissionsLoading, + error: permissionsError, + updatePermissions, + } = useWorkspacePermissions(workspaceId) + + // Get base user permissions from workspace permissions + const baseUserPermissions = useUserPermissions( + workspacePermissions, + permissionsLoading, + permissionsError + ) + + // Get connection status and update offline mode accordingly + const { isConnected } = usePresence() + + useEffect(() => { + if (isConnected) { + // Mark that we've been connected at least once + setHasBeenConnected(true) + // On initial connection, allow going online + if (!hasBeenConnected) { + setIsOfflineMode(false) + } + // If we were previously connected and this is a reconnection, stay offline (user must refresh) + } else if (hasBeenConnected) { + // Only enter offline mode if we were previously connected and now disconnected + setIsOfflineMode(true) + } + // If not connected and never been connected, stay in initial state (not offline mode) + }, [isConnected, hasBeenConnected]) + + // Create connection-aware permissions that override user permissions when offline + const userPermissions = useMemo((): WorkspaceUserPermissions & { isOfflineMode?: boolean } => { + if (isOfflineMode) { + // In offline mode, force read-only permissions regardless of actual user permissions + return { + ...baseUserPermissions, + canEdit: false, + canAdmin: false, + // Keep canRead true so users can still view content + canRead: baseUserPermissions.canRead, + isOfflineMode: true, + } } - const { - permissions: workspacePermissions, - loading: permissionsLoading, - error: permissionsError, - updatePermissions, - } = useWorkspacePermissions(workspaceId) + // When online, use normal permissions + return { + ...baseUserPermissions, + isOfflineMode: false, + } + }, [baseUserPermissions, isOfflineMode]) + + // Log permissions when they change + useMemo(() => { + if (workspacePermissions && !permissionsLoading) { + logger.info('Workspace permissions updated', { + workspaceId, + userCount: workspacePermissions.total, + isOfflineMode, + userCanEdit: userPermissions.canEdit, + userCanAdmin: userPermissions.canAdmin, + }) + } + }, [workspacePermissions, permissionsLoading, workspaceId, isOfflineMode, userPermissions]) - const userPermissions = useUserPermissions( + const contextValue = useMemo( + () => ({ workspacePermissions, permissionsLoading, - permissionsError - ) - - const contextValue = useMemo( - () => ({ - workspacePermissions, - permissionsLoading, - permissionsError, - updatePermissions, - userPermissions, - }), - [ - workspacePermissions, - permissionsLoading, - permissionsError, - updatePermissions, - userPermissions, - ] - ) - - return ( - - {children} - - ) - } -) - -WorkspacePermissionsProvider.displayName = 'WorkspacePermissionsProvider' - -export { WorkspacePermissionsProvider } + permissionsError, + updatePermissions, + userPermissions, + setOfflineMode: setIsOfflineMode, + }), + [workspacePermissions, permissionsLoading, permissionsError, updatePermissions, userPermissions] + ) + + return ( + + {children} + + ) +} /** - * Hook to access workspace permissions context - * This replaces individual useWorkspacePermissions calls to avoid duplicate API requests + * Hook to access workspace permissions and data from context + * This provides both raw workspace permissions and computed user permissions */ export function useWorkspacePermissionsContext(): WorkspacePermissionsContextType { const context = useContext(WorkspacePermissionsContext) - if (!context) { throw new Error( 'useWorkspacePermissionsContext must be used within a WorkspacePermissionsProvider' ) } - return context } /** * Hook to access user permissions from context - * This replaces individual useUserPermissions calls + * This replaces individual useUserPermissions calls and includes connection-aware permissions */ -export function useUserPermissionsContext(): WorkspaceUserPermissions { +export function useUserPermissionsContext(): WorkspaceUserPermissions & { + isOfflineMode?: boolean +} { const { userPermissions } = useWorkspacePermissionsContext() return userPermissions } diff --git a/apps/sim/contexts/socket-context.tsx b/apps/sim/contexts/socket-context.tsx index 9ebd490d87..12c37c707b 100644 --- a/apps/sim/contexts/socket-context.tsx +++ b/apps/sim/contexts/socket-context.tsx @@ -150,9 +150,9 @@ export function SocketProvider({ children, user }: SocketProviderProps) { const socketInstance = io(socketUrl, { transports: ['websocket', 'polling'], // Keep polling fallback for reliability withCredentials: true, - reconnectionAttempts: 5, // Socket.IO handles base reconnection + reconnectionAttempts: Number.POSITIVE_INFINITY, // Socket.IO handles base reconnection reconnectionDelay: 1000, // Start with 1 second delay - reconnectionDelayMax: 5000, // Max 5 second delay + reconnectionDelayMax: 30000, // Max 30 second delay timeout: 10000, // Back to original timeout auth: (cb) => { // Generate a fresh token for each connection attempt (including reconnections) From 62e39db3a5da2681cf2582e2caf59b376056cd2d Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 8 Jul 2025 20:05:08 -0700 Subject: [PATCH 2/5] remove unused hook --- .../components/connection-status/connection-status.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx index 153f554ec3..3c06a4ac63 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx @@ -56,9 +56,3 @@ export function ConnectionStatus({ isConnected }: ConnectionStatusProps) { ) } -// Hook to check if we're in offline mode (for use by other components) -// This now uses the workspace permissions context for consistent state -export function useConnectionStatus() { - const userPermissions = useUserPermissionsContext() - return { isOfflineMode: userPermissions.isOfflineMode } -} From 33a4c79dc725b224f389f66eac2dc9a9681cf788 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 8 Jul 2025 20:06:09 -0700 Subject: [PATCH 3/5] style --- .../components/connection-status/connection-status.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx index 3c06a4ac63..803cf813fe 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx @@ -55,4 +55,3 @@ export function ConnectionStatus({ isConnected }: ConnectionStatusProps) {
) } - From 005375b94fe0f9d02c7e956b76b3b82288c1776a Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 8 Jul 2025 20:07:56 -0700 Subject: [PATCH 4/5] update tooltip msg --- .../components/connection-status/connection-status.tsx | 2 +- .../w/[workflowId]/components/control-bar/control-bar.tsx | 6 +++--- .../toolbar/components/toolbar-block/toolbar-block.tsx | 2 +- .../components/toolbar-loop-block/toolbar-loop-block.tsx | 2 +- .../toolbar-parallel-block/toolbar-parallel-block.tsx | 2 +- .../workflow-block/components/action-bar/action-bar.tsx | 2 +- .../components/workflow-block/workflow-block.tsx | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx index 803cf813fe..90d4b5dc13 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/user-avatar-stack/components/connection-status/connection-status.tsx @@ -32,7 +32,7 @@ export function ConnectionStatus({ isConnected }: ConnectionStatusProps) {
- {isConnected ? 'Reconnected' : 'Connection lost - Please Refresh'} + {isConnected ? 'Reconnected' : 'Connection lost - please refresh'} {isConnected ? 'Refresh to continue editing' : 'Read-only mode active'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx index 96ae93eb54..18b60d4176 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx @@ -672,7 +672,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { {!canEdit && ( {userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Edit permissions required to rename workflows'} )} @@ -941,7 +941,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { {canEdit ? 'Duplicate Workflow' : userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Admin permission required to duplicate workflows'} @@ -984,7 +984,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { {!userPermissions.canEdit ? userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Admin permission required to use auto-layout' : 'Auto Layout'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx index b447d53f79..f868b84a0e 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-block/toolbar-block.tsx @@ -71,7 +71,7 @@ export function ToolbarBlock({ config, disabled = false }: ToolbarBlockProps) { {blockContent} {userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Edit permissions required to add blocks'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx index 991f18224e..767de4f406 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-loop-block/toolbar-loop-block.tsx @@ -79,7 +79,7 @@ export default function LoopToolbarItem({ disabled = false }: LoopToolbarItemPro {blockContent} {userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Edit permissions required to add blocks'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx index e1f8ddfc33..22e1bc397c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/toolbar/components/toolbar-parallel-block/toolbar-parallel-block.tsx @@ -79,7 +79,7 @@ export default function ParallelToolbarItem({ disabled = false }: ParallelToolba {blockContent} {userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Edit permissions required to add blocks'} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx index ddc4e2f0ea..df5d7c68ea 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/action-bar/action-bar.tsx @@ -29,7 +29,7 @@ export function ActionBar({ blockId, blockType, disabled = false }: ActionBarPro const getTooltipMessage = (defaultMessage: string) => { if (disabled) { - return userPermissions.isOfflineMode ? 'Connection lost - Please Refresh' : 'Read-only mode' + return userPermissions.isOfflineMode ? 'Connection lost - please refresh' : 'Read-only mode' } return defaultMessage } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index 167b406aa7..2a3cd42cb5 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -655,7 +655,7 @@ export function WorkflowBlock({ id, data }: NodeProps) { {!userPermissions.canEdit ? userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Read-only mode' : blockAdvancedMode ? 'Switch to Basic Mode' @@ -753,7 +753,7 @@ export function WorkflowBlock({ id, data }: NodeProps) { {!userPermissions.canEdit ? userPermissions.isOfflineMode - ? 'Connection lost - Please Refresh' + ? 'Connection lost - please refresh' : 'Read-only mode' : isWide ? 'Narrow Block' From 743c98239f3b60e15607885bb07c5a7b4ac0f643 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 8 Jul 2025 20:09:18 -0700 Subject: [PATCH 5/5] remove unnecessary useMemo around log --- .../providers/workspace-permissions-provider.tsx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx index 806c29d161..dc48846f8f 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/providers/workspace-permissions-provider.tsx @@ -114,19 +114,6 @@ export function WorkspacePermissionsProvider({ children }: WorkspacePermissionsP } }, [baseUserPermissions, isOfflineMode]) - // Log permissions when they change - useMemo(() => { - if (workspacePermissions && !permissionsLoading) { - logger.info('Workspace permissions updated', { - workspaceId, - userCount: workspacePermissions.total, - isOfflineMode, - userCanEdit: userPermissions.canEdit, - userCanAdmin: userPermissions.canAdmin, - }) - } - }, [workspacePermissions, permissionsLoading, workspaceId, isOfflineMode, userPermissions]) - const contextValue = useMemo( () => ({ workspacePermissions,