<commit_before>import {
    action,
    computed,
    observable }                   from 'mobx';
import { WS }                      from 'Services';
import {
    epochToMoment,
    getDiffDuration }              from 'Utils/Date';
import { formatPortfolioPosition } from './Helpers/format-response';
import {
    getDurationUnitText,
    getDurationUnitValue }         from './Helpers/details';
import {
    getDisplayStatus,
    getEndSpotTime,
    isUserSold,
    isValidToSell }                from '../Contract/Helpers/logic';
import BaseStore                   from '../../base-store';

export default class PortfolioStore extends BaseStore {
    @observable positions  = [];
    @observable is_loading = false;
    @observable error      = '';

    @action.bound
    initializePortfolio = () => {
        if (!this.root_store.client.is_logged_in) return;
        this.is_loading = true;

        WS.portfolio().then(this.portfolioHandler);
        WS.subscribeProposalOpenContract(null, this.proposalOpenContractHandler, false);
        WS.subscribeTransaction(this.transactionHandler, false);
    };

    @action.bound
    clearTable() {
        this.positions  = [];
        this.is_loading = false;
        this.error      = '';
    }

    @action.bound
    portfolioHandler(response) {
        this.is_loading = false;
        if ('error' in response) {
            this.error = response.error.message;
            return;
        }
        this.error = '';
        if (response.portfolio.contracts) {
            this.positions = response.portfolio.contracts
                .map(pos => formatPortfolioPosition(pos))
                .sort((pos1, pos2) => pos2.reference - pos1.reference); // new contracts first
        }
    }

    @action.bound
    transactionHandler(response) {
        if ('error' in response) {
            this.error = response.error.message;
        }
        if (!response.transaction) return;
        const { contract_id, action: act } = response.transaction;

        if (act === 'buy') {
            WS.portfolio().then((res) => {
                const new_pos = res.portfolio.contracts.find(pos => +pos.contract_id === +contract_id);
                if (!new_pos) return;
                this.pushNewPosition(new_pos);
            });
            // subscribe to new contract:
            WS.subscribeProposalOpenContract(contract_id, this.proposalOpenContractHandler, false);
        } else if (act === 'sell') {
            // TODO: Refactor with contract-store and use common helpers to handle contract result
            WS.proposalOpenContract(contract_id).then(action((proposal_response) => {
                // populate result details box for specified positions card
                this.populateResultDetails(proposal_response);
            }));
        }
    }

    @action.bound
    proposalOpenContractHandler(response) {
        if ('error' in response) return;

        const proposal = response.proposal_open_contract;
        const portfolio_position = this.positions.find((position) => +position.id === +proposal.contract_id);

        if (!portfolio_position) return;

        const prev_indicative = portfolio_position.indicative;
        const new_indicative  = +proposal.bid_price;
        const profit_loss     = +proposal.profit;

        portfolio_position.purchase_time = (proposal.is_forward_starting === 1) ?
            proposal.date_start
            :
            proposal.purchase_time;

        portfolio_position.bid_price        = proposal.bid_price;
        portfolio_position.indicative       = new_indicative;
        portfolio_position.underlying_code  = proposal.underlying;
        portfolio_position.underlying_name  = proposal.display_name;
        portfolio_position.profit_loss      = profit_loss;
        portfolio_position.tick_count       = proposal.tick_count;
        portfolio_position.is_valid_to_sell = isValidToSell(proposal);

        if (!proposal.is_valid_to_sell) {
            portfolio_position.status = 'no-resale';
        } else if (new_indicative > prev_indicative) {
            portfolio_position.status = 'price-moved-up';
        } else if (new_indicative < prev_indicative) {
            portfolio_position.status = 'price-moved-down';
        } else {
            portfolio_position.status = 'price-stable';
        }
    }

    @action.bound
    onClickSell(contract_id) {
        const i = this.positions.findIndex(pos => +pos.id === +contract_id);
        const bid_price = this.positions[i].bid_price;
        if (contract_id && bid_price) {
            WS.sell(contract_id, bid_price).then(this.handleSell);
        }
    }

    @action.bound
    handleSell(response) {
        const is_contract_mode = this.root_store.modules.smart_chart.is_contract_mode;
        // TODO: Refactor with ContractStore for re-drawing of chart markers and barriers
        // Toast messages are temporary UI for prompting user of sold contracts
        if (!is_contract_mode && response.error) {
            // If unable to sell due to error, give error via toast message if not in contract mode
            this.root_store.ui.addToastMessage({
                message: response.error.message,
                type   : 'error',
            });
        // Check if still in contract_mode
        } else if (is_contract_mode && !response.error) {
            WS.forget('proposal_open_contract', this.root_store.modules.contract.updateProposal, { contract_id: response.sell.contract_id });
            WS.proposalOpenContract(response.sell.contract_id).then(action((proposal_response) => {
                // update contract store proposal after sell
                this.root_store.modules.contract.updateProposal(proposal_response);
                this.populateResultDetails(proposal_response);
            }));
            // update contract store sell info after sell
            this.root_store.modules.contract.sell_info = {
                sell_price    : response.sell.sold_for,
                transaction_id: response.sell.transaction_id,
            };
        }
    }

    populateResultDetails(response) {
        const contract_response = response.proposal_open_contract;
        const i = this.positions.findIndex(pos => +pos.id === +contract_response.contract_id);
        const sell_time = isUserSold(contract_response) ?
            +contract_response.date_expiry
            :
            getEndSpotTime(contract_response);
        const duration_diff =
            getDiffDuration(
                epochToMoment(this.positions[i].purchase_time || this.positions[i].date_start),
                epochToMoment(this.positions[i].expiry_time)
            );
        const duration = this.positions[i].tick_count ?
            this.positions[i].tick_count
            :
            getDurationUnitValue(duration_diff);

        this.positions[i].id_sell       = +contract_response.transaction_ids.sell;<commit_msg>show toast message when not in contract mode if sell from positions drawer<commit_after>import {
    action,
    computed,
    observable }                   from 'mobx';
import { WS }                      from 'Services';
import {
    epochToMoment,
    getDiffDuration }              from 'Utils/Date';
import { formatPortfolioPosition } from './Helpers/format-response';
import {
    getDurationUnitText,
    getDurationUnitValue }         from './Helpers/details';
import {
    getDisplayStatus,
    getEndSpotTime,
    isUserSold,
    isValidToSell }                from '../Contract/Helpers/logic';
import BaseStore                   from '../../base-store';

export default class PortfolioStore extends BaseStore {
    @observable positions  = [];
    @observable is_loading = false;
    @observable error      = '';

    @action.bound
    initializePortfolio = () => {
        if (!this.root_store.client.is_logged_in) return;
        this.is_loading = true;

        WS.portfolio().then(this.portfolioHandler);
        WS.subscribeProposalOpenContract(null, this.proposalOpenContractHandler, false);
        WS.subscribeTransaction(this.transactionHandler, false);
    };

    @action.bound
    clearTable() {
        this.positions  = [];
        this.is_loading = false;
        this.error      = '';
    }

    @action.bound
    portfolioHandler(response) {
        this.is_loading = false;
        if ('error' in response) {
            this.error = response.error.message;
            return;
        }
        this.error = '';
        if (response.portfolio.contracts) {
            this.positions = response.portfolio.contracts
                .map(pos => formatPortfolioPosition(pos))
                .sort((pos1, pos2) => pos2.reference - pos1.reference); // new contracts first
        }
    }

    @action.bound
    transactionHandler(response) {
        if ('error' in response) {
            this.error = response.error.message;
        }
        if (!response.transaction) return;
        const { contract_id, action: act } = response.transaction;

        if (act === 'buy') {
            WS.portfolio().then((res) => {
                const new_pos = res.portfolio.contracts.find(pos => +pos.contract_id === +contract_id);
                if (!new_pos) return;
                this.pushNewPosition(new_pos);
            });
            // subscribe to new contract:
            WS.subscribeProposalOpenContract(contract_id, this.proposalOpenContractHandler, false);
        } else if (act === 'sell') {
            // TODO: Refactor with contract-store and use common helpers to handle contract result
            WS.proposalOpenContract(contract_id).then(action((proposal_response) => {
                // populate result details box for specified positions card
                this.populateResultDetails(proposal_response);
            }));
        }
    }

    @action.bound
    proposalOpenContractHandler(response) {
        if ('error' in response) return;

        const proposal = response.proposal_open_contract;
        const portfolio_position = this.positions.find((position) => +position.id === +proposal.contract_id);

        if (!portfolio_position) return;

        const prev_indicative = portfolio_position.indicative;
        const new_indicative  = +proposal.bid_price;
        const profit_loss     = +proposal.profit;

        portfolio_position.purchase_time = (proposal.is_forward_starting === 1) ?
            proposal.date_start
            :
            proposal.purchase_time;

        portfolio_position.bid_price        = proposal.bid_price;
        portfolio_position.indicative       = new_indicative;
        portfolio_position.underlying_code  = proposal.underlying;
        portfolio_position.underlying_name  = proposal.display_name;
        portfolio_position.profit_loss      = profit_loss;
        portfolio_position.tick_count       = proposal.tick_count;
        portfolio_position.is_valid_to_sell = isValidToSell(proposal);

        if (!proposal.is_valid_to_sell) {
            portfolio_position.status = 'no-resale';
        } else if (new_indicative > prev_indicative) {
            portfolio_position.status = 'price-moved-up';
        } else if (new_indicative < prev_indicative) {
            portfolio_position.status = 'price-moved-down';
        } else {
            portfolio_position.status = 'price-stable';
        }
    }

    @action.bound
    onClickSell(contract_id) {
        const i = this.positions.findIndex(pos => +pos.id === +contract_id);
        const bid_price = this.positions[i].bid_price;
        if (contract_id && bid_price) {
            WS.sell(contract_id, bid_price).then(this.handleSell);
        }
    }

    @action.bound
    handleSell(response) {
        const is_contract_mode = this.root_store.modules.smart_chart.is_contract_mode;
        // TODO: Refactor with ContractStore for re-drawing of chart markers and barriers
        // Toast messages are temporary UI for prompting user of sold contracts
        if (!is_contract_mode && response.error) {
            // If unable to sell due to error, give error via toast message if not in contract mode
            this.root_store.ui.addToastMessage({
                message: response.error.message,
                type   : 'error',
            });
        // Check if still in contract_mode
        } else if (is_contract_mode && !response.error) {
            WS.forget('proposal_open_contract', this.root_store.modules.contract.updateProposal, { contract_id: response.sell.contract_id });
            WS.proposalOpenContract(response.sell.contract_id).then(action((proposal_response) => {
                // update contract store proposal after sell
                this.root_store.modules.contract.updateProposal(proposal_response);
                this.populateResultDetails(proposal_response);
            }));
            // update contract store sell info after sell
            this.root_store.modules.contract.sell_info = {
                sell_price    : response.sell.sold_for,
                transaction_id: response.sell.transaction_id,
            };
        } else if (!is_contract_mode && response.sell.sold_for) {
            this.root_store.ui.addToastMessage({
                message: `Contract was sold for ${response.sell.sold_for}.`,
                type   : 'info',
            });
        }
    }

    populateResultDetails(response) {
        const contract_response = response.proposal_open_contract;
        const i = this.positions.findIndex(pos => +pos.id === +contract_response.contract_id);
        const sell_time = isUserSold(contract_response) ?
            +contract_response.date_expiry
            :
            getEndSpotTime(contract_response);
        const duration_diff =
            getDiffDuration(
                epochToMoment(this.positions[i].purchase_time || this.positions[i].date_start),
                epochToMoment(this.positions[i].expiry_time)
            );
        const duration = this.positions[i].tick_count ?
            this.positions[i].tick_count
            :
            getDurationUnitValue(duration_diff);

        this.positions[i].id_sell       = +contract_response.transaction_ids.sell;
{'fraction_non_alphanumeric': 0.09830388692579506, 'fraction_numerical': 0.0007067137809187279, 'mean_word_length': 2.2052095130237825, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4626750', 'n_tokens_mistral': 3919, 'n_tokens_neox': 3797, 'n_words': 718}