diff --git a/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/run-strategy.tsx b/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/run-strategy.tsx
deleted file mode 100644
index bef7c154a4d6..000000000000
--- a/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/run-strategy.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import React from 'react';
-import TradeAnimation from 'Components/trade-animation';
-
-const RunStrategy = () => (
-
-
-
-);
-
-export default RunStrategy;
diff --git a/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/toolbar.scss b/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/toolbar.scss
index 52359c2b4d4d..dea20935fe24 100644
--- a/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/toolbar.scss
+++ b/packages/bot-web-ui/src/components/dashboard/bot-builder/toolbar/toolbar.scss
@@ -91,8 +91,7 @@
}
}
&__animation {
- margin-right: 0.5rem;
- max-width: 35rem;
+ width: 35rem;
}
&__dialog {
&-text--second {
diff --git a/packages/bot-web-ui/src/components/dashboard/bot-stop-notifiaction.spec.tsx b/packages/bot-web-ui/src/components/dashboard/bot-stop-notifiaction.spec.tsx
new file mode 100644
index 000000000000..38be3d4fbf7d
--- /dev/null
+++ b/packages/bot-web-ui/src/components/dashboard/bot-stop-notifiaction.spec.tsx
@@ -0,0 +1,101 @@
+import React from 'react';
+import { mockStore, StoreProvider } from '@deriv/stores';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { act, fireEvent, render, screen } from '@testing-library/react';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import userEvent from '@testing-library/user-event';
+import RootStore from 'Stores/index';
+import { DBotStoreProvider, mockDBotStore } from 'Stores/useDBotStore';
+import BotStopNotification from './bot-stop-notification';
+
+jest.mock('@deriv/bot-skeleton/src/scratch/blockly', () => jest.fn());
+jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => ({
+ saveRecentWorkspace: jest.fn(),
+ unHighlightAllBlocks: jest.fn(),
+}));
+jest.mock('@deriv/bot-skeleton/src/scratch/hooks/block_svg', () => jest.fn());
+
+const mock_ws = {
+ authorized: {
+ subscribeProposalOpenContract: jest.fn(),
+ send: jest.fn(),
+ },
+ storage: {
+ send: jest.fn(),
+ },
+ contractUpdate: jest.fn(),
+ subscribeTicksHistory: jest.fn(),
+ forgetStream: jest.fn(),
+ activeSymbols: jest.fn(),
+ send: jest.fn(),
+};
+describe('BotStopNotification', () => {
+ let wrapper: ({ children }: { children: JSX.Element }) => JSX.Element, mock_DBot_store: RootStore | undefined;
+
+ beforeEach(() => {
+ jest.resetModules();
+ const mock_store = mockStore({});
+ mock_DBot_store = mockDBotStore(mock_store, mock_ws);
+
+ wrapper = ({ children }: { children: JSX.Element }) => (
+
+
+ {children}
+
+
+ );
+ });
+
+ it('should clear the notification timer and hide message after timer expires', () => {
+ act(() => {
+ mock_DBot_store?.run_panel.setShowBotStopMessage(false);
+ });
+ jest.useFakeTimers();
+
+ render( , {
+ wrapper,
+ });
+
+ // Advance timers to trigger notificationTimer
+ jest.advanceTimersByTime(6000);
+
+ // Expect that setShowBotStopMessage(false) was called
+ expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
+ });
+
+ it('should render the toast component', () => {
+ const { container } = render( , {
+ wrapper,
+ });
+ expect(container).toBeInTheDocument();
+ });
+
+ it('should render to remove the toast component when clicking on close icon', () => {
+ act(() => {
+ mock_DBot_store?.run_panel.setShowBotStopMessage(false);
+ });
+
+ render( , {
+ wrapper,
+ });
+ userEvent.click(screen.getByTestId('notification-close'));
+ expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
+ });
+
+ it('should render toast', () => {
+ act(() => {
+ mock_DBot_store?.run_panel.setShowBotStopMessage(true);
+ });
+
+ render( , {
+ wrapper,
+ });
+ fireEvent.mouseOver(screen.getByTestId('bot-stop-notification'));
+ jest.advanceTimersByTime(6000);
+ expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
+
+ fireEvent.mouseLeave(screen.getByTestId('bot-stop-notification'));
+ jest.advanceTimersByTime(4000);
+ expect(screen.queryByText('You’ve just stopped the bot.')).not.toBeInTheDocument();
+ });
+});
diff --git a/packages/bot-web-ui/src/components/dashboard/bot-stop-notification.tsx b/packages/bot-web-ui/src/components/dashboard/bot-stop-notification.tsx
new file mode 100644
index 000000000000..fb9821a26748
--- /dev/null
+++ b/packages/bot-web-ui/src/components/dashboard/bot-stop-notification.tsx
@@ -0,0 +1,61 @@
+import React, { useRef } from 'react';
+import { observer } from 'mobx-react';
+import { Icon, Toast } from '@deriv/components';
+import { Localize } from '@deriv/translations';
+import { useDBotStore } from 'Stores/useDBotStore';
+
+const BotStopNotification = observer(() => {
+ const { run_panel } = useDBotStore();
+ const { setShowBotStopMessage } = run_panel;
+ const notification_timer = useRef(6000);
+
+ const notificationTimer = setTimeout(() => {
+ if (notification_timer.current) {
+ setShowBotStopMessage(false);
+ }
+ }, notification_timer.current);
+
+ const resetTimer = () => {
+ setTimeout(() => {
+ setShowBotStopMessage(false);
+ }, 4000);
+ };
+
+ return (
+ {
+ clearTimeout(notificationTimer);
+ }}
+ onMouseLeave={e => {
+ resetTimer();
+ }}
+ data-testid='bot-stop-notification'
+ >
+
+
+ ,
+ ]}
+ />
+
+ setShowBotStopMessage(false)}
+ />
+
+
+ );
+});
+
+export default BotStopNotification;
diff --git a/packages/bot-web-ui/src/components/dashboard/dashboard-component/run-strategy.tsx b/packages/bot-web-ui/src/components/dashboard/dashboard-component/run-strategy.tsx
index c985dc6662ea..bef7c154a4d6 100644
--- a/packages/bot-web-ui/src/components/dashboard/dashboard-component/run-strategy.tsx
+++ b/packages/bot-web-ui/src/components/dashboard/dashboard-component/run-strategy.tsx
@@ -3,7 +3,7 @@ import TradeAnimation from 'Components/trade-animation';
const RunStrategy = () => (
-
+
);
diff --git a/packages/bot-web-ui/src/components/dashboard/dashboard.scss b/packages/bot-web-ui/src/components/dashboard/dashboard.scss
index 0662098752f4..674808040125 100644
--- a/packages/bot-web-ui/src/components/dashboard/dashboard.scss
+++ b/packages/bot-web-ui/src/components/dashboard/dashboard.scss
@@ -675,10 +675,18 @@
position: fixed;
z-index: 1;
left: 0;
+ right: 0;
bottom: 7rem;
- .dc-toast__message {
- background: var(--text-general);
- color: var(--general-main-1);
+ .dc-toast {
+ width: 100%;
+ &__message {
+ background: var(--text-prominent);
+ color: var(--general-main-1);
+ }
+ }
+
+ @include mobile {
+ bottom: 10rem;
}
}
diff --git a/packages/bot-web-ui/src/components/dashboard/dashboard.tsx b/packages/bot-web-ui/src/components/dashboard/dashboard.tsx
index c88dbf6dd0f1..d29258b432c7 100644
--- a/packages/bot-web-ui/src/components/dashboard/dashboard.tsx
+++ b/packages/bot-web-ui/src/components/dashboard/dashboard.tsx
@@ -10,7 +10,6 @@ import { DBOT_TABS, TAB_IDS } from 'Constants/bot-contents';
import { useDBotStore } from 'Stores/useDBotStore';
import RunPanel from '../run-panel';
import RunStrategy from './dashboard-component/run-strategy';
-import BotNotification from './bot-notification';
import DashboardComponent from './dashboard-component';
import {
DBOT_ONBOARDING,
@@ -21,6 +20,7 @@ import {
tour_type,
} from './joyride-config';
import ReactJoyrideWrapper from './react-joyride-wrapper';
+import StrategyNotification from './strategy-notification';
import TourSlider from './tour-slider';
import TourTriggrerDialog from './tour-trigger-dialog';
import Tutorial from './tutorial-tab';
@@ -264,7 +264,7 @@ const Dashboard = observer(() => {
>
{dialog_options.message}
-
+
);
});
diff --git a/packages/bot-web-ui/src/components/dashboard/bot-notification.tsx b/packages/bot-web-ui/src/components/dashboard/strategy-notification.tsx
similarity index 92%
rename from packages/bot-web-ui/src/components/dashboard/bot-notification.tsx
rename to packages/bot-web-ui/src/components/dashboard/strategy-notification.tsx
index 6069b63a1815..8190d8882b18 100644
--- a/packages/bot-web-ui/src/components/dashboard/bot-notification.tsx
+++ b/packages/bot-web-ui/src/components/dashboard/strategy-notification.tsx
@@ -5,7 +5,7 @@ import { observer } from '@deriv/stores';
import { localize } from '@deriv/translations';
import { useDBotStore } from 'Stores/useDBotStore';
-const BotNotification = observer(() => {
+const StrategyNotification = observer(() => {
const { dashboard } = useDBotStore();
const { show_toast, toast_message, setOpenSettings } = dashboard;
@@ -33,4 +33,4 @@ const BotNotification = observer(() => {
return null;
});
-export default BotNotification;
+export default StrategyNotification;
diff --git a/packages/bot-web-ui/src/components/run-panel/run-panel.scss b/packages/bot-web-ui/src/components/run-panel/run-panel.scss
index d654730e0395..ea10f7406930 100644
--- a/packages/bot-web-ui/src/components/run-panel/run-panel.scss
+++ b/packages/bot-web-ui/src/components/run-panel/run-panel.scss
@@ -166,7 +166,6 @@
height: 6rem;
display: flex;
width: inherit;
- padding-right: 8px;
}
&__stop-button,
&__run-button {
diff --git a/packages/bot-web-ui/src/components/trade-animation/trade-animation.jsx b/packages/bot-web-ui/src/components/trade-animation/trade-animation.jsx
index 71de7214db6d..3db07001fece 100644
--- a/packages/bot-web-ui/src/components/trade-animation/trade-animation.jsx
+++ b/packages/bot-web-ui/src/components/trade-animation/trade-animation.jsx
@@ -1,11 +1,11 @@
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
-import { Button, Icon, Modal, Text } from '@deriv/components';
-import { isMobile } from '@deriv/shared';
+import { Button, Icon } from '@deriv/components';
import { observer, useStore } from '@deriv/stores';
import { Localize, localize } from '@deriv/translations';
import ContractResultOverlay from 'Components/contract-result-overlay';
+import BotStopNotification from 'Components/dashboard/bot-stop-notification';
import { contract_stages } from 'Constants/contract-stage';
import { useDBotStore } from 'Stores/useDBotStore';
@@ -16,44 +16,6 @@ const CircularWrapper = ({ className }) => (
);
-const AnimationInfo = ({ toggleAnimationInfoModal }) => {
- return (
-
-
-
- );
-};
-
-const AnimationInfoModal = ({ is_mobile, is_animation_info_modal_open, toggleAnimationInfoModal }) => {
- return (
-
-
-
-
- {localize(
- 'Stopping the bot will prevent further trades. Any ongoing trades will be completed by our system.'
- )}
-
-
- {localize(
- 'Please be aware that some completed transactions may not be displayed in the transaction table if the bot is stopped while placing trades.'
- )}
-
-
- {localize('You may refer to the statement page for details of all completed transactions.')}
-
-
-
-
- );
-};
-
const ContractStageText = ({ contract_stage }) => {
switch (contract_stage) {
case contract_stages.NOT_RUNNING:
@@ -72,8 +34,8 @@ const ContractStageText = ({ contract_stage }) => {
}
};
-const TradeAnimation = observer(({ className, info_direction }) => {
- const { run_panel, toolbar, summary_card } = useDBotStore();
+const TradeAnimation = observer(({ className }) => {
+ const { run_panel, summary_card } = useDBotStore();
const { client } = useStore();
const { is_contract_completed, profit } = summary_card;
const {
@@ -84,13 +46,13 @@ const TradeAnimation = observer(({ className, info_direction }) => {
onStopButtonClick,
performSelfExclusionCheck,
should_show_overlay,
+ show_bot_stop_message,
} = run_panel;
- const { is_animation_info_modal_open, toggleAnimationInfoModal } = toolbar;
const { account_status } = client;
const cashier_validation = account_status?.cashier_validation;
-
const [is_button_disabled, updateIsButtonDisabled] = React.useState(false);
const is_unavailable_for_payment_agent = cashier_validation?.includes('WithdrawServiceUnavailableForPA');
+
// perform self-exclusion checks which will be stored under the self-exclusion-store
React.useEffect(() => {
performSelfExclusionCheck();
@@ -127,13 +89,12 @@ const TradeAnimation = observer(({ className, info_direction }) => {
return (
- {info_direction === 'left' &&
}
}
+ icon={
}
onClick={() => {
updateIsButtonDisabled(true);
if (is_stop_button_visible) {
@@ -145,6 +106,7 @@ const TradeAnimation = observer(({ className, info_direction }) => {
has_effect
{...(is_stop_button_visible || !is_unavailable_for_payment_agent ? { primary: true } : { green: true })}
/>
+ {show_bot_stop_message &&
}
0,
@@ -164,19 +126,12 @@ const TradeAnimation = observer(({ className, info_direction }) => {
))}
- {info_direction === 'right' && }
-
);
});
TradeAnimation.propTypes = {
className: PropTypes.string,
- info_direction: PropTypes.string,
};
export default TradeAnimation;
diff --git a/packages/bot-web-ui/src/components/trade-animation/trade-animation.scss b/packages/bot-web-ui/src/components/trade-animation/trade-animation.scss
index 473352517115..5e4d246d3372 100644
--- a/packages/bot-web-ui/src/components/trade-animation/trade-animation.scss
+++ b/packages/bot-web-ui/src/components/trade-animation/trade-animation.scss
@@ -63,11 +63,6 @@
border-top-left-radius: 0;
border-bottom-right-radius: $BORDER_RADIUS;
}
- &__info {
- font-size: 10px;
- margin: 8px;
- cursor: pointer;
- }
// TODO: [fix-dc-bundle] Fix import issue with Deriv Component stylesheets (app should take precedence, and not repeat)
&__button {
width: fit-content;
@@ -187,30 +182,47 @@
}
}
}
- &__modal {
- height: 28.4rem !important;
- width: 44rem !important;
- font-size: 1.6rem;
- padding: 2.4rem;
+}
+
+.dc-modal__container_animation__modal {
+ @include mobile {
+ width: 31.2rem !important;
+ }
+}
+
+.bot-stop-notification {
+ position: fixed;
+ z-index: 9;
+ right: 38rem;
+ top: 12rem;
- &--mobile {
- font-size: 1.6rem !important;
+ .dc-toast {
+ width: 100%;
+ &__message {
+ background: var(--text-prominent);
+ color: var(--general-main-1);
+ padding: 1rem 1.6rem;
}
- &-body {
- height: 438px;
+ &__message-content {
+ display: flex;
- &--mobile {
- padding: 1.6rem 0 !important;
- }
- &--content {
- margin-top: 2rem;
+ @include mobile {
+ align-items: center;
}
}
}
-}
-.dc-modal__container_animation__modal {
@include mobile {
- width: 31.2rem !important;
+ top: unset;
+ left: 0;
+ right: 0;
+ bottom: 10.5rem;
+ }
+
+ .notification-close {
+ cursor: pointer;
+ filter: invert(1);
+ margin-left: 1rem;
+ margin-top: 0.1rem;
}
}
diff --git a/packages/bot-web-ui/src/stores/run-panel-store.js b/packages/bot-web-ui/src/stores/run-panel-store.js
index ceefa006727a..1923f7132edd 100644
--- a/packages/bot-web-ui/src/stores/run-panel-store.js
+++ b/packages/bot-web-ui/src/stores/run-panel-store.js
@@ -21,6 +21,7 @@ export default class RunPanelStore {
is_sell_requested: observable,
run_id: observable,
error_type: observable,
+ show_bot_stop_message: observable,
statistics: computed,
is_stop_button_visible: computed,
is_stop_button_disabled: computed,
@@ -52,6 +53,7 @@ export default class RunPanelStore {
setContractStage: action.bound,
setHasOpenContract: action.bound,
setIsRunning: action.bound,
+ setShowBotStopMessage: action.bound,
onMount: action.bound,
onUnmount: action.bound,
handleInvalidToken: action.bound,
@@ -82,6 +84,7 @@ export default class RunPanelStore {
is_drawer_open = true;
is_dialog_open = false;
is_sell_requested = false;
+ show_bot_stop_message = false;
run_id = '';
@@ -141,6 +144,10 @@ export default class RunPanelStore {
);
}
+ setShowBotStopMessage(value) {
+ this.show_bot_stop_message = value;
+ }
+
async performSelfExclusionCheck() {
const { self_exclusion } = this.root_store;
await self_exclusion.checkRestriction();
@@ -207,15 +214,20 @@ export default class RunPanelStore {
this.setContractStage(contract_stages.STARTING);
this.dbot.runBot();
});
+ this.setShowBotStopMessage(false);
}
onStopButtonClick() {
const { is_multiplier } = this.root_store.summary_card;
+ const { summary_card } = this.root_store;
if (is_multiplier) {
this.showStopMultiplierContractDialog();
} else {
this.stopBot();
+ this.dbot.terminateBot();
+ summary_card.clear();
+ this.setShowBotStopMessage(true);
}
}
@@ -553,7 +565,7 @@ export default class RunPanelStore {
onBotTradeAgain(is_trade_again) {
if (!is_trade_again) {
- this.onStopButtonClick();
+ this.stopBot();
}
}
diff --git a/packages/bot-web-ui/src/stores/toolbar-store.ts b/packages/bot-web-ui/src/stores/toolbar-store.ts
index e614e5286b37..dc4a8fa8ea4a 100644
--- a/packages/bot-web-ui/src/stores/toolbar-store.ts
+++ b/packages/bot-web-ui/src/stores/toolbar-store.ts
@@ -8,7 +8,6 @@ interface IToolbarStore {
file_name: { [key: string]: string };
has_undo_stack: boolean;
has_redo_stack: boolean;
- toggleAnimationInfoModal: () => void;
onResetClick: () => void;
closeResetDialog: () => void;
onResetOkButtonClick: () => void;
@@ -29,7 +28,6 @@ export default class ToolbarStore implements IToolbarStore {
file_name: observable,
has_undo_stack: observable,
has_redo_stack: observable,
- toggleAnimationInfoModal: action.bound,
onResetClick: action.bound,
closeResetDialog: action.bound,
onResetOkButtonClick: action.bound,
@@ -47,10 +45,6 @@ export default class ToolbarStore implements IToolbarStore {
has_undo_stack = false;
has_redo_stack = false;
- toggleAnimationInfoModal = (): void => {
- this.is_animation_info_modal_open = !this.is_animation_info_modal_open;
- };
-
onResetClick = (): void => {
this.is_dialog_open = true;
};
diff --git a/packages/components/src/components/icon/common/ic-bot-stop.svg b/packages/components/src/components/icon/common/ic-bot-stop.svg
new file mode 100644
index 000000000000..b976aecb0ac1
--- /dev/null
+++ b/packages/components/src/components/icon/common/ic-bot-stop.svg
@@ -0,0 +1 @@
+
\ No newline at end of file