Skip to content
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

Ayush/feat/add actions to notifications #4101

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import {useMemo} from 'react';
import ReactMarkdown from 'react-markdown';
import {Provider} from 'react-redux';

import {Modal} from 'antd';
import {Button, Modal} from 'antd';

import _ from 'lodash';

import {useAppDispatch, useAppSelector} from '@redux/hooks';
import store from '@redux/store';
import {updateHelmDependencies} from '@redux/thunks/updateHelmDependencies';

import {TelemetryButtons} from '@molecules/NotificationMarkdown/TelemetryButtons';

import {ROOT_FILE_ENTRY} from '@shared/constants/fileEntry';
import {AlertType, ExtraContentType} from '@shared/models/alert';
import {openUrlInExternalBrowser} from '@shared/utils/shell';

Expand All @@ -30,7 +33,16 @@ const getExtraContent = (extraContentType: ExtraContentType, notificationId?: st

const NotificationMarkdown: React.FC<NotificationProps> = props => {
const {notification, type} = props;
const {extraContentType, id, message, title} = notification;
const dispatch = useAppDispatch();
const {extraContentType, id, message, title, buttons} = notification;
const helmChartMap = useAppSelector(state => state.main.helmChartMap);
const rootFolderPath = useAppSelector(state => state.main.fileMap[ROOT_FILE_ENTRY].filePath);
const helmChartIds = useMemo(() => {
return Object.keys(helmChartMap);
}, [helmChartMap]);
const helmChartFilePaths = useMemo(() => {
return helmChartIds.map(ID => helmChartMap[ID].filePath);
}, [helmChartIds, helmChartMap]);

const truncatedMessage = useMemo(() => {
if (message.length <= 200) {
Expand All @@ -40,6 +52,12 @@ const NotificationMarkdown: React.FC<NotificationProps> = props => {
return _.truncate(message, {length: 240});
}, [message]);

const handleDefaultAction = (action: string) => {
if (action === 'update_dependencies') {
dispatch(updateHelmDependencies({rootFolderPath, filePaths: helmChartFilePaths}));
}
};

const handleSeeMore = () => {
// @ts-ignore
Modal[type]({
Expand All @@ -58,6 +76,17 @@ const NotificationMarkdown: React.FC<NotificationProps> = props => {
>
{message}
</ReactMarkdown>
<S.ButtonContainer>
{buttons?.map(button =>
button.text === 'Cancel' ? (
<S.CancelButton onClick={() => Modal.destroyAll()}>{button.text}</S.CancelButton>
) : (
<S.DefaultActionButton onClick={() => handleDefaultAction(button.action)}>
{button.text}
</S.DefaultActionButton>
)
)}
</S.ButtonContainer>
</S.NotificationModalContent>
),
title: (
Expand All @@ -66,6 +95,7 @@ const NotificationMarkdown: React.FC<NotificationProps> = props => {
</Provider>
),
width: 600,
cancel: 'Cancel',
okText: 'Done',
});
};
Expand Down Expand Up @@ -93,6 +123,13 @@ const NotificationMarkdown: React.FC<NotificationProps> = props => {
</S.SeeAllButton>
)}
{extraContentType && getExtraContent(extraContentType, id)}
<div>
{buttons?.map(button => (
<Button key={button.text} style={button.style} onClick={() => handleDefaultAction(button.action)}>
{button.text}
</Button>
))}
</div>
</S.NotificationMarkdownContainer>
);
};
Expand Down
24 changes: 24 additions & 0 deletions src/components/molecules/NotificationMarkdown/styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {Button as RawButton} from 'antd';

import styled from 'styled-components';

import {Colors} from '@shared/styles/colors';

export const Button = styled(RawButton)`
padding: 0 20px;
`;
Expand Down Expand Up @@ -31,3 +33,25 @@ export const SeeAllButton = styled(RawButton)`
margin-left: 6px;
height: auto;
`;

export const CancelButton = styled.button`
border: 1px solid ${Colors.blue6};
color: ${Colors.grey100};
padding: 0 20px;
height: 32px;
`;

export const DefaultActionButton = styled.button`
border: none;
background-color: ${Colors.blue6};
padding: 0 20px;
height: 32px;
margin-left: 10px;
`;

export const ButtonContainer = styled.div`
display: flex;
justify-content: flex-end;
margin-top: 20px;
align-items: center;
`;
12 changes: 8 additions & 4 deletions src/components/organisms/MessageBox/MessageBox.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {useEffect, useMemo} from 'react';
import {Provider} from 'react-redux';

import {notification} from 'antd';

import {useAppDispatch, useAppSelector} from '@redux/hooks';
import {clearAlert} from '@redux/reducers/alert';
import store from '@redux/store';

import {NotificationMarkdown} from '@molecules';

Expand Down Expand Up @@ -40,10 +42,12 @@ const MessageBox: React.FC = () => {
key: alert.id,
message: alert.title,
description: (
<NotificationMarkdown
notification={{...alert, message: enhanceErrorMessage(alert.message)}}
type={notificationType}
/>
<Provider store={store}>
<NotificationMarkdown
notification={{...alert, message: enhanceErrorMessage(alert.message)}}
type={notificationType}
/>
</Provider>
),
duration: alert.duration || 4,
style: {zIndex: '10000'},
Expand Down
31 changes: 30 additions & 1 deletion src/components/organisms/PageHeader/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {activeProjectSelector, updateProjectsGitRepo} from '@redux/appConfig';
import {setCurrentBranch, setRepo} from '@redux/git';
import {getRepoInfo, initGitRepo} from '@redux/git/git.ipc';
import {useAppDispatch, useAppSelector} from '@redux/hooks';
// import {setAlert, testSyncAction, triggerTestAsyncAction} from '@redux/reducers/alert';
import {setAutosavingError} from '@redux/reducers/main';
import {
setIsFromBackToStart,
Expand All @@ -39,6 +40,7 @@ import {showGitErrorModal} from '@utils/terminal';
import MonokleKubeshopLogo from '@assets/NewMonokleLogoDark.svg';

import {Icon} from '@monokle/components';
// import {AlertEnum} from '@shared/models/alert';
import {isInClusterModeSelector, isInPreviewModeSelector} from '@shared/utils/selectors';
import {trackEvent} from '@shared/utils/telemetry';

Expand Down Expand Up @@ -279,7 +281,34 @@ const PageHeader = () => {

<DownloadProgress />
</div>

<div>
<button
type="button"
style={{color: 'black'}}
// onClick={() =>
// dispatch(
// setAlert({
// type: AlertEnum.Info,
// title: 'test',
// message:
// 'gvefodiuvnbritnuvbirtbvnirtubhirtubvrtibvrtibnrtibirtbhibrtiurtibuvrtibvuivbivbertivberivberivbeirvbierbhfierhrefierufheirucvndfivuhero9fiuheriuhgvefodiuvnbritnuvbirtbvnirtubhirtubvrtibvrtibnrtibirtbhibrtiurtibuvrtibvuivbivbertivberivberivbeirvbierbhfierhrefierufheirucvndfivuhero9fiuheriuh',
// buttons: [
// {
// text: 'Cannel',
// action: testSyncAction(),
// },
// {
// text: 'Async action',
// action: triggerTestAsyncAction(),
// },
// ],
// })
// )
// }
>
Test notification
</button>
</div>
<div style={{display: 'flex', alignItems: 'center', gap: 8}}>
<K8sVersionSelection />
{isInPreviewMode ? <PreviewControls /> : <ClusterControls />}
Expand Down
19 changes: 18 additions & 1 deletion src/redux/reducers/alert.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Draft, PayloadAction, createSlice} from '@reduxjs/toolkit';

import initialState from '@redux/initialState';
import {AppListenerFn} from '@redux/listeners/base';

import {AlertState, AlertType} from '@shared/models/alert';

Expand All @@ -14,6 +15,13 @@ export const alertSlice = createSlice({
clearAlert: (state: Draft<AlertState>) => {
state.alert = undefined;
},
testSyncAction: () => {
console.log('Some sync action');
console.log('%cSome sync action', 'color: red');
},
triggerTestAsyncAction: () => {
console.log('Dispatching a sync action to trigger an async listener');
},
},
extraReducers: builder => {
builder.addMatcher(
Expand All @@ -27,5 +35,14 @@ export const alertSlice = createSlice({
},
});

export const {setAlert, clearAlert} = alertSlice.actions;
export const {setAlert, clearAlert, testSyncAction, triggerTestAsyncAction} = alertSlice.actions;
export default alertSlice.reducer;

export const testAsyncListener: AppListenerFn = listen => {
listen({
actionCreator: triggerTestAsyncAction,
effect: async (action, {getState, dispatch}) => {
console.log('doing some async action, with access to getState and dispatch');
},
});
};
3 changes: 2 additions & 1 deletion src/redux/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {dashboardSlice} from './dashboard';
import {formSlice} from './forms';
import {gitSlice} from './git';
import {combineListeners, listenerMiddleware} from './listeners/base';
import {alertSlice} from './reducers/alert';
import {alertSlice, testAsyncListener} from './reducers/alert';
import {extensionSlice} from './reducers/extension';
import {mainSlice} from './reducers/main';
import {imageListParserListener} from './reducers/main/mainListeners';
Expand Down Expand Up @@ -48,6 +48,7 @@ combineListeners([
...appConfigListeners,
...clusterListeners,
imageListParserListener,
testAsyncListener,
]);

const appReducer = combineReducers({
Expand Down
4 changes: 4 additions & 0 deletions src/redux/thunks/runHelmCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {createAsyncThunk} from '@reduxjs/toolkit';
import log from 'loglevel';
import path, {dirname} from 'path';

import {useAppDispatch} from '@redux/hooks';
import {createRejectionWithAlert} from '@redux/thunks/utils';

import {errorMsg} from '@utils/error';
Expand Down Expand Up @@ -30,6 +31,8 @@ export const runHelmCommand = createAsyncThunk<
const helmChartMap = thunkAPI.getState().main.helmChartMap;
const {chart, command} = payload;

const dispatch = useAppDispatch();

trackEvent('helm/command/start', {command});
try {
if (command.length === 0) {
Expand Down Expand Up @@ -72,6 +75,7 @@ export const runHelmCommand = createAsyncThunk<
} catch (err) {
let reason = errorMsg(err);
trackEvent('helm/command/fail', {reason});

return createRejectionWithAlert(thunkAPI, `helm ${command.join(' ')} failed`, reason);
}
});
15 changes: 14 additions & 1 deletion src/redux/thunks/runPreviewConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {createRejectionWithAlert} from '@redux/thunks/utils';
import {buildHelmCommand} from '@utils/helm';

import {ROOT_FILE_ENTRY} from '@shared/constants/fileEntry';
import {AlertButton} from '@shared/models/alert';
import {AppDispatch} from '@shared/models/appDispatch';
import {CommandOptions} from '@shared/models/commands';
import {HelmPreviewConfiguration, PreviewConfigValuesFileItem} from '@shared/models/config';
Expand Down Expand Up @@ -154,9 +155,21 @@ export const runPreviewConfiguration = createAsyncThunk<

trackEvent('preview/helm_config/end', {executionTime: endTime - startTime});

const buttons: AlertButton[] = [
{
text: 'Cancel',
action: '',
},
{
text: 'Update Dependencies',
action: 'update_dependencies',
},
];

return createRejectionWithAlert(
thunkAPI,
'Helm Error',
`Unable to run Helm with Preview Configuration: ${previewConfiguration.name} - [${result.stderr}]`
`Unable to run Helm with Preview Configuration: ${previewConfiguration.name} - [${result.stderr}]`,
buttons
);
});
39 changes: 39 additions & 0 deletions src/redux/thunks/updateHelmDependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {createAsyncThunk} from '@reduxjs/toolkit';

import path, {dirname} from 'path';

import {setAlert} from '@redux/reducers/alert';

import {errorAlert, successAlert} from '@utils/alert';

import {AppDispatch} from '@shared/models/appDispatch';
import {RootState} from '@shared/models/rootState';
import {hasCommandFailed, runCommandInMainThread} from '@shared/utils/commands';

export const updateHelmDependencies = createAsyncThunk<
void,
{rootFolderPath: string; filePaths: string[]},
{dispatch: AppDispatch; state: RootState}
>('main/updateHelmDependencies', async ({rootFolderPath, filePaths}, {dispatch, getState}) => {
if (!rootFolderPath) {
throw new Error("Couldn't find current working directory.");
}

if (filePaths.length === 0) {
throw new Error("Couldn't find current working file.");
}
filePaths.forEach(async (filePath: string) => {
const result = await runCommandInMainThread({
commandId: 'helm/command',
cmd: 'helm',
args: ['dependency', 'update'],
cwd: dirname(path.join(rootFolderPath, filePath)),
});

if (hasCommandFailed(result)) {
dispatch(setAlert(errorAlert(`Helm Dependency could not be updated for path ${filePath}`, result.stderr)));
throw new Error(result.stderr);
}
dispatch(setAlert(successAlert('Update Helm Dependencies', `Helm Dependencies updated successfully`)));
});
});
5 changes: 3 additions & 2 deletions src/redux/thunks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {createKubeClientWithSetup} from '@redux/cluster/service/kube-client';

import {getResourceKindHandler} from '@src/kindhandlers';

import {AlertEnum} from '@shared/models/alert';
import {AlertButton, AlertEnum} from '@shared/models/alert';
import {K8sObject} from '@shared/models/k8s';
import {ResourceMeta} from '@shared/models/k8sResource';

Expand Down Expand Up @@ -67,12 +67,13 @@ export function getK8sObjectsAsYaml(items: any[], kind?: string, apiVersion?: st
* Creates a thunk rejection that displays an error alert
*/

export function createRejectionWithAlert(thunkAPI: any, title: string, message: string) {
export function createRejectionWithAlert(thunkAPI: any, title: string, message: string, buttons: AlertButton[] = []) {
return thunkAPI.rejectWithValue({
alert: {
title,
message,
type: AlertEnum.Error,
buttons,
},
});
}
Expand Down
Loading