mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-03 01:18:43 +02:00
commit
d69c024bbe
@ -103,4 +103,21 @@ export const useCommonStyles = makeStyles(theme => ({
|
|||||||
opacity: '0',
|
opacity: '0',
|
||||||
transition: 'transform 1.25s ease, opacity 1s ease',
|
transition: 'transform 1.25s ease, opacity 1s ease',
|
||||||
},
|
},
|
||||||
|
fadeInTopStart: {
|
||||||
|
opacity: '0',
|
||||||
|
position: 'fixed',
|
||||||
|
right: '40px',
|
||||||
|
top: '40px',
|
||||||
|
transform: 'translateY(-400px)',
|
||||||
|
},
|
||||||
|
fadeInTopEnter: {
|
||||||
|
transform: 'translateY(100px)',
|
||||||
|
opacity: '1',
|
||||||
|
transition: 'transform 0.6s ease, opacity 1s ease',
|
||||||
|
},
|
||||||
|
fadeInTopLeave: {
|
||||||
|
transform: 'translateY(-400px)',
|
||||||
|
opacity: '0',
|
||||||
|
transition: 'transform 1.25s ease, opacity 1s ease',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -12,7 +12,7 @@ import styles from './styles.module.scss';
|
|||||||
import IAuthStatus from '../interfaces/user';
|
import IAuthStatus from '../interfaces/user';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import NotFound from './common/NotFound/NotFound';
|
import NotFound from './common/NotFound/NotFound';
|
||||||
import Feedback from './common/Feedback';
|
import Feedback from './common/Feedback/Feedback';
|
||||||
import SWRProvider from './providers/SWRProvider/SWRProvider';
|
import SWRProvider from './providers/SWRProvider/SWRProvider';
|
||||||
import ConditionallyRender from './common/ConditionallyRender';
|
import ConditionallyRender from './common/ConditionallyRender';
|
||||||
import EnvironmentSplash from './common/EnvironmentSplash/EnvironmentSplash';
|
import EnvironmentSplash from './common/EnvironmentSplash/EnvironmentSplash';
|
||||||
@ -109,6 +109,8 @@ const App = ({ location, user, fetchUiBootstrap }: IAppProps) => {
|
|||||||
show={<Loader />}
|
show={<Loader />}
|
||||||
elseShow={
|
elseShow={
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
|
<ToastRenderer />
|
||||||
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={showSplash}
|
condition={showSplash}
|
||||||
show={
|
show={
|
||||||
@ -139,8 +141,6 @@ const App = ({ location, user, fetchUiBootstrap }: IAppProps) => {
|
|||||||
</LayoutPicker>
|
</LayoutPicker>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ToastRenderer />
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -2,7 +2,7 @@ import { makeStyles } from '@material-ui/core/styles';
|
|||||||
|
|
||||||
export const useStyles = makeStyles(theme => ({
|
export const useStyles = makeStyles(theme => ({
|
||||||
feedback: {
|
feedback: {
|
||||||
borderRadius: '3px',
|
borderRadius: '12.5px',
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
zIndex: '9999',
|
zIndex: '9999',
|
||||||
boxShadow: '2px 2px 4px 4px rgba(143,143,143, 0.25)',
|
boxShadow: '2px 2px 4px 4px rgba(143,143,143, 0.25)',
|
||||||
@ -12,7 +12,7 @@ export const useStyles = makeStyles(theme => ({
|
|||||||
height: '200px',
|
height: '200px',
|
||||||
},
|
},
|
||||||
animateContainer: {
|
animateContainer: {
|
||||||
zIndex: '9999',
|
zIndex: 9999,
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useContext, useState } from 'react';
|
||||||
import { Button, IconButton } from '@material-ui/core';
|
import { Button, IconButton } from '@material-ui/core';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import CloseIcon from '@material-ui/icons/Close';
|
import CloseIcon from '@material-ui/icons/Close';
|
||||||
@ -10,6 +10,8 @@ import AnimateOnMount from '../AnimateOnMount/AnimateOnMount';
|
|||||||
import ConditionallyRender from '../ConditionallyRender';
|
import ConditionallyRender from '../ConditionallyRender';
|
||||||
import { formatApiPath } from '../../../utils/format-path';
|
import { formatApiPath } from '../../../utils/format-path';
|
||||||
import { Action, Dispatch } from 'redux';
|
import { Action, Dispatch } from 'redux';
|
||||||
|
import UIContext from '../../../contexts/UIContext';
|
||||||
|
import useUser from '../../../hooks/api/getters/useUser/useUser';
|
||||||
|
|
||||||
interface IFeedbackProps {
|
interface IFeedbackProps {
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
@ -19,13 +21,9 @@ interface IFeedbackProps {
|
|||||||
openUrl: string;
|
openUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Feedback = ({
|
const Feedback = ({ feedbackId, openUrl }: IFeedbackProps) => {
|
||||||
show,
|
const { showFeedback, setShowFeedback } = useContext(UIContext);
|
||||||
hideFeedback,
|
const { refetch, feedback } = useUser();
|
||||||
fetchUser,
|
|
||||||
feedbackId,
|
|
||||||
openUrl,
|
|
||||||
}: IFeedbackProps) => {
|
|
||||||
const [answeredNotNow, setAnsweredNotNow] = useState(false);
|
const [answeredNotNow, setAnsweredNotNow] = useState(false);
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const commonStyles = useCommonStyles();
|
const commonStyles = useCommonStyles();
|
||||||
@ -42,15 +40,15 @@ const Feedback = ({
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ feedbackId }),
|
body: JSON.stringify({ feedbackId }),
|
||||||
});
|
});
|
||||||
await fetchUser();
|
await refetch();
|
||||||
} catch {
|
} catch {
|
||||||
hideFeedback();
|
setShowFeedback(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Await api call to register confirmation
|
// Await api call to register confirmation
|
||||||
window.open(openUrl, '_blank');
|
window.open(openUrl, '_blank');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
hideFeedback();
|
setShowFeedback(false);
|
||||||
}, 200);
|
}, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -69,22 +67,28 @@ const Feedback = ({
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ feedbackId, neverShow: true }),
|
body: JSON.stringify({ feedbackId, neverShow: true }),
|
||||||
});
|
});
|
||||||
await fetchUser();
|
await refetch();
|
||||||
} catch {
|
} catch {
|
||||||
hideFeedback();
|
setShowFeedback(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
hideFeedback();
|
setShowFeedback(false);
|
||||||
}, 100);
|
}, 100);
|
||||||
};
|
};
|
||||||
|
console.log(feedback);
|
||||||
|
const pnps = feedback.find(feedback => feedback.feedbackId === feedbackId);
|
||||||
|
|
||||||
|
if (pnps?.given || pnps?.neverShow) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimateOnMount
|
<AnimateOnMount
|
||||||
mounted={show}
|
mounted={showFeedback}
|
||||||
start={commonStyles.fadeInBottomStart}
|
start={commonStyles.fadeInTopStart}
|
||||||
enter={commonStyles.fadeInBottomEnter}
|
enter={commonStyles.fadeInTopEnter}
|
||||||
leave={commonStyles.fadeInBottomLeave}
|
leave={commonStyles.fadeInTopLeave}
|
||||||
container={styles.animateContainer}
|
container={styles.animateContainer}
|
||||||
>
|
>
|
||||||
<div className={styles.feedback}>
|
<div className={styles.feedback}>
|
||||||
@ -96,7 +100,7 @@ const Feedback = ({
|
|||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
className={styles.close}
|
className={styles.close}
|
||||||
onClick={() => hideFeedback()}
|
onClick={() => setShowFeedback(false)}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import { Dispatch } from 'react';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Action } from 'redux';
|
|
||||||
import { hideFeedback } from '../../../store/feedback/actions';
|
|
||||||
import { fetchUser } from '../../../store/user/actions';
|
|
||||||
import Feedback from './Feedback';
|
|
||||||
|
|
||||||
const mapStateToProps = (state: any) => ({
|
|
||||||
show: state.feedback.show,
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: Dispatch<Action>) => ({
|
|
||||||
hideFeedback: () => {
|
|
||||||
hideFeedback(dispatch)();
|
|
||||||
},
|
|
||||||
fetchUser: () => fetchUser()(dispatch),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Feedback);
|
|
@ -0,0 +1,10 @@
|
|||||||
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
|
|
||||||
|
export const useStyles = makeStyles(theme => ({
|
||||||
|
toastWrapper: {
|
||||||
|
right: 0,
|
||||||
|
left: 0,
|
||||||
|
margin: '0 auto',
|
||||||
|
maxWidth: '450px',
|
||||||
|
},
|
||||||
|
}));
|
@ -2,13 +2,15 @@ import { Portal } from '@material-ui/core';
|
|||||||
import { useContext, useEffect } from 'react';
|
import { useContext, useEffect } from 'react';
|
||||||
import { useCommonStyles } from '../../../common.styles';
|
import { useCommonStyles } from '../../../common.styles';
|
||||||
import UIContext, { IToastData } from '../../../contexts/UIContext';
|
import UIContext, { IToastData } from '../../../contexts/UIContext';
|
||||||
|
import { useStyles } from './ToastRenderer.styles';
|
||||||
import AnimateOnMount from '../AnimateOnMount/AnimateOnMount';
|
import AnimateOnMount from '../AnimateOnMount/AnimateOnMount';
|
||||||
import Toast from './Toast/Toast';
|
import Toast from './Toast/Toast';
|
||||||
|
|
||||||
const ToastRenderer = () => {
|
const ToastRenderer = () => {
|
||||||
// @ts-ignore-next-line
|
// @ts-ignore-next-line
|
||||||
const { toastData, setToast } = useContext(UIContext);
|
const { toastData, setToast } = useContext(UIContext);
|
||||||
const styles = useCommonStyles();
|
const commonStyles = useCommonStyles();
|
||||||
|
const styles = useStyles();
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
setToast((prev: IToastData) => ({ ...prev, show: false }));
|
setToast((prev: IToastData) => ({ ...prev, show: false }));
|
||||||
@ -30,10 +32,10 @@ const ToastRenderer = () => {
|
|||||||
<Portal>
|
<Portal>
|
||||||
<AnimateOnMount
|
<AnimateOnMount
|
||||||
mounted={toastData?.show}
|
mounted={toastData?.show}
|
||||||
start={styles.fadeInBottomStartWithoutFixed}
|
start={commonStyles.fadeInBottomStartWithoutFixed}
|
||||||
enter={styles.fadeInBottomEnter}
|
enter={commonStyles.fadeInBottomEnter}
|
||||||
leave={styles.fadeInBottomLeave}
|
leave={commonStyles.fadeInBottomLeave}
|
||||||
container={styles.fullWidth}
|
container={styles.toastWrapper}
|
||||||
>
|
>
|
||||||
<Toast {...toastData} />
|
<Toast {...toastData} />
|
||||||
</AnimateOnMount>
|
</AnimateOnMount>
|
||||||
|
@ -8,10 +8,13 @@ import useFeatureApi from '../../../../hooks/api/actions/useFeatureApi/useFeatur
|
|||||||
import { CREATE_FEATURE } from '../../../providers/AccessProvider/permissions';
|
import { CREATE_FEATURE } from '../../../providers/AccessProvider/permissions';
|
||||||
import PermissionButton from '../../../common/PermissionButton/PermissionButton';
|
import PermissionButton from '../../../common/PermissionButton/PermissionButton';
|
||||||
import { CF_CREATE_BTN_ID } from '../../../../testIds';
|
import { CF_CREATE_BTN_ID } from '../../../../testIds';
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import UIContext from '../../../../contexts/UIContext';
|
||||||
|
|
||||||
const CreateFeature = () => {
|
const CreateFeature = () => {
|
||||||
/* @ts-ignore */
|
/* @ts-ignore */
|
||||||
const { setToastData, setToastApiError } = useToast();
|
const { setToastData, setToastApiError } = useToast();
|
||||||
|
const { setShowFeedback } = useContext(UIContext);
|
||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@ -46,6 +49,7 @@ const CreateFeature = () => {
|
|||||||
confetti: true,
|
confetti: true,
|
||||||
type: 'success',
|
type: 'success',
|
||||||
});
|
});
|
||||||
|
setShowFeedback(true);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setToastApiError(e.toString());
|
setToastApiError(e.toString());
|
||||||
}
|
}
|
||||||
|
@ -496,10 +496,10 @@ exports[`renders correctly with with variants 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
<fieldset
|
<fieldset
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
className="PrivateNotchedOutline-root-23 MuiOutlinedInput-notchedOutline"
|
className="PrivateNotchedOutline-root-26 MuiOutlinedInput-notchedOutline"
|
||||||
>
|
>
|
||||||
<legend
|
<legend
|
||||||
className="PrivateNotchedOutline-legendLabelled-25 PrivateNotchedOutline-legendNotched-26"
|
className="PrivateNotchedOutline-legendLabelled-28 PrivateNotchedOutline-legendNotched-29"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
Stickiness
|
Stickiness
|
||||||
|
@ -29,7 +29,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="MuiChip-root makeStyles-chip-21 MuiChip-colorPrimary MuiChip-outlined MuiChip-outlinedPrimary"
|
className="MuiChip-root makeStyles-chip-24 MuiChip-colorPrimary MuiChip-outlined MuiChip-outlinedPrimary"
|
||||||
onKeyDown={[Function]}
|
onKeyDown={[Function]}
|
||||||
onKeyUp={[Function]}
|
onKeyUp={[Function]}
|
||||||
title="Feature toggle is active."
|
title="Feature toggle is active."
|
||||||
@ -104,10 +104,10 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
<fieldset
|
<fieldset
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
className="PrivateNotchedOutline-root-22 MuiOutlinedInput-notchedOutline"
|
className="PrivateNotchedOutline-root-25 MuiOutlinedInput-notchedOutline"
|
||||||
>
|
>
|
||||||
<legend
|
<legend
|
||||||
className="PrivateNotchedOutline-legendLabelled-24 PrivateNotchedOutline-legendNotched-25"
|
className="PrivateNotchedOutline-legendLabelled-27 PrivateNotchedOutline-legendNotched-28"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
Feature type
|
Feature type
|
||||||
@ -163,10 +163,10 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
<fieldset
|
<fieldset
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
className="PrivateNotchedOutline-root-22 MuiOutlinedInput-notchedOutline"
|
className="PrivateNotchedOutline-root-25 MuiOutlinedInput-notchedOutline"
|
||||||
>
|
>
|
||||||
<legend
|
<legend
|
||||||
className="PrivateNotchedOutline-legendLabelled-24 PrivateNotchedOutline-legendNotched-25"
|
className="PrivateNotchedOutline-legendLabelled-27 PrivateNotchedOutline-legendNotched-28"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
Project
|
Project
|
||||||
@ -198,7 +198,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
aria-disabled={true}
|
aria-disabled={true}
|
||||||
className="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-26 MuiSwitch-switchBase MuiSwitch-colorSecondary PrivateSwitchBase-disabled-28 Mui-disabled Mui-disabled Mui-disabled"
|
className="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-29 MuiSwitch-switchBase MuiSwitch-colorSecondary PrivateSwitchBase-disabled-31 Mui-disabled Mui-disabled Mui-disabled"
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
onDragLeave={[Function]}
|
onDragLeave={[Function]}
|
||||||
onFocus={[Function]}
|
onFocus={[Function]}
|
||||||
@ -217,7 +217,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
checked={false}
|
checked={false}
|
||||||
className="PrivateSwitchBase-input-29 MuiSwitch-input"
|
className="PrivateSwitchBase-input-32 MuiSwitch-input"
|
||||||
disabled={true}
|
disabled={true}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -350,7 +350,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<div
|
<div
|
||||||
className="MuiPaper-root makeStyles-tabNav-30 MuiPaper-elevation1 MuiPaper-rounded"
|
className="MuiPaper-root makeStyles-tabNav-33 MuiPaper-elevation1 MuiPaper-rounded"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="MuiTabs-root"
|
className="MuiTabs-root"
|
||||||
@ -398,7 +398,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
Activation
|
Activation
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className="PrivateTabIndicator-root-31 PrivateTabIndicator-colorPrimary-32 MuiTabs-indicator"
|
className="PrivateTabIndicator-root-34 PrivateTabIndicator-colorPrimary-35 MuiTabs-indicator"
|
||||||
style={Object {}}
|
style={Object {}}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
@ -11,13 +11,16 @@ const UIProvider: React.FC = ({ children }) => {
|
|||||||
persist: false,
|
persist: false,
|
||||||
type: '',
|
type: '',
|
||||||
});
|
});
|
||||||
|
const [showFeedback, setShowFeedback] = useState();
|
||||||
|
|
||||||
const context = React.useMemo(
|
const context = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
setToast,
|
setToast,
|
||||||
toastData,
|
toastData,
|
||||||
|
showFeedback,
|
||||||
|
setShowFeedback,
|
||||||
}),
|
}),
|
||||||
[toastData]
|
[toastData, showFeedback]
|
||||||
);
|
);
|
||||||
|
|
||||||
return <UIContext.Provider value={context}>{children}</UIContext.Provider>;
|
return <UIContext.Provider value={context}>{children}</UIContext.Provider>;
|
||||||
|
@ -9,11 +9,13 @@ export interface IToastData {
|
|||||||
confetti?: boolean;
|
confetti?: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
interface IFeatureStrategiesUIContext {
|
interface IUIContext {
|
||||||
toastData: IToastData;
|
toastData: IToastData;
|
||||||
setToast: React.Dispatch<React.SetStateAction<IToastData>>;
|
setToast: React.Dispatch<React.SetStateAction<IToastData>>;
|
||||||
|
showFeedback: boolean;
|
||||||
|
setShowFeedback: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UIContext = React.createContext<IFeatureStrategiesUIContext | null>(null);
|
const UIContext = React.createContext<IUIContext | null>(null);
|
||||||
|
|
||||||
export default UIContext;
|
export default UIContext;
|
||||||
|
Loading…
Reference in New Issue
Block a user