mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
console: notifications bug fixes (#6067)
https://github.com/hasura/graphql-engine/pull/6067
This commit is contained in:
parent
4e4e3f3fd3
commit
31d07cb976
@ -20,8 +20,9 @@ const Endpoints = {
|
||||
hasuractlMetadata: `${hasuractlUrl}/apis/metadata`,
|
||||
hasuractlMigrateSettings: `${hasuractlUrl}/apis/migrate/settings`,
|
||||
telemetryServer: 'wss://telemetry.hasura.io/v1/ws',
|
||||
consoleNotificationsStg: 'https://data.hasura-stg.hasura-app.io/v1/query',
|
||||
consoleNotificationsProd: 'https://data.hasura.io/v1/query',
|
||||
consoleNotificationsStg:
|
||||
'https://notifications.hasura-stg.hasura-app.io/v1/graphql',
|
||||
consoleNotificationsProd: 'https://notifications.hasura.io/v1/graphql',
|
||||
};
|
||||
|
||||
const globalCookiePolicy = 'same-origin';
|
||||
|
@ -795,47 +795,34 @@ export const getConsoleNotificationQuery = (
|
||||
time: Date | string | number,
|
||||
userType?: Nullable<ConsoleScope>
|
||||
) => {
|
||||
let consoleUserScope = {
|
||||
$ilike: `%${userType}%`,
|
||||
};
|
||||
let consoleUserScopeVar = `%${userType}%`;
|
||||
if (!userType) {
|
||||
consoleUserScope = {
|
||||
$ilike: '%OSS%',
|
||||
};
|
||||
consoleUserScopeVar = '%OSS%';
|
||||
}
|
||||
|
||||
return {
|
||||
args: {
|
||||
table: 'console_notification',
|
||||
columns: ['*'],
|
||||
where: {
|
||||
$or: [
|
||||
{
|
||||
expiry_date: {
|
||||
$gte: time,
|
||||
},
|
||||
},
|
||||
{
|
||||
expiry_date: {
|
||||
$eq: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
scope: consoleUserScope,
|
||||
start_date: { $lte: time },
|
||||
},
|
||||
order_by: [
|
||||
{
|
||||
type: 'asc',
|
||||
nulls: 'last',
|
||||
column: 'priority',
|
||||
},
|
||||
{
|
||||
type: 'desc',
|
||||
column: 'start_date',
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'select',
|
||||
const query = `query fetchNotifications($currentTime: timestamptz, $userScope: String) {
|
||||
console_notifications(
|
||||
where: {start_date: {_lte: $currentTime}, scope: {_ilike: $userScope}, _or: [{expiry_date: {_gte: $currentTime}}, {expiry_date: {_eq: null}}]},
|
||||
order_by: {priority: asc_nulls_last, start_date: desc}
|
||||
) {
|
||||
content
|
||||
created_at
|
||||
external_link
|
||||
expiry_date
|
||||
id
|
||||
is_active
|
||||
priority
|
||||
scope
|
||||
start_date
|
||||
subject
|
||||
type
|
||||
}
|
||||
}`;
|
||||
|
||||
const variables = {
|
||||
userScope: consoleUserScopeVar,
|
||||
currentTime: time,
|
||||
};
|
||||
|
||||
return { query, variables };
|
||||
};
|
||||
|
@ -79,8 +79,12 @@ const fetchConsoleNotifications = () => (dispatch, getState) => {
|
||||
const consoleId = window.__env.consoleId;
|
||||
const consoleScope = getConsoleScope(serverVersion, consoleId);
|
||||
let userType = 'admin';
|
||||
if (headers.hasOwnProperty(HASURA_COLLABORATOR_TOKEN)) {
|
||||
const collabToken = headers[HASURA_COLLABORATOR_TOKEN];
|
||||
const headerHasCollabToken = Object.keys(headers).find(
|
||||
header => header.toLowerCase() === HASURA_COLLABORATOR_TOKEN
|
||||
);
|
||||
|
||||
if (headerHasCollabToken) {
|
||||
const collabToken = headers[headerHasCollabToken];
|
||||
userType = getUserType(collabToken);
|
||||
}
|
||||
|
||||
@ -94,12 +98,14 @@ const fetchConsoleNotifications = () => (dispatch, getState) => {
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const query = getConsoleNotificationQuery(now, consoleScope);
|
||||
const payload = getConsoleNotificationQuery(now, consoleScope);
|
||||
const options = {
|
||||
body: JSON.stringify(query),
|
||||
body: JSON.stringify(payload),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
// temp. change until Auth is added
|
||||
'x-hasura-role': 'user',
|
||||
},
|
||||
};
|
||||
|
||||
@ -108,94 +114,131 @@ const fetchConsoleNotifications = () => (dispatch, getState) => {
|
||||
const lastSeenNotifications = JSON.parse(
|
||||
window.localStorage.getItem('notifications:lastSeen')
|
||||
);
|
||||
if (!data.length) {
|
||||
dispatch({ type: FETCH_CONSOLE_NOTIFICATIONS_SET_DEFAULT });
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: 'default',
|
||||
date: now,
|
||||
showBadge: false,
|
||||
})
|
||||
);
|
||||
if (!lastSeenNotifications) {
|
||||
if (data.data.console_notifications) {
|
||||
const fetchedData = data.data.console_notifications;
|
||||
|
||||
if (!fetchedData.length) {
|
||||
dispatch({ type: FETCH_CONSOLE_NOTIFICATIONS_SET_DEFAULT });
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: 'default',
|
||||
date: now,
|
||||
showBadge: false,
|
||||
})
|
||||
);
|
||||
if (!lastSeenNotifications) {
|
||||
window.localStorage.setItem(
|
||||
'notifications:lastSeen',
|
||||
JSON.stringify(0)
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: these 2 steps may not be required if the table in the DB
|
||||
// enforces the usage of `enums` and we're sure that the notification scope
|
||||
// is only from the allowed permutations of scope. We aren't doing that yet
|
||||
// because within the GQL query, I can't be using the `_ilike` operator during
|
||||
// filtering. Hence I'm keeping it here since this is a new feature and
|
||||
// mistakes can happen while adding data into the DB.
|
||||
|
||||
// TODO: is to remove these once things are more streamlined
|
||||
const uppercaseScopedData = makeUppercaseScopes(fetchedData);
|
||||
let filteredData = filterScope(uppercaseScopedData, consoleScope);
|
||||
|
||||
if (
|
||||
lastSeenNotifications &&
|
||||
lastSeenNotifications > filteredData.length
|
||||
) {
|
||||
window.localStorage.setItem(
|
||||
'notifications:lastSeen',
|
||||
JSON.stringify(0)
|
||||
JSON.stringify(filteredData.length)
|
||||
);
|
||||
}
|
||||
|
||||
if (previousRead) {
|
||||
if (!consoleStateDB.console_notifications) {
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: [],
|
||||
date: now,
|
||||
showBadge: true,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
let newReadValue;
|
||||
if (previousRead === 'default' || previousRead === 'error') {
|
||||
newReadValue = [];
|
||||
toShowBadge = false;
|
||||
} else if (previousRead === 'all') {
|
||||
const previousList = JSON.parse(
|
||||
localStorage.getItem('notifications:data')
|
||||
);
|
||||
if (!previousList) {
|
||||
// we don't have a record of the IDs that were marked as read previously
|
||||
newReadValue = [];
|
||||
toShowBadge = true;
|
||||
} else if (previousList.length) {
|
||||
const readNotificationsDiff = filteredData.filter(
|
||||
newNotif =>
|
||||
!previousList.find(oldNotif => oldNotif.id === newNotif.id)
|
||||
);
|
||||
if (!readNotificationsDiff.length) {
|
||||
// since the data hasn't changed since the last call
|
||||
newReadValue = previousRead;
|
||||
toShowBadge = false;
|
||||
} else {
|
||||
newReadValue = [...previousList.map(notif => `${notif.id}`)];
|
||||
toShowBadge = true;
|
||||
filteredData = [...readNotificationsDiff, ...previousList];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newReadValue = previousRead;
|
||||
if (
|
||||
previousRead.length &&
|
||||
lastSeenNotifications >= filteredData.length
|
||||
) {
|
||||
toShowBadge = false;
|
||||
} else if (lastSeenNotifications < filteredData.length) {
|
||||
toShowBadge = true;
|
||||
}
|
||||
}
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: newReadValue,
|
||||
date: consoleStateDB.console_notifications[userType].date,
|
||||
showBadge: toShowBadge,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: FETCH_CONSOLE_NOTIFICATIONS_SUCCESS,
|
||||
data: filteredData,
|
||||
});
|
||||
|
||||
// update/set the lastSeen value upon data is set
|
||||
if (
|
||||
!lastSeenNotifications ||
|
||||
lastSeenNotifications !== filteredData.length
|
||||
) {
|
||||
window.localStorage.setItem(
|
||||
'notifications:lastSeen',
|
||||
JSON.stringify(filteredData.length)
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const uppercaseScopedData = makeUppercaseScopes(data);
|
||||
let filteredData = filterScope(uppercaseScopedData, consoleScope);
|
||||
|
||||
if (
|
||||
!lastSeenNotifications ||
|
||||
lastSeenNotifications !== filteredData.length
|
||||
) {
|
||||
window.localStorage.setItem(
|
||||
'notifications:lastSeen',
|
||||
JSON.stringify(filteredData.length)
|
||||
);
|
||||
}
|
||||
|
||||
if (previousRead) {
|
||||
if (!consoleStateDB.console_notifications) {
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: [],
|
||||
date: now,
|
||||
showBadge: true,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
let newReadValue;
|
||||
|
||||
if (previousRead === 'default' || previousRead === 'error') {
|
||||
newReadValue = [];
|
||||
toShowBadge = false;
|
||||
} else if (previousRead === 'all') {
|
||||
const previousList = JSON.parse(
|
||||
localStorage.getItem('notifications:data')
|
||||
);
|
||||
if (previousList.length) {
|
||||
const resDiff = filteredData.filter(
|
||||
newNotif =>
|
||||
!previousList.find(oldNotif => oldNotif.id === newNotif.id)
|
||||
);
|
||||
if (!resDiff.length) {
|
||||
// since the data hasn't changed since the last call
|
||||
newReadValue = previousRead;
|
||||
toShowBadge = false;
|
||||
} else {
|
||||
newReadValue = [...previousList.map(notif => `${notif.id}`)];
|
||||
toShowBadge = true;
|
||||
filteredData = [...resDiff, ...previousList];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newReadValue = previousRead;
|
||||
if (
|
||||
previousRead.length &&
|
||||
lastSeenNotifications === filteredData.length
|
||||
) {
|
||||
toShowBadge = false;
|
||||
}
|
||||
}
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: newReadValue,
|
||||
date: consoleStateDB.console_notifications[userType].date,
|
||||
showBadge: toShowBadge,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: FETCH_CONSOLE_NOTIFICATIONS_SUCCESS,
|
||||
data: filteredData,
|
||||
});
|
||||
dispatch({ type: FETCH_CONSOLE_NOTIFICATIONS_ERROR });
|
||||
dispatch(
|
||||
updateConsoleNotificationsState({
|
||||
read: 'error',
|
||||
date: now,
|
||||
showBadge: false,
|
||||
})
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
|
@ -31,7 +31,6 @@ export type ConsoleNotification = {
|
||||
scope?: NotificationScope;
|
||||
};
|
||||
|
||||
// FIXME? : we may have to remove this
|
||||
export const defaultNotification: ConsoleNotification = {
|
||||
subject: 'No updates available at the moment',
|
||||
created_at: Date.now(),
|
||||
|
@ -35,10 +35,40 @@ import {
|
||||
setProClickState,
|
||||
getLoveConsentState,
|
||||
setLoveConsentState,
|
||||
getUserType,
|
||||
} from './utils';
|
||||
import { getSchemaBaseRoute } from '../Common/utils/routesUtils';
|
||||
import LoveSection from './LoveSection';
|
||||
import { Help, ProPopup } from './components/';
|
||||
import { HASURA_COLLABORATOR_TOKEN } from '../../constants';
|
||||
import { UPDATE_CONSOLE_NOTIFICATIONS } from '../../telemetry/Actions';
|
||||
|
||||
const updateRequestHeaders = props => {
|
||||
const { requestHeaders, dispatch } = props;
|
||||
|
||||
const collabTokenKey = Object.keys(requestHeaders).find(
|
||||
hdr => hdr.toLowerCase() === HASURA_COLLABORATOR_TOKEN
|
||||
);
|
||||
|
||||
if (collabTokenKey) {
|
||||
const userID = getUserType(requestHeaders[collabTokenKey]);
|
||||
if (props.console_opts && props.console_opts.console_notifications) {
|
||||
if (!props.console_opts.console_notifications[userID]) {
|
||||
dispatch({
|
||||
type: UPDATE_CONSOLE_NOTIFICATIONS,
|
||||
data: {
|
||||
...props.console_opts.console_notifications,
|
||||
[userID]: {
|
||||
read: [],
|
||||
date: null,
|
||||
showBadge: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Main extends React.Component {
|
||||
constructor(props) {
|
||||
@ -57,6 +87,7 @@ class Main extends React.Component {
|
||||
componentDidMount() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
updateRequestHeaders(this.props);
|
||||
dispatch(loadServerVersion()).then(() => {
|
||||
dispatch(featureCompatibilityInit());
|
||||
|
||||
@ -74,6 +105,18 @@ class Main extends React.Component {
|
||||
dispatch(fetchServerConfig);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const prevHeaders = Object.keys(prevProps.requestHeaders);
|
||||
const currHeaders = Object.keys(this.props.requestHeaders);
|
||||
|
||||
if (
|
||||
prevHeaders.length !== currHeaders.length ||
|
||||
prevHeaders.filter(hdr => !currHeaders.includes(hdr)).length
|
||||
) {
|
||||
updateRequestHeaders(this.props);
|
||||
}
|
||||
}
|
||||
|
||||
toggleProPopup = () => {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(emitProClickedEvent({ open: !this.state.isPopUpOpen }));
|
||||
@ -368,6 +411,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
currentSchema: state.tables.currentSchema,
|
||||
metadata: state.metadata,
|
||||
console_opts: state.telemetry.console_opts,
|
||||
requestHeaders: state.tables.dataHeaders,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1367,7 +1367,7 @@
|
||||
position: absolute;
|
||||
width: 17px;
|
||||
top: 16px;
|
||||
right: 8px;
|
||||
right: 0.8rem;
|
||||
border-radius: 50%;
|
||||
user-select: none;
|
||||
visibility: visible;
|
||||
@ -1536,10 +1536,6 @@
|
||||
.secureSectionText {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shareSection {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1050px) {
|
||||
|
@ -511,15 +511,19 @@ const HasuraNotifications: React.FC<
|
||||
|
||||
let userType = 'admin';
|
||||
|
||||
if (dataHeaders?.[HASURA_COLLABORATOR_TOKEN]) {
|
||||
const collabToken = dataHeaders[HASURA_COLLABORATOR_TOKEN];
|
||||
const headerHasCollabToken = Object.keys(dataHeaders).find(
|
||||
header => header.toLowerCase() === HASURA_COLLABORATOR_TOKEN
|
||||
);
|
||||
|
||||
if (headerHasCollabToken) {
|
||||
const collabToken = dataHeaders[headerHasCollabToken];
|
||||
userType = getUserType(collabToken);
|
||||
}
|
||||
|
||||
const previouslyReadState = React.useMemo(
|
||||
() =>
|
||||
console_opts?.console_notifications &&
|
||||
console_opts?.console_notifications[userType].read,
|
||||
console_opts?.console_notifications[userType]?.read,
|
||||
[console_opts?.console_notifications, userType]
|
||||
);
|
||||
const showBadge = React.useMemo(
|
||||
@ -639,7 +643,7 @@ const HasuraNotifications: React.FC<
|
||||
|
||||
useOnClickOutside([dropDownRef, wrapperRef], onClickOutside);
|
||||
|
||||
const onClickShareSection = () => {
|
||||
const onClickNotificationButton = () => {
|
||||
if (showBadge) {
|
||||
if (console_opts?.console_notifications) {
|
||||
let updatedState = {};
|
||||
@ -718,11 +722,11 @@ const HasuraNotifications: React.FC<
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${styles.shareSection} ${
|
||||
className={`${styles.shareSection} ${styles.headerRightNavbarBtn} ${
|
||||
isDropDownOpen ? styles.opened : ''
|
||||
} dropdown-toggle`}
|
||||
aria-expanded="false"
|
||||
onClick={onClickShareSection}
|
||||
onClick={onClickNotificationButton}
|
||||
ref={wrapperRef}
|
||||
>
|
||||
<i className={`fa fa-bell ${styles.bellIcon}`} />
|
||||
@ -750,7 +754,7 @@ const HasuraNotifications: React.FC<
|
||||
<Button
|
||||
title="Mark all as read"
|
||||
onClick={onClickMarkAllAsRead}
|
||||
disabled={!numberNotifications}
|
||||
disabled={!numberNotifications || !consoleNotifications.length}
|
||||
className={styles.markAllAsReadBtn}
|
||||
>
|
||||
mark all as read
|
||||
|
@ -149,81 +149,168 @@ const setPreReleaseNotificationOptOutInDB = () => (
|
||||
return dispatch(setConsoleOptsInDB(options, successCb, errorCb));
|
||||
};
|
||||
|
||||
// TODO: We could fetch the latest `read` state from the DB everytime we
|
||||
// open the notifications dropdown. That way we can reach a more consistent behavior on notifications.
|
||||
// OR another option would be to provide a refresh button so that users can use it to refresh state
|
||||
const updateConsoleNotificationsState = (updatedState: NotificationsState) => {
|
||||
return (
|
||||
dispatch: ThunkDispatch<ReduxState, unknown, AnyAction>,
|
||||
getState: GetReduxState
|
||||
) => {
|
||||
const url = Endpoints.schemaChange;
|
||||
const currentNotifications = getState().main.consoleNotifications;
|
||||
const restState = getState().telemetry.console_opts;
|
||||
const headers = dataHeaders(getState);
|
||||
let userType = 'admin';
|
||||
if (headers?.[HASURA_COLLABORATOR_TOKEN]) {
|
||||
const collabToken = headers[HASURA_COLLABORATOR_TOKEN];
|
||||
userType = getUserType(collabToken);
|
||||
}
|
||||
let composedUpdatedState: ConsoleState['console_opts'] = {
|
||||
...restState,
|
||||
console_notifications: {
|
||||
[userType]: updatedState,
|
||||
},
|
||||
};
|
||||
if (userType !== 'admin') {
|
||||
const currentState = restState?.console_notifications;
|
||||
if (Object.keys(currentState ?? {}).length > 1) {
|
||||
composedUpdatedState = {
|
||||
...restState,
|
||||
console_notifications: {
|
||||
...currentState,
|
||||
[userType]: updatedState,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
if (currentNotifications && Array.isArray(currentNotifications)) {
|
||||
if (isUpdateIDsEqual(currentNotifications, updatedState.read)) {
|
||||
composedUpdatedState = {
|
||||
...restState,
|
||||
console_notifications: {
|
||||
...restState?.console_notifications,
|
||||
[userType]: {
|
||||
read: 'all',
|
||||
showBadge: false,
|
||||
date: updatedState.date,
|
||||
},
|
||||
},
|
||||
};
|
||||
// update the localStorage var with all the notifications
|
||||
// since all the notifications were clicked on read state
|
||||
window.localStorage.setItem(
|
||||
'notifications:data',
|
||||
JSON.stringify(currentNotifications)
|
||||
);
|
||||
}
|
||||
}
|
||||
const updatedReadNotifications = getUpdateConsoleStateQuery(
|
||||
composedUpdatedState
|
||||
);
|
||||
const options: RequestInit = {
|
||||
credentials: globalCookiePolicy,
|
||||
const getStateURL = Endpoints.query;
|
||||
const getStateOptions: RequestInit = {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(updatedReadNotifications),
|
||||
body: JSON.stringify(getConsoleOptsQuery()),
|
||||
headers: dataHeaders(getState),
|
||||
credentials: globalCookiePolicy,
|
||||
};
|
||||
return dispatch(requestAction(url, options))
|
||||
.then((data: any) => {
|
||||
dispatch({
|
||||
type: UPDATE_CONSOLE_NOTIFICATIONS,
|
||||
data: data.returning[0].console_state.console_notifications,
|
||||
});
|
||||
// make a query to get the latest state from db prior to updating the read state for a user
|
||||
return dispatch(requestAction(getStateURL, getStateOptions))
|
||||
.then((data: Telemetry[]) => {
|
||||
if (data?.length) {
|
||||
const { console_state: current_console_state } = data[0];
|
||||
let composedUpdatedState: ConsoleState['console_opts'] = {
|
||||
...current_console_state,
|
||||
console_notifications: {
|
||||
...current_console_state?.console_notifications,
|
||||
},
|
||||
};
|
||||
const url = Endpoints.query;
|
||||
const currentNotifications = getState().main.consoleNotifications;
|
||||
const headers = dataHeaders(getState);
|
||||
let userType = 'admin';
|
||||
|
||||
const headerHasAdminToken = Object.keys(headers).find(
|
||||
header => header.toLowerCase() === HASURA_COLLABORATOR_TOKEN
|
||||
);
|
||||
if (headerHasAdminToken) {
|
||||
const collabToken = headers[headerHasAdminToken];
|
||||
userType = getUserType(collabToken);
|
||||
}
|
||||
|
||||
const dbReadState =
|
||||
current_console_state?.console_notifications?.[userType]?.read;
|
||||
let combinedReadState: NotificationsState['read'] = [];
|
||||
|
||||
if (
|
||||
!dbReadState ||
|
||||
dbReadState === 'default' ||
|
||||
dbReadState === 'error'
|
||||
) {
|
||||
composedUpdatedState = {
|
||||
...current_console_state,
|
||||
console_notifications: {
|
||||
...current_console_state?.console_notifications,
|
||||
[userType]: updatedState,
|
||||
},
|
||||
};
|
||||
} else if (dbReadState === 'all') {
|
||||
if (updatedState.read === 'all') {
|
||||
composedUpdatedState = {
|
||||
...current_console_state,
|
||||
console_notifications: {
|
||||
...current_console_state?.console_notifications,
|
||||
[userType]: {
|
||||
read: 'all',
|
||||
date: updatedState.date,
|
||||
showBadge: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
composedUpdatedState = {
|
||||
...current_console_state,
|
||||
console_notifications: {
|
||||
...current_console_state?.console_notifications,
|
||||
[userType]: updatedState,
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (typeof updatedState.read === 'string') {
|
||||
combinedReadState = updatedState.read;
|
||||
} else if (Array.isArray(updatedState.read)) {
|
||||
// this is being done to ensure that there is a consistency between the read
|
||||
// state of the users and the data present in the DB
|
||||
combinedReadState = dbReadState
|
||||
.concat(updatedState.read)
|
||||
.reduce((acc: string[], val: string) => {
|
||||
if (!acc.includes(val)) {
|
||||
return [...acc, val];
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
composedUpdatedState = {
|
||||
...current_console_state,
|
||||
console_notifications: {
|
||||
...current_console_state?.console_notifications,
|
||||
[userType]: {
|
||||
...updatedState,
|
||||
read: combinedReadState,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
currentNotifications &&
|
||||
Array.isArray(currentNotifications) &&
|
||||
Array.isArray(combinedReadState)
|
||||
) {
|
||||
if (isUpdateIDsEqual(currentNotifications, combinedReadState)) {
|
||||
composedUpdatedState = {
|
||||
...current_console_state,
|
||||
console_notifications: {
|
||||
...current_console_state?.console_notifications,
|
||||
[userType]: {
|
||||
read: 'all',
|
||||
showBadge: false,
|
||||
date: updatedState.date,
|
||||
},
|
||||
},
|
||||
};
|
||||
// update the localStorage var with all the notifications
|
||||
// since all the notifications were clicked on read state
|
||||
window.localStorage.setItem(
|
||||
'notifications:data',
|
||||
JSON.stringify(currentNotifications)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const updatedReadNotifications = getUpdateConsoleStateQuery(
|
||||
composedUpdatedState
|
||||
);
|
||||
const options: RequestInit = {
|
||||
credentials: globalCookiePolicy,
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify(updatedReadNotifications),
|
||||
};
|
||||
|
||||
return dispatch(requestAction(url, options))
|
||||
.then((retData: any) => {
|
||||
dispatch({
|
||||
type: UPDATE_CONSOLE_NOTIFICATIONS,
|
||||
data: retData.returning[0].console_state.console_notifications,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(
|
||||
'There was an error in updating the read console notifications.',
|
||||
error
|
||||
);
|
||||
return error;
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
.catch(err => {
|
||||
console.error(
|
||||
'There was an error in updating the console notifications.',
|
||||
error
|
||||
'There was an error in fetching the latest state from the DB.',
|
||||
err
|
||||
);
|
||||
return error;
|
||||
});
|
||||
};
|
||||
};
|
||||
@ -242,12 +329,18 @@ const loadConsoleOpts = () => {
|
||||
body: JSON.stringify(getConsoleOptsQuery()),
|
||||
};
|
||||
let userType = 'admin';
|
||||
if (headers?.[HASURA_COLLABORATOR_TOKEN]) {
|
||||
userType = headers[HASURA_COLLABORATOR_TOKEN];
|
||||
|
||||
const headerHasAdminToken = Object.keys(headers).find(
|
||||
header => header.toLowerCase() === HASURA_COLLABORATOR_TOKEN
|
||||
);
|
||||
if (headerHasAdminToken) {
|
||||
const collabToken = headers[headerHasAdminToken];
|
||||
userType = getUserType(collabToken);
|
||||
}
|
||||
return dispatch(requestAction(url, options) as any).then(
|
||||
|
||||
return dispatch(requestAction(url, options)).then(
|
||||
(data: Telemetry[]) => {
|
||||
if (data.length) {
|
||||
if (data?.length) {
|
||||
const { hasura_uuid, console_state } = data[0];
|
||||
|
||||
dispatch({
|
||||
@ -274,6 +367,20 @@ const loadConsoleOpts = () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
} else if (
|
||||
console_state.console_notifications &&
|
||||
!console_state.console_notifications[userType]
|
||||
) {
|
||||
dispatch({
|
||||
type: UPDATE_CONSOLE_NOTIFICATIONS,
|
||||
data: {
|
||||
[userType]: {
|
||||
read: [],
|
||||
date: null,
|
||||
showBadge: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
@ -353,7 +460,10 @@ const telemetryReducer = (
|
||||
...state,
|
||||
console_opts: {
|
||||
...state.console_opts,
|
||||
console_notifications: action.data,
|
||||
console_notifications: {
|
||||
...state.console_opts?.console_notifications,
|
||||
...action.data,
|
||||
},
|
||||
},
|
||||
};
|
||||
default:
|
||||
@ -369,4 +479,5 @@ export {
|
||||
setPreReleaseNotificationOptOutInDB,
|
||||
setTelemetryNotificationShownInDB,
|
||||
updateConsoleNotificationsState,
|
||||
UPDATE_CONSOLE_NOTIFICATIONS,
|
||||
};
|
||||
|
@ -27,6 +27,40 @@ export type ConsoleState = {
|
||||
hasura_uuid: string;
|
||||
};
|
||||
|
||||
export type ApiExplorer = {
|
||||
authApiExpanded: string;
|
||||
currentTab: number;
|
||||
headerFocus: boolean;
|
||||
loading: boolean;
|
||||
mode: string;
|
||||
modalState: Record<string, string>;
|
||||
explorerData: Record<string, string>;
|
||||
displayedApi: DisplayedApiState;
|
||||
};
|
||||
|
||||
export type DisplayedApiState = {
|
||||
details: Record<string, string>;
|
||||
id: string;
|
||||
request: ApiExplorerRequest;
|
||||
};
|
||||
|
||||
export type ApiExplorerRequest = {
|
||||
bodyType: string;
|
||||
headers: ApiExplorerHeader[];
|
||||
headersInitialised: boolean;
|
||||
method: string;
|
||||
params: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type ApiExplorerHeader = {
|
||||
key: string;
|
||||
value: string;
|
||||
isActive: boolean;
|
||||
isNewHeader: boolean;
|
||||
isDisabled: boolean;
|
||||
};
|
||||
|
||||
// Redux Utils
|
||||
export type ReduxState = {
|
||||
tables: {
|
||||
@ -43,6 +77,7 @@ export type ReduxState = {
|
||||
consoleNotifications: ConsoleNotification[];
|
||||
};
|
||||
telemetry: ConsoleState;
|
||||
apiexplorer: ApiExplorer;
|
||||
};
|
||||
|
||||
export type ReduxAction = RAEvents | RouterAction;
|
||||
|
Loading…
Reference in New Issue
Block a user