Skip to content

Commit e8edac7

Browse files
Merge pull request #1352 from digma-ai/feature/recent-activity-rest
Add plugin API
2 parents 60e12b9 + d583069 commit e8edac7

File tree

8 files changed

+167
-48
lines changed

8 files changed

+167
-48
lines changed

apps.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export const appData: AppData = {
5454
// },
5555
["recent-activity"]: {
5656
entry: path.resolve(__dirname, "./src/containers/RecentActivity/index.tsx"),
57-
environmentVariables: ["recentActivityExpirationLimit"],
5857
platforms: ["jetbrains"]
5958
},
6059
troubleshooting: {

src/components/RecentActivity/RecentActivityTable/index.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type {
1313
SlimEntrySpanData
1414
} from "../../../redux/services/types";
1515
import { useConfigSelector } from "../../../store/config/useConfigSelector";
16-
import { isNumber } from "../../../typeGuards/isNumber";
1716
import { FeatureFlag } from "../../../types";
1817
import { formatTimeDistance } from "../../../utils/formatTimeDistance";
1918
import { getDurationString } from "../../../utils/getDurationString";
@@ -30,11 +29,9 @@ import * as s from "./styles";
3029
import type { ColumnMeta, RecentActivityTableProps } from "./types";
3130

3231
const columnHelper = createColumnHelper<RecentActivityEntry>();
32+
export const MAX_DISTANCE = 10 * 60 * 1000; // in milliseconds
3333

34-
export const isRecent = (entry: RecentActivityEntry): boolean => {
35-
const MAX_DISTANCE = isNumber(window.recentActivityExpirationLimit)
36-
? window.recentActivityExpirationLimit
37-
: 10 * 60 * 1000; // in milliseconds
34+
const isRecent = (entry: RecentActivityEntry): boolean => {
3835
const now = new Date();
3936
return (
4037
now.valueOf() - new Date(entry.latestTraceTimestamp).valueOf() <=

src/components/RecentActivity/index.tsx

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { dispatcher } from "../../dispatcher";
77
import { usePersistence } from "../../hooks/usePersistence";
88
import { usePrevious } from "../../hooks/usePrevious";
99
import { useDeleteEnvironmentMutation } from "../../redux/services/digma";
10+
import { useToggleRecentIndicatorMutation } from "../../redux/services/plugin";
1011
import type {
1112
Environment,
1213
SlimEntrySpanData
@@ -34,7 +35,7 @@ import type { ViewMode } from "./EnvironmentPanel/types";
3435
import { LiveView } from "./LiveView";
3536
import { NoData } from "./NoData";
3637
import { ObservabilityStatusBadge } from "./ObservabilityStatusBadge";
37-
import { RecentActivityTable, isRecent } from "./RecentActivityTable";
38+
import { MAX_DISTANCE, RecentActivityTable } from "./RecentActivityTable";
3839
import { Toggle } from "./Toggle";
3940
import { WelcomeScreen } from "./WelcomeScreen";
4041
import { actions } from "./actions";
@@ -96,8 +97,9 @@ export const RecentActivity = () => {
9697
const [environmentToDelete, setEnvironmentToDelete] = useState<string>();
9798
const [environmentToClearData, setEnvironmentToClearData] =
9899
useState<string>();
99-
const { recentActivityData: data } = useRecentActivityData();
100+
const recentActivityData = useRecentActivityData(selectedEnvironment?.id);
100101
const [deleteEnvironment] = useDeleteEnvironmentMutation();
102+
const [toggleRecentIndicator] = useToggleRecentIndicatorMutation();
101103
const isEnvironmentConfirmationDialogVisible = Boolean(
102104
environmentToDelete ?? environmentToClearData
103105
);
@@ -137,37 +139,50 @@ export const RecentActivity = () => {
137139

138140
const filteredEntries = useMemo(
139141
() =>
140-
data.entries.filter((entry) => {
142+
recentActivityData.entries?.filter((entry) => {
141143
const clearDataTimestamp =
142144
persistedEnvironmentClearDataTimestamps?.[entry.environment];
143145

144146
return clearDataTimestamp
145147
? new Date(entry.latestTraceTimestamp).valueOf() >
146148
new Date(clearDataTimestamp).valueOf()
147149
: true;
148-
}),
149-
[data, persistedEnvironmentClearDataTimestamps]
150+
}) ?? [],
151+
[recentActivityData.entries, persistedEnvironmentClearDataTimestamps]
150152
);
151153

152154
const environmentActivities = useMemo(
153-
() =>
154-
filteredEntries ? groupBy(filteredEntries, (x) => x.environment) : {},
155+
() => groupBy(filteredEntries, (x) => x.environment),
155156
[filteredEntries]
156157
);
157158

158-
const environments: ExtendedEnvironment[] = useMemo(
159-
() =>
160-
data.environments.map((environment) => ({
159+
const environments: ExtendedEnvironment[] = useMemo(() => {
160+
const now = new Date();
161+
162+
return (
163+
recentActivityData.environments?.map((environment) => ({
161164
...environment,
162165
additionToConfigResult: null,
163166
serverApiUrl: null,
164167
isOrgDigmaSetupFinished: false,
165-
hasRecentActivity: environmentActivities[environment.id]
166-
? environmentActivities[environment.id].some(isRecent)
167-
: false
168-
})),
169-
[data, environmentActivities]
170-
);
168+
hasRecentActivity: Boolean(
169+
environment.lastActive &&
170+
now.valueOf() - new Date(environment.lastActive).valueOf() <=
171+
MAX_DISTANCE
172+
)
173+
})) ?? []
174+
);
175+
}, [recentActivityData.environments]);
176+
177+
useEffect(() => {
178+
const isAnyRecentActivity = environments.some(
179+
(environment) => environment.hasRecentActivity
180+
);
181+
182+
void toggleRecentIndicator({
183+
status: isAnyRecentActivity
184+
});
185+
}, [environments, toggleRecentIndicator]);
171186

172187
useEffect(() => {
173188
if (selectedEnvironment?.id) {
@@ -448,6 +463,22 @@ export const RecentActivity = () => {
448463
);
449464
}
450465

466+
if (
467+
recentActivityData.areEntriesLoading &&
468+
recentActivityData.entries === undefined
469+
) {
470+
return (
471+
<>
472+
<s.NoDataRecentActivityContainerBackground>
473+
<s.NoDataRecentActivityContainerBackgroundGradient />
474+
</s.NoDataRecentActivityContainerBackground>
475+
<s.LoadingContainer>
476+
<s.Spinner size={32} />
477+
</s.LoadingContainer>
478+
</>
479+
);
480+
}
481+
451482
if (
452483
!selectedEnvironment ||
453484
!environmentActivities[selectedEnvironment.id]

src/components/RecentActivity/styles.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import styled from "styled-components";
2+
import { Spinner as CommonSpinner } from "../common/v3/Spinner";
23

34
const RECENT_ACTIVITY_NO_DATA_PADDING_TOP = 39; // in pixels
45
const RECENT_ACTIVITY_MIN_WIDTH = 550; // in pixels
@@ -126,6 +127,17 @@ export const RecentActivityContainerBackgroundGradient = styled.div`
126127
filter: blur(5px);
127128
`;
128129

130+
export const LoadingContainer = styled.div`
131+
display: flex;
132+
align-items: center;
133+
justify-content: center;
134+
padding-top: 60px;
135+
`;
136+
137+
export const Spinner = styled(CommonSpinner)`
138+
color: ${({ theme }) => theme.colors.v3.surface.gray};
139+
`;
140+
129141
export const ClearDataMessageContainer = styled.div`
130142
display: flex;
131143
flex-direction: column;
Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,84 @@
1-
import { useMemo } from "react";
1+
import { useEffect, useState } from "react";
22
import {
33
useGetEnvironmentsQuery,
44
useGetRecentActivityQuery
55
} from "../../redux/services/digma";
6+
import type {
7+
Environment,
8+
RecentActivityEntry
9+
} from "../../redux/services/types";
610

711
const REFRESH_INTERVAL = 10 * 1000; // in milliseconds
812

9-
export const useRecentActivityData = () => {
10-
const { data: environments } = useGetEnvironmentsQuery(undefined, {
11-
pollingInterval: REFRESH_INTERVAL
13+
interface RecentActivityData {
14+
environments: Environment[] | undefined;
15+
entries: RecentActivityEntry[] | undefined;
16+
areEnvironmentsLoading: boolean;
17+
areEntriesLoading: boolean;
18+
}
19+
20+
export const useRecentActivityData = (environmentId: string | undefined) => {
21+
const [data, setData] = useState<RecentActivityData>({
22+
environments: undefined,
23+
entries: undefined,
24+
areEnvironmentsLoading: false,
25+
areEntriesLoading: false
1226
});
13-
const { data: recentActivityData } = useGetRecentActivityQuery(
14-
{
15-
environments: environments?.map((env) => env.id) ?? []
16-
},
17-
{
18-
skip: !environments || environments.length === 0,
27+
28+
const { data: environments, isFetching: areEnvironmentsFetching } =
29+
useGetEnvironmentsQuery(undefined, {
1930
pollingInterval: REFRESH_INTERVAL
31+
});
32+
33+
const { data: recentActivityData, isFetching: isRecentActivityDataFetching } =
34+
useGetRecentActivityQuery(
35+
{
36+
environments: environmentId ? [environmentId] : []
37+
},
38+
{
39+
skip: !environments || environments.length === 0 || !environmentId,
40+
pollingInterval: REFRESH_INTERVAL
41+
}
42+
);
43+
44+
useEffect(() => {
45+
if (environmentId) {
46+
setData((prevData) => ({
47+
...prevData,
48+
entries: undefined
49+
}));
50+
}
51+
}, [environmentId]);
52+
53+
useEffect(() => {
54+
setData((prevData) => ({
55+
...prevData,
56+
areEnvironmentsLoading: areEnvironmentsFetching
57+
}));
58+
}, [areEnvironmentsFetching]);
59+
60+
useEffect(() => {
61+
setData((prevData) => ({
62+
...prevData,
63+
areEntriesLoading: isRecentActivityDataFetching
64+
}));
65+
}, [isRecentActivityDataFetching]);
66+
67+
useEffect(() => {
68+
if (environments) {
69+
setData((prevData) => ({
70+
...prevData,
71+
environments
72+
}));
2073
}
21-
);
22-
23-
const data = useMemo(
24-
() => ({
25-
environments: environments ?? [],
26-
entries: recentActivityData?.entries ?? []
27-
}),
28-
[environments, recentActivityData]
29-
);
30-
31-
return {
32-
recentActivityData: data
33-
};
74+
}, [environments]);
75+
76+
useEffect(() => {
77+
setData((prevData) => ({
78+
...prevData,
79+
entries: recentActivityData?.entries
80+
}));
81+
}, [recentActivityData]);
82+
83+
return data;
3484
};

src/containers/RecentActivity/store.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { configureStore } from "@reduxjs/toolkit";
22
import { setupListeners } from "@reduxjs/toolkit/query";
33
import { digmaApi } from "../../redux/services/digma";
4+
import { pluginApi } from "../../redux/services/plugin";
45

56
export const store = configureStore({
67
reducer: {
7-
[digmaApi.reducerPath]: digmaApi.reducer
8+
[digmaApi.reducerPath]: digmaApi.reducer,
9+
[pluginApi.reducerPath]: pluginApi.reducer
810
},
911
middleware: (getDefaultMiddleware) =>
10-
getDefaultMiddleware().concat(digmaApi.middleware)
12+
getDefaultMiddleware().concat(digmaApi.middleware, pluginApi.middleware)
1113
});
1214

1315
setupListeners(store.dispatch);

src/globals.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ declare global {
5858
documentationPage?: unknown;
5959
initialRoutePath?: unknown;
6060
notificationsViewMode?: unknown;
61-
recentActivityExpirationLimit?: unknown;
6261
wizardSkipInstallationStep?: unknown;
6362
wizardFirstLaunch?: unknown;
6463
productKey?: unknown;

src/redux/services/plugin.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
2+
3+
export interface ToggleRecentIndicatorPayload {
4+
status: boolean;
5+
}
6+
7+
export const pluginApi = createApi({
8+
reducerPath: "pluginApi",
9+
baseQuery: fetchBaseQuery({
10+
baseUrl: "/plugin-api",
11+
credentials: "same-origin"
12+
}),
13+
endpoints: (builder) => ({
14+
toggleRecentIndicator: builder.mutation<void, ToggleRecentIndicatorPayload>(
15+
{
16+
query: (data) => ({
17+
url: "",
18+
params: {
19+
pluginCommand: "RecentActivityBadge"
20+
},
21+
method: "PUT",
22+
body: data
23+
})
24+
}
25+
)
26+
})
27+
});
28+
29+
export const { useToggleRecentIndicatorMutation } = pluginApi;

0 commit comments

Comments
 (0)