diff --git a/changelog/unreleased/issue-25517.toml b/changelog/unreleased/issue-25517.toml
new file mode 100644
index 000000000000..ac588c8c6405
--- /dev/null
+++ b/changelog/unreleased/issue-25517.toml
@@ -0,0 +1,5 @@
+type = "f"
+message = "Fix issue when default favorite fields disappear after adding favorite field."
+
+pulls = ["25530"]
+issues = ["25517"]
diff --git a/graylog2-web-interface/src/components/common/message/details/context/MessageFavoriteFieldsProvider.tsx b/graylog2-web-interface/src/components/common/message/details/context/MessageFavoriteFieldsProvider.tsx
index 4af4c9644329..0b55ace1bdef 100644
--- a/graylog2-web-interface/src/components/common/message/details/context/MessageFavoriteFieldsProvider.tsx
+++ b/graylog2-web-interface/src/components/common/message/details/context/MessageFavoriteFieldsProvider.tsx
@@ -28,9 +28,8 @@ import { StreamsStore } from 'views/stores/StreamsStore';
import type { Stream } from 'logic/streams/types';
import { isPermitted } from 'util/PermissionsMixin';
import useCurrentUser from 'hooks/useCurrentUser';
-
-import { DEFAULT_FIELDS } from '../fields/hooks/useMessageFavoriteFieldsForEditing';
-import useMessageFavoriteFieldsMutation from '../fields/hooks/useMessageFavoriteFieldsMutation';
+import { getStreamFavoriteFields } from 'components/common/message/helpers';
+import useMessageFavoriteFieldsMutation from 'components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation';
type OriginalProps = React.PropsWithChildren<{
message: Message;
@@ -51,18 +50,28 @@ const OriginalMessageFavoriteFieldsProvider = ({ children = null, message, messa
return messageStreamIds.map((id) => streamsById?.[id]).filter((s) => !!s);
}, [message?.fields?.streams, permissions, streamsList]);
+ const initialFavoriteFieldsByStream = useMemo(
+ () => Object.fromEntries(streams.map((stream) => [stream.id, getStreamFavoriteFields(stream, message?.fields)])),
+ [message?.fields, streams],
+ );
+
const initialFavoriteFields = useMemo(
- () => uniq(flattenDeep(zip(streams.map((stream) => stream?.favorite_fields ?? DEFAULT_FIELDS)))),
- [streams],
+ () => uniq(flattenDeep(zip(Object.values(initialFavoriteFieldsByStream)))),
+ [initialFavoriteFieldsByStream],
);
const editableStreams = useMemo(
- () => streams.filter((stream: Stream) => isPermitted(permissions, `streams:edit:${stream.id}`)),
+ () => streams.filter((stream) => isPermitted(permissions, `streams:edit:${stream.id}`)),
[permissions, streams],
);
+ const editableStreamsInitialFavoriteFields = useMemo(
+ () => Object.fromEntries(editableStreams.map(({ id }) => [id, initialFavoriteFieldsByStream[id]])),
+ [editableStreams, initialFavoriteFieldsByStream],
+ );
+
const { saveFavoriteField, toggleField, setFieldsIsPending } = useMessageFavoriteFieldsMutation(
- editableStreams,
+ editableStreamsInitialFavoriteFields,
initialFavoriteFields,
);
@@ -75,6 +84,7 @@ const OriginalMessageFavoriteFieldsProvider = ({ children = null, message, messa
toggleField,
editableStreams,
setFieldsIsPending,
+ initialFavoriteFieldsByStream,
}),
[
initialFavoriteFields,
@@ -82,8 +92,9 @@ const OriginalMessageFavoriteFieldsProvider = ({ children = null, message, messa
messageFields,
message,
toggleField,
- editableStreams,
setFieldsIsPending,
+ initialFavoriteFieldsByStream,
+ editableStreams,
],
);
diff --git a/graylog2-web-interface/src/components/common/message/details/fields/MessageFieldsEditModal.tsx b/graylog2-web-interface/src/components/common/message/details/fields/MessageFieldsEditModal.tsx
index 8bf5aa57f955..58638b24047d 100644
--- a/graylog2-web-interface/src/components/common/message/details/fields/MessageFieldsEditModal.tsx
+++ b/graylog2-web-interface/src/components/common/message/details/fields/MessageFieldsEditModal.tsx
@@ -25,8 +25,9 @@ import { StreamsStore } from 'views/stores/StreamsStore';
import MessageFavoriteFieldsContext from 'views/components/contexts/MessageFavoriteFieldsContext';
import StringUtils from 'util/StringUtils';
import { ModalSubmit } from 'components/common';
+import { DEFAULT_FIELDS } from 'components/common/message/helpers';
-import useMessageFavoriteFieldsForEditing, { DEFAULT_FIELDS } from './hooks/useMessageFavoriteFieldsForEditing';
+import useMessageFavoriteFieldsForEditing from './hooks/useMessageFavoriteFieldsForEditing';
import MessageFieldsEditModeLists from './MessageFieldsEditModeLists';
import useSendFavoriteFieldTelemetry from './hooks/useSendFavoriteFieldTelemetry';
diff --git a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.test.tsx b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.test.tsx
index b99291095f82..95a820b5f422 100644
--- a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.test.tsx
+++ b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.test.tsx
@@ -47,6 +47,9 @@ const createWrapper =
message: undefined,
editableStreams: [],
setFieldsIsPending: false,
+ initialFavoriteFieldsByStream: {
+ streamId: initialFavorites,
+ },
}}>
{children}
diff --git a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.ts b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.ts
index 3062e7209bc7..a9ff9f18f5ff 100644
--- a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.ts
+++ b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsForEditing.ts
@@ -18,13 +18,11 @@ import { useState, useCallback, useContext } from 'react';
import uniq from 'lodash/uniq';
import MessageFavoriteFieldsContext from 'views/components/contexts/MessageFavoriteFieldsContext';
+import type { FormattedField } from 'components/common/message/details/fields/types';
+import { DEFAULT_FIELDS } from 'components/common/message/helpers';
import useSendFavoriteFieldTelemetry from './useSendFavoriteFieldTelemetry';
-import type { FormattedField } from '../types';
-
-export const DEFAULT_FIELDS = ['source', 'destination_ip', 'usernames'];
-
const useMessageFavoriteFieldsForEditing = () => {
const sendFavoriteFieldTelemetry = useSendFavoriteFieldTelemetry();
const { saveFavoriteField, favoriteFields: initialFavoriteFields } = useContext(MessageFavoriteFieldsContext);
diff --git a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.test.tsx b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.test.tsx
index 34b39e5f2a32..53ffe992dc5f 100644
--- a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.test.tsx
+++ b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.test.tsx
@@ -62,7 +62,14 @@ const queryClient = new QueryClient({
const wrapper = ({ children }) => {children};
const renderTestHook = (streams: Array, initialFavoriteFields: Array) =>
- renderHook(() => useMessageFavoriteFieldsMutation(streams, initialFavoriteFields), { wrapper });
+ renderHook(
+ () =>
+ useMessageFavoriteFieldsMutation(
+ Object.fromEntries(streams.map((stream) => [stream.id, stream.favorite_fields])),
+ initialFavoriteFields,
+ ),
+ { wrapper },
+ );
describe('useMessageFavoriteFieldsMutation', () => {
const streams: Array = [
{
diff --git a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.ts b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.ts
index d7e87c91e97e..d9fd1fa50f8c 100644
--- a/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.ts
+++ b/graylog2-web-interface/src/components/common/message/details/fields/hooks/useMessageFavoriteFieldsMutation.ts
@@ -16,14 +16,13 @@
*/
import { useCallback } from 'react';
import { useMutation } from '@tanstack/react-query';
+import mapValues from 'lodash/mapValues';
import { FavoriteFields } from '@graylog/server-api';
import { StreamsActions } from 'views/stores/StreamsStore';
import UserNotification from 'util/UserNotification';
-import type { Stream } from 'logic/streams/types';
-
-import useSendFavoriteFieldTelemetry from './useSendFavoriteFieldTelemetry';
+import useSendFavoriteFieldTelemetry from 'components/common/message/details/fields/hooks/useSendFavoriteFieldTelemetry';
interface FavoriteFieldRequest {
readonly field: string;
@@ -36,7 +35,10 @@ interface SetFavoriteFieldsRequest {
};
}
-const useMessageFavoriteFieldsMutation = (streams: Array, initialFavoriteFields: Array) => {
+const useMessageFavoriteFieldsMutation = (
+ initialFavoriteFieldsByStream: Record>,
+ initialFavoriteFields: Array,
+) => {
const sendFavoriteFieldTelemetry = useSendFavoriteFieldTelemetry();
const { isPending: setFieldsIsPending, mutate: setFavoriteFields } = useMutation({
mutationFn: (props: SetFavoriteFieldsRequest) => FavoriteFields.set(props),
@@ -90,26 +92,19 @@ const useMessageFavoriteFieldsMutation = (streams: Array, initialFavorit
(favoritesToSave: Array) => {
const newAddedFields = favoritesToSave.filter((f) => !initialFavoriteFields.includes(f));
- const newFavoriteFieldsByStream = Object.fromEntries(
- streams.map((stream) => [
- stream.id,
- favoritesToSave.filter((f) => {
- const streamFavoriteFields = stream?.favorite_fields ?? [];
-
- return streamFavoriteFields.includes(f) || newAddedFields.includes(f);
- }),
- ]),
+ const newFavoriteFieldsByStream = mapValues(initialFavoriteFieldsByStream, (streamFavoriteFields) =>
+ favoritesToSave.filter((f) => streamFavoriteFields.includes(f) || newAddedFields.includes(f)),
);
setFavoriteFields({ fields: newFavoriteFieldsByStream });
},
- [initialFavoriteFields, setFavoriteFields, streams],
+ [initialFavoriteFields, initialFavoriteFieldsByStream, setFavoriteFields],
);
const toggleField = useCallback(
(field: string) => {
const isFavorite = initialFavoriteFields?.includes(field);
- const streamIds = streams.map((stream) => stream.id);
+ const streamIds = Object.keys(initialFavoriteFieldsByStream);
if (isFavorite) {
removeFavoriteField({ field, stream_ids: streamIds });
@@ -117,7 +112,7 @@ const useMessageFavoriteFieldsMutation = (streams: Array, initialFavorit
addFavoriteField({ field, stream_ids: streamIds });
}
},
- [addFavoriteField, initialFavoriteFields, removeFavoriteField, streams],
+ [addFavoriteField, initialFavoriteFields, initialFavoriteFieldsByStream, removeFavoriteField],
);
return {
diff --git a/graylog2-web-interface/src/components/common/message/helpers.ts b/graylog2-web-interface/src/components/common/message/helpers.ts
new file mode 100644
index 000000000000..faa1ea83cbb1
--- /dev/null
+++ b/graylog2-web-interface/src/components/common/message/helpers.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 Graylog, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * .
+ */
+import type { Stream } from 'logic/streams/types';
+import type { Message } from 'views/components/messagelist/Types';
+
+export const DEFAULT_FIELDS = ['source', 'destination_ip', 'usernames'];
+
+export const getStreamFavoriteFields = (stream: Stream, fields: Message['fields'] = {}) =>
+ stream?.favorite_fields ?? DEFAULT_FIELDS.filter((field) => Object.hasOwn(fields, field));
diff --git a/graylog2-web-interface/src/views/components/contexts/MessageFavoriteFieldsContext.ts b/graylog2-web-interface/src/views/components/contexts/MessageFavoriteFieldsContext.ts
index 7a0db2a7c7ae..5afeed0e8c99 100644
--- a/graylog2-web-interface/src/views/components/contexts/MessageFavoriteFieldsContext.ts
+++ b/graylog2-web-interface/src/views/components/contexts/MessageFavoriteFieldsContext.ts
@@ -31,6 +31,7 @@ export type MessageFavoriteFieldsContextState = {
message: Message;
editableStreams: Array;
setFieldsIsPending: boolean;
+ initialFavoriteFieldsByStream: Record>;
};
const MessageFavoriteFieldsContext = React.createContext({
@@ -41,6 +42,7 @@ const MessageFavoriteFieldsContext = React.createContext MessageFavoriteFieldsContext);