-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🪟 🐛 Fix loading state of "Launch" button in connection table #22002
Changes from 5 commits
ce33c10
84a6de6
c30da22
1f26340
ab0f9dc
5032bb0
5dcaf69
aeb9344
2b1d443
b1baaa3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
.inProgressLabel { | ||
height: 32px; /** Needs to match our Button height to avoid jumping around */ | ||
display: flex; | ||
align-items: center; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
import React from "react"; | ||
import { FormattedMessage } from "react-intl"; | ||
import { useAsyncFn } from "react-use"; | ||
import styled from "styled-components"; | ||
|
||
import { Button } from "components/ui/Button"; | ||
import { Switch } from "components/ui/Switch"; | ||
|
||
import { useEnableConnection } from "hooks/services/useConnectionHook"; | ||
import { useConnectionList, useEnableConnection, useSyncConnection } from "hooks/services/useConnectionHook"; | ||
|
||
import styles from "./StatusCellControl.module.scss"; | ||
|
||
interface StatusCellControlProps { | ||
allowSync?: boolean; | ||
|
@@ -15,30 +15,28 @@ interface StatusCellControlProps { | |
isSyncing?: boolean; | ||
isManual?: boolean; | ||
id: string; | ||
onSync: (id: string) => void; | ||
} | ||
|
||
const ProgressMessage = styled.div` | ||
padding: 7px 0; | ||
`; | ||
|
||
export const StatusCellControl: React.FC<StatusCellControlProps> = ({ | ||
enabled, | ||
isManual, | ||
id, | ||
isSyncing, | ||
onSync, | ||
allowSync, | ||
hasBreakingChange, | ||
}) => { | ||
const { connections } = useConnectionList(); | ||
const { mutateAsync: enableConnection, isLoading } = useEnableConnection(); | ||
const [{ loading }, OnLaunch] = useAsyncFn( | ||
async (event: React.SyntheticEvent) => { | ||
event.stopPropagation(); | ||
onSync(id); | ||
}, | ||
[id] | ||
); | ||
const { mutateAsync: syncConnection, isLoading: isSyncStarting } = useSyncConnection(); | ||
|
||
const onRunManualSync = (event: React.SyntheticEvent) => { | ||
event.stopPropagation(); | ||
|
||
const connection = connections.find((c) => c.connectionId === id); | ||
if (connection) { | ||
syncConnection(connection); | ||
} | ||
}; | ||
Comment on lines
+32
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function was basically constructed in three places before ( |
||
|
||
if (!isManual) { | ||
const onSwitchChange = async (event: React.SyntheticEvent) => { | ||
|
@@ -69,21 +67,20 @@ export const StatusCellControl: React.FC<StatusCellControlProps> = ({ | |
|
||
if (isSyncing) { | ||
return ( | ||
<ProgressMessage> | ||
<FormattedMessage id="tables.progress" /> | ||
</ProgressMessage> | ||
<div className={styles.inProgressLabel}> | ||
<FormattedMessage id="connection.syncInProgress" /> | ||
</div> | ||
); | ||
} | ||
|
||
return ( | ||
<Button | ||
size="xs" | ||
onClick={OnLaunch} | ||
isLoading={loading} | ||
disabled={!allowSync || !enabled || hasBreakingChange} | ||
onClick={onRunManualSync} | ||
isLoading={isSyncStarting} | ||
disabled={!allowSync || !enabled || hasBreakingChange || isSyncStarting} | ||
data-testid="manual-sync-button" | ||
> | ||
<FormattedMessage id="tables.launch" /> | ||
<FormattedMessage id="connection.startSync" /> | ||
</Button> | ||
); | ||
}; |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import { useCallback } from "react"; | ||
import { useIntl } from "react-intl"; | ||
import { useMutation, useQueryClient } from "react-query"; | ||
|
||
import { ToastType } from "components/ui/Toast"; | ||
|
||
import { Action, Namespace } from "core/analytics"; | ||
import { getFrequencyFromScheduleData } from "core/analytics/utils"; | ||
import { SyncSchema } from "core/domain/catalog"; | ||
|
@@ -9,6 +12,8 @@ import { ConnectionService } from "core/domain/connection/ConnectionService"; | |
import { useInitService } from "services/useInitService"; | ||
|
||
import { useAnalyticsService } from "./Analytics"; | ||
import { useAppMonitoringService } from "./AppMonitoringService"; | ||
import { useNotificationService } from "./Notification"; | ||
import { useCurrentWorkspace } from "./useWorkspace"; | ||
import { useConfig } from "../../config"; | ||
import { | ||
|
@@ -74,20 +79,43 @@ export function useConnectionService() { | |
|
||
export const useSyncConnection = () => { | ||
const service = useConnectionService(); | ||
const webConnectionService = useWebConnectionService(); | ||
const { trackError } = useAppMonitoringService(); | ||
const queryClient = useQueryClient(); | ||
const analyticsService = useAnalyticsService(); | ||
const notificationService = useNotificationService(); | ||
const workspaceId = useCurrentWorkspace(); | ||
const { formatMessage } = useIntl(); | ||
|
||
return useMutation((connection: WebBackendConnectionRead | WebBackendConnectionListItem) => { | ||
analyticsService.track(Namespace.CONNECTION, Action.SYNC, { | ||
actionDescription: "Manual triggered sync", | ||
connector_source: connection.source?.sourceName, | ||
connector_source_definition_id: connection.source?.sourceDefinitionId, | ||
connector_destination: connection.destination?.destinationName, | ||
connector_destination_definition_id: connection.destination?.destinationDefinitionId, | ||
frequency: getFrequencyFromScheduleData(connection.scheduleData), | ||
}); | ||
|
||
return service.sync(connection.connectionId); | ||
}); | ||
return useMutation( | ||
(connection: WebBackendConnectionRead | WebBackendConnectionListItem) => { | ||
analyticsService.track(Namespace.CONNECTION, Action.SYNC, { | ||
actionDescription: "Manual triggered sync", | ||
connector_source: connection.source?.sourceName, | ||
connector_source_definition_id: connection.source?.sourceDefinitionId, | ||
connector_destination: connection.destination?.destinationName, | ||
connector_destination_definition_id: connection.destination?.destinationDefinitionId, | ||
frequency: getFrequencyFromScheduleData(connection.scheduleData), | ||
}); | ||
|
||
return service.sync(connection.connectionId); | ||
}, | ||
{ | ||
onError: (error: Error) => { | ||
trackError(error); | ||
notificationService.registerNotification({ | ||
id: `tables.startSyncError.${error.message}`, | ||
text: `${formatMessage({ id: "connection.startSyncError" })}: ${error.message}`, | ||
type: ToastType.ERROR, | ||
}); | ||
}, | ||
onSuccess: async () => { | ||
webConnectionService | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may need to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point! fixed ✅ 5dcaf69 |
||
.list(workspaceId) | ||
.then((updatedConnections) => queryClient.setQueryData(connectionsKeys.lists(), updatedConnections)); | ||
}, | ||
} | ||
); | ||
}; | ||
|
||
export const useResetConnection = () => { | ||
|
@@ -239,9 +267,12 @@ export const useRemoveConnectionsFromList = (): ((connectionIds: string[]) => vo | |
const useConnectionList = (payload: Pick<WebBackendConnectionListRequestBody, "destinationId" | "sourceId"> = {}) => { | ||
const workspace = useCurrentWorkspace(); | ||
const service = useWebConnectionService(); | ||
const REFETCH_CONNECTION_LIST_INTERVAL = 60_000; | ||
|
||
return useSuspenseQuery(connectionsKeys.lists(payload.destinationId ?? payload.sourceId), () => | ||
service.list({ ...payload, workspaceId: workspace.workspaceId }) | ||
return useSuspenseQuery( | ||
connectionsKeys.lists(payload.destinationId ?? payload.sourceId), | ||
() => service.list({ ...payload, workspaceId: workspace.workspaceId }), | ||
{ refetchInterval: REFETCH_CONNECTION_LIST_INTERVAL } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re-fetching the connections every 60 seconds lets us show updated progress in the connection table. That way syncs don't stay "pending" until you refresh the page. p99 latency for this endpoint is under 100ms, so I don't think it's a performance problem. |
||
); | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make this a variable? I've also had another occasion where I needed the button height
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I didn't is that IMO we should not really need to pass around the height of the button. The table row should have an appropriate (larger) height so that it's not reflowing depending on the content. But of course that's a little out of scope. On second thought, a variable is at least better than a comment like this, so I'll add the button heights to
_variables.scss
aeb9344