From 40354eeda30275f1474d526fa99d2ee76cce89cd Mon Sep 17 00:00:00 2001 From: Niloofar Sadeghi Date: Sun, 9 Oct 2022 12:26:52 +0330 Subject: [PATCH] refactor: convert helpers to TS --- .../src/utils/contract/contract-types.ts | 77 +++++++++++++++ .../utils/date/{date-time.js => date-time.ts} | 94 ++++++++++-------- .../__tests__/{barrier.js => barrier.ts} | 10 +- .../__tests__/{barriers.js => barriers.ts} | 38 +------- .../__tests__/{durations.js => durations.ts} | 37 ++----- ...{format-response.js => format-response.ts} | 0 .../{start-date.js => start-date.ts} | 4 +- .../{active-symbols.js => active-symbols.ts} | 70 ++++++++----- packages/shared/src/utils/helpers/barrier.js | 20 ---- packages/shared/src/utils/helpers/barrier.ts | 30 ++++++ packages/shared/src/utils/helpers/barriers.js | 28 ------ packages/shared/src/utils/helpers/barriers.ts | 15 +++ ...tifications.js => chart-notifications.tsx} | 0 .../utils/helpers/{details.js => details.ts} | 22 +++-- .../helpers/{duration.js => duration.ts} | 97 ++++++++++++++----- .../src/utils/helpers/format-response.js | 29 ------ .../src/utils/helpers/format-response.ts | 56 +++++++++++ .../src/utils/helpers/{index.js => index.ts} | 0 packages/shared/src/utils/helpers/logic.js | 58 ----------- packages/shared/src/utils/helpers/logic.ts | 91 +++++++++++++++++ ...ket-underlying.js => market-underlying.ts} | 15 ++- ...cations.js => portfolio-notifications.tsx} | 2 +- .../helpers/{start-date.js => start-date.ts} | 25 ++++- ...alidation-rules.js => validation-rules.ts} | 17 +++- 24 files changed, 527 insertions(+), 308 deletions(-) create mode 100644 packages/shared/src/utils/contract/contract-types.ts rename packages/shared/src/utils/date/{date-time.js => date-time.ts} (62%) rename packages/shared/src/utils/helpers/__tests__/{barrier.js => barrier.ts} (93%) rename packages/shared/src/utils/helpers/__tests__/{barriers.js => barriers.ts} (56%) rename packages/shared/src/utils/helpers/__tests__/{durations.js => durations.ts} (76%) rename packages/shared/src/utils/helpers/__tests__/{format-response.js => format-response.ts} (100%) rename packages/shared/src/utils/helpers/__tests__/{start-date.js => start-date.ts} (85%) rename packages/shared/src/utils/helpers/{active-symbols.js => active-symbols.ts} (71%) delete mode 100644 packages/shared/src/utils/helpers/barrier.js create mode 100644 packages/shared/src/utils/helpers/barrier.ts delete mode 100644 packages/shared/src/utils/helpers/barriers.js create mode 100644 packages/shared/src/utils/helpers/barriers.ts rename packages/shared/src/utils/helpers/{chart-notifications.js => chart-notifications.tsx} (100%) rename packages/shared/src/utils/helpers/{details.js => details.ts} (82%) rename packages/shared/src/utils/helpers/{duration.js => duration.ts} (55%) delete mode 100644 packages/shared/src/utils/helpers/format-response.js create mode 100644 packages/shared/src/utils/helpers/format-response.ts rename packages/shared/src/utils/helpers/{index.js => index.ts} (100%) delete mode 100644 packages/shared/src/utils/helpers/logic.js create mode 100644 packages/shared/src/utils/helpers/logic.ts rename packages/shared/src/utils/helpers/{market-underlying.js => market-underlying.ts} (60%) rename packages/shared/src/utils/helpers/{portfolio-notifications.js => portfolio-notifications.tsx} (89%) rename packages/shared/src/utils/helpers/{start-date.js => start-date.ts} (61%) rename packages/shared/src/utils/helpers/{validation-rules.js => validation-rules.ts} (74%) diff --git a/packages/shared/src/utils/contract/contract-types.ts b/packages/shared/src/utils/contract/contract-types.ts new file mode 100644 index 000000000000..5af91ba65d80 --- /dev/null +++ b/packages/shared/src/utils/contract/contract-types.ts @@ -0,0 +1,77 @@ +import { ContractUpdate } from '@deriv/api-types'; + +export type TStatus = 'open' | 'sold' | 'won' | 'lost' | 'cancelled'; + +export type TGetFinalPrice = { + sell_price: number; + bid_price: number; +}; + +export type TIsEnded = Partial & { + is_valid_to_sell?: 0 | 1; + status?: TStatus; + is_expired?: 0 | 1; + is_settleable?: 0 | 1; +}; + +export type TContractInfo = { + tick_stream?: TTickItem[]; + cancellation?: { + ask_price?: number; + date_expiry?: number; + }; + status?: TStatus; + is_expired?: 0 | 1; + is_settleable?: 0 | 1; + is_valid_to_cancel?: 0 | 1; + entry_spot?: number; + profit?: number; + entry_tick_time?: number; + entry_tick?: number; + current_spot_time?: number; + current_spot?: number; + barrier?: string; + contract_type?: string; + exit_tick_time?: number; + date_expiry?: number; + is_path_dependent?: 0 | 1; + sell_time?: number | null; + tick_count?: number; + date_start?: number; + is_forward_starting?: 0 | 1; +}; + +export type TIsValidToSell = TIsEnded & { + is_valid_to_sell: 0 | 1; +}; + +export type TTickItem = { + epoch?: number; + tick?: null | number; + tick_display_value?: null | string; +}; + +export type TDigitsInfo = { [key: number]: { digit: number; spot: string } }; + +export type TGetTotalProfit = { + bid_price: number; + buy_price: number; +}; + +type TLimitProperty = { + display_name?: string; + order_amount?: null | number; + order_date?: number; + value?: null | string; +}; + +export type TLimitOrder = Partial>; + +export type TGetDisplayStatus = TGetTotalProfit & { + status: TStatus; +}; + +export type TGetContractUpdateConfig = { + contract_update: ContractUpdate; + limit_order: TLimitOrder; +}; diff --git a/packages/shared/src/utils/date/date-time.js b/packages/shared/src/utils/date/date-time.ts similarity index 62% rename from packages/shared/src/utils/date/date-time.js rename to packages/shared/src/utils/date/date-time.ts index bd30ab91cea2..844bc3426aa7 100644 --- a/packages/shared/src/utils/date/date-time.js +++ b/packages/shared/src/utils/date/date-time.ts @@ -1,21 +1,25 @@ import moment from 'moment'; import 'moment/min/locales'; +type TExtendedMoment = typeof moment & { + createFromInputFallback: (config: { _d: Date }) => void; +}; + // Disables moment's fallback to native Date object // moment will return `Invalid Date` if date cannot be parsed -moment.createFromInputFallback = function (config) { +(moment as TExtendedMoment).createFromInputFallback = function (config) { config._d = new Date(NaN); // eslint-disable-line no-underscore-dangle }; // Localize moment instance with specific object -export const initMoment = lang => moment.locale(lang); +export const initMoment = (lang: string) => moment.locale(lang); /** * Convert epoch to moment object * @param {Number} epoch * @return {moment} the moment object of provided epoch */ -export const epochToMoment = epoch => moment.unix(epoch).utc(); +export const epochToMoment = (epoch: number) => moment.unix(epoch).utc(); /** * Convert date string or epoch to moment object @@ -23,33 +27,34 @@ export const epochToMoment = epoch => moment.unix(epoch).utc(); * @param {String} value the date in string format * @return {moment} the moment object of 'now' or the provided date epoch or string */ -export const toMoment = value => { +export const toMoment = (value?: moment.MomentInput): moment.Moment => { if (!value) return moment().utc(); // returns 'now' moment object - if (value instanceof moment && value.isValid() && value.isUTC()) return value; // returns if already a moment object + if (value instanceof moment && (value as moment.Moment).isValid() && (value as moment.Moment).isUTC()) + return value as moment.Moment; // returns if already a moment object if (typeof value === 'number') return epochToMoment(value); // returns epochToMoment() if not a date - if (/invalid/i.test(moment(value))) { + if (/invalid/i.test(moment(value).toString())) { const today_moment = moment(); return value > today_moment.utc().daysInMonth() - ? moment.utc(today_moment.add(value, 'd'), 'DD MMM YYYY') + ? moment.utc(today_moment.add(value as string | number, 'd'), 'DD MMM YYYY') : moment.utc(value, 'DD MMM YYYY'); // returns target date } return moment.utc(value); }; -export const toLocalFormat = time => moment.utc(time).local().format('YYYY-MM-DD HH:mm:ss Z'); +export const toLocalFormat = (time: moment.MomentInput) => moment.utc(time).local().format('YYYY-MM-DD HH:mm:ss Z'); /** * Set specified time on moment object * @param {moment} moment_obj the moment to set the time on * @param {String} time 24 hours format, may or may not include seconds * @return {moment} a new moment object of result */ -export const setTime = (moment_obj, time) => { +export const setTime = (moment_obj: moment.Moment, time: string) => { const [hour, minute, second] = time ? time.split(':') : [0, 0, 0]; moment_obj - .hour(hour) - .minute(minute || 0) - .second(second || 0); + .hour(+hour) + .minute(+minute || 0) + .second(+second || 0); return moment_obj; }; @@ -59,23 +64,24 @@ export const setTime = (moment_obj, time) => { * @param {String} time the time to set on the date * @return {Number} unix value of the result */ -export const convertToUnix = (epoch, time) => setTime(toMoment(epoch), time).unix(); +export const convertToUnix = (epoch: number | string, time: string) => setTime(toMoment(epoch), time).unix(); -export const toGMTFormat = time => +export const toGMTFormat = (time?: moment.MomentInput) => moment(time || undefined) .utc() .format('YYYY-MM-DD HH:mm:ss [GMT]'); -export const formatDate = (date, date_format = 'YYYY-MM-DD') => toMoment(date).format(date_format); +export const formatDate = (date?: moment.MomentInput, date_format = 'YYYY-MM-DD') => toMoment(date).format(date_format); -export const formatTime = (epoch, time_format = 'HH:mm:ss [GMT]') => toMoment(epoch).format(time_format); +export const formatTime = (epoch: number | string, time_format = 'HH:mm:ss [GMT]') => + toMoment(epoch).format(time_format); /** * return the number of days from today to date specified * @param {String} date the date to calculate number of days from today * @return {Number} an integer of the number of days */ -export const daysFromTodayTo = date => { +export const daysFromTodayTo = (date?: string) => { const diff = toMoment(date).startOf('day').diff(toMoment().startOf('day'), 'days'); return !date || diff < 0 ? '' : diff; }; @@ -85,7 +91,7 @@ export const daysFromTodayTo = date => { * @param {String} date the date to calculate number of days since * @return {Number} an integer of the number of days */ -export const daysSince = date => { +export const daysSince = (date: string) => { const diff = toMoment().startOf('day').diff(toMoment(date).startOf('day'), 'days'); return !date ? '' : diff; }; @@ -93,18 +99,22 @@ export const daysSince = date => { /** * return the number of months between two specified dates */ -export const diffInMonths = (now, then) => then.diff(now, 'month'); +export const diffInMonths = (now: moment.MomentInput, then: moment.Moment) => then.diff(now, 'month'); /** * return moment duration between two dates * @param {Number} epoch start time * @param {Number} epoch end time * @return {moment.duration} moment duration between start time and end time */ -export const getDiffDuration = (start_time, end_time) => +export const getDiffDuration = (start_time: number, end_time: number) => moment.duration(moment.unix(end_time).diff(moment.unix(start_time))); /** returns the DD MM YYYY format */ -export const getDateFromNow = (days, unit, format) => { +export const getDateFromNow = ( + days: string | number, + unit?: moment.unitOfTime.DurationConstructor, + format?: string +) => { const date = moment(new Date()); return date.add(days, unit).format(format); }; @@ -114,7 +124,7 @@ export const getDateFromNow = (days, unit, format) => { * @param {moment.duration} moment duration object * @return {String} formatted display string */ -export const formatDuration = (duration, format) => { +export const formatDuration = (duration: moment.Duration, format?: string) => { const d = Math.floor(duration.asDays()); // duration.days() does not include months/years const h = duration.hours(); const m = duration.minutes(); @@ -135,95 +145,99 @@ export const formatDuration = (duration, format) => { * return true if the time_str is in "HH:MM" format, else return false * @param {String} time_str time */ -export const isTimeValid = time_str => +export const isTimeValid = (time_str: string) => /^([0-9]|[0-1][0-9]|2[0-3]):([0-9]|[0-5][0-9])(:([0-9]|[0-5][0-9]))?$/.test(time_str); /** * return true if the time_str's hour is between 0 and 23, else return false * @param {String} time_str time */ -export const isHourValid = time_str => isTimeValid(time_str) && /^([01][0-9]|2[0-3])$/.test(time_str.split(':')[0]); +export const isHourValid = (time_str: string) => + isTimeValid(time_str) && /^([01][0-9]|2[0-3])$/.test(time_str.split(':')[0]); /** * return true if the time_str's minute is between 0 and 59, else return false * @param {String} time_str time */ -export const isMinuteValid = time_str => isTimeValid(time_str) && /^[0-5][0-9]$/.test(time_str.split(':')[1]); +export const isMinuteValid = (time_str: string) => isTimeValid(time_str) && /^[0-5][0-9]$/.test(time_str.split(':')[1]); /** * return true if the date is typeof string and a valid moment date, else return false * @param {String|moment} date date */ -export const isDateValid = date => moment(date, 'DD MMM YYYY').isValid(); +export const isDateValid = (date: moment.MomentInput) => moment(date, 'DD MMM YYYY').isValid(); /** * add the specified number of days to the given date * @param {String} date date * @param {Number} num_of_days number of days to add */ -export const addDays = (date, num_of_days) => toMoment(date).clone().add(num_of_days, 'day'); +export const addDays = (date: string, num_of_days: number) => toMoment(date).clone().add(num_of_days, 'day'); /** * add the specified number of weeks to the given date * @param {String} date date * @param {Number} num_of_weeks number of days to add */ -export const addWeeks = (date, num_of_weeks) => toMoment(date).clone().add(num_of_weeks, 'week'); +export const addWeeks = (date: string, num_of_weeks: number) => toMoment(date).clone().add(num_of_weeks, 'week'); /** * add the specified number of months to the given date * @param {String} date date * @param {Number} num_of_months number of months to add */ -export const addMonths = (date, num_of_months) => toMoment(date).clone().add(num_of_months, 'month'); +export const addMonths = (date: string, num_of_months: number) => toMoment(date).clone().add(num_of_months, 'month'); /** * add the specified number of years to the given date * @param {String} date date * @param {Number} num_of_years number of years to add */ -export const addYears = (date, num_of_years) => toMoment(date).clone().add(num_of_years, 'year'); +export const addYears = (date: string, num_of_years: number) => toMoment(date).clone().add(num_of_years, 'year'); /** * subtract the specified number of days from the given date * @param {String} date date * @param {Number} num_of_days number of days to subtract */ -export const subDays = (date, num_of_days) => toMoment(date).clone().subtract(num_of_days, 'day'); +export const subDays = (date: string, num_of_days: number) => toMoment(date).clone().subtract(num_of_days, 'day'); /** * subtract the specified number of months from the given date * @param {String} date date * @param {Number} num_of_months number of months to subtract */ -export const subMonths = (date, num_of_months) => toMoment(date).clone().subtract(num_of_months, 'month'); +export const subMonths = (date: string, num_of_months: number) => + toMoment(date).clone().subtract(num_of_months, 'month'); /** * subtract the specified number of years from the given date * @param {String} date date * @param {Number} num_of_years number of years to subtract */ -export const subYears = (date, num_of_years) => toMoment(date).clone().subtract(num_of_years, 'year'); +export const subYears = (date: string, num_of_years: number) => toMoment(date).clone().subtract(num_of_years, 'year'); /** * returns the minimum moment between the two passing parameters * @param {moment|string|epoch} first datetime parameter * @param {moment|string|epoch} second datetime parameter */ -export const minDate = (date_1, date_2) => moment.min(toMoment(date_1), toMoment(date_2)); +export const minDate = (date_1: moment.MomentInput, date_2: moment.MomentInput) => + moment.min(toMoment(date_1), toMoment(date_2)); /** * returns a new date * @param {moment|string|epoch} date date */ -export const getStartOfMonth = date => toMoment(date).clone().startOf('month').format('YYYY-MM-DD'); +export const getStartOfMonth = (date: moment.MomentInput) => + toMoment(date).clone().startOf('month').format('YYYY-MM-DD'); /** * returns miliseconds into UTC formatted string * @param {Number} miliseconds miliseconds * @param {String} str_format formatting using moment e.g - YYYY-MM-DD HH:mm */ -export const formatMilliseconds = (miliseconds, str_format, is_local_time = false) => { +export const formatMilliseconds = (miliseconds: moment.MomentInput, str_format: string, is_local_time = false) => { if (is_local_time) { return moment(miliseconds).format(str_format); } @@ -236,7 +250,7 @@ export const formatMilliseconds = (miliseconds, str_format, is_local_time = fals * @param {String} from_date_format initial date format * @param {String} to_date_format to date format */ -export const convertDateFormat = (date, from_date_format, to_date_format) => +export const convertDateFormat = (date: moment.MomentInput, from_date_format: string, to_date_format: string) => moment(date, from_date_format).format(to_date_format); /** @@ -244,11 +258,11 @@ export const convertDateFormat = (date, from_date_format, to_date_format) => * @param {String} time 24 hours format, may or may not include seconds * @return {String} equivalent 12-hour time */ -export const convertTimeFormat = time => { +export const convertTimeFormat = (time: string) => { const time_moment_obj = moment(time, 'HH:mm'); const time_hour = time_moment_obj.format('HH'); const time_min = time_moment_obj.format('mm'); - const formatted_time = `${Number(time_hour % 12) || 12}:${time_min}`; - const time_suffix = `${Number(time_hour >= 12) ? 'pm' : 'am'}`; + const formatted_time = `${Number(time_hour) % 12 || 12}:${time_min}`; + const time_suffix = `${Number(time_hour) >= 12 ? 'pm' : 'am'}`; return `${formatted_time} ${time_suffix}`; }; diff --git a/packages/shared/src/utils/helpers/__tests__/barrier.js b/packages/shared/src/utils/helpers/__tests__/barrier.ts similarity index 93% rename from packages/shared/src/utils/helpers/__tests__/barrier.js rename to packages/shared/src/utils/helpers/__tests__/barrier.ts index bdc7e2b23098..44b4950f5ce2 100644 --- a/packages/shared/src/utils/helpers/__tests__/barrier.js +++ b/packages/shared/src/utils/helpers/__tests__/barrier.ts @@ -48,9 +48,9 @@ describe('buildBarriersConfig', () => { const contract = { ...contract_obj, barriers: 1, - low_barrier: 22, - barrier: 33, - high_barrier: 44, + low_barrier: '22', + barrier: '33', + high_barrier: '44', }; expect(buildBarriersConfig(contract)).to.eql({ count: 1, @@ -66,8 +66,8 @@ describe('buildBarriersConfig', () => { const contract = { ...contract_obj, barriers: 1, - low_barrier: 22, - barrier: 33, + low_barrier: '22', + barrier: '33', }; expect(buildBarriersConfig(contract)).to.eql({ count: 1, diff --git a/packages/shared/src/utils/helpers/__tests__/barriers.js b/packages/shared/src/utils/helpers/__tests__/barriers.ts similarity index 56% rename from packages/shared/src/utils/helpers/__tests__/barriers.js rename to packages/shared/src/utils/helpers/__tests__/barriers.ts index af2b46b7ec0f..13a2b66380eb 100644 --- a/packages/shared/src/utils/helpers/__tests__/barriers.js +++ b/packages/shared/src/utils/helpers/__tests__/barriers.ts @@ -13,46 +13,16 @@ describe('Barriers', () => { describe('barriersToString', () => { it('should convert non-zero barriers which do not have +/- to string consisting of them without +/- while is_relative is false', () => { - expect(Barriers.barriersToString(false, 10, 15)).to.deep.eql(['10', '15']); + expect(Barriers.barriersToString(false, 10, 15)).to.deep.equal(['10', '15']); }); it('should convert values without +/- and zero to string consisting of them without +/- while is_relative is false', () => { - expect(Barriers.barriersToString(false, 0, 15)).to.deep.eql(['0', '15']); + expect(Barriers.barriersToString(false, 0, 15)).to.deep.equal(['0', '15']); }); it('should convert barriers which have +/- to string consisting of them without +/- while is_relative is false', () => { - expect(Barriers.barriersToString(false, +11, 15)).to.deep.eql(['11', '15']); + expect(Barriers.barriersToString(false, +11, 15)).to.deep.equal(['11', '15']); }); it('should convert barriers which have +/- to string consisting of them with +/- while is_relative is true', () => { - expect(Barriers.barriersToString(true, +11, +15)).to.deep.eql(['+11', '+15']); - }); - }); - - describe('barriersObjectToArray', () => { - const main = { - color: 'green', - draggable: false, - }; - it('should return an array from values in barriers object', () => { - const barriers = { - main, - }; - expect(Barriers.barriersObjectToArray(barriers, [])).to.deep.eql([ - { - color: 'green', - draggable: false, - }, - ]); - }); - it('should return an array from values in barriers object (empty values should be filtered out)', () => { - const barriers = { - main, - somethingEmpty: {}, - }; - expect(Barriers.barriersObjectToArray(barriers, [])).to.deep.eql([ - { - color: 'green', - draggable: false, - }, - ]); + expect(Barriers.barriersToString(true, +11, +15)).to.deep.equal(['+11', '+15']); }); }); }); diff --git a/packages/shared/src/utils/helpers/__tests__/durations.js b/packages/shared/src/utils/helpers/__tests__/durations.ts similarity index 76% rename from packages/shared/src/utils/helpers/__tests__/durations.js rename to packages/shared/src/utils/helpers/__tests__/durations.ts index dd02339bb848..a0712b4e0ada 100644 --- a/packages/shared/src/utils/helpers/__tests__/durations.js +++ b/packages/shared/src/utils/helpers/__tests__/durations.ts @@ -50,40 +50,28 @@ describe('buildDurationConfig', () => { }); describe('convertDurationUnit', () => { - it('Returns null if the arguments are empty value', () => { - expect(Duration.convertDurationUnit('', '', '')).to.be.null; - }); - - it('Returns null if the arguments are invalid value', () => { - expect(Duration.convertDurationUnit('sdf', 'd', 'm')).to.be.null; - }); - - it('Returns null if there is no arguments', () => { - expect(Duration.convertDurationUnit()).to.be.null; - }); - it('Returns correct value convert day to second', () => { - expect(Duration.convertDurationUnit('365', 'd', 's')).to.eql(31536000); + expect(Duration.convertDurationUnit(365, 'd', 's')).to.eql(31536000); }); it('Returns correct value convert minute to second', () => { - expect(Duration.convertDurationUnit('5', 'm', 's')).to.eql(300); + expect(Duration.convertDurationUnit(5, 'm', 's')).to.eql(300); }); it('Returns correct value convert day to minute', () => { - expect(Duration.convertDurationUnit('1', 'd', 'm')).to.eql(1440); + expect(Duration.convertDurationUnit(1, 'd', 'm')).to.eql(1440); }); it('Returns correct value convert second to minute', () => { - expect(Duration.convertDurationUnit('180', 's', 'm')).to.eql(3); + expect(Duration.convertDurationUnit(180, 's', 'm')).to.eql(3); }); it('Returns correct value convert minute to day', () => { - expect(Duration.convertDurationUnit('2880', 'm', 'd')).to.eql(2); + expect(Duration.convertDurationUnit(2880, 'm', 'd')).to.eql(2); }); it('Returns correct value convert second to day', () => { - expect(Duration.convertDurationUnit('86400', 's', 'd')).to.eql(1); + expect(Duration.convertDurationUnit(86400, 's', 'd')).to.eql(1); }); }); @@ -100,6 +88,9 @@ describe('getExpiryType', () => { { text: 'hours', value: 'h' }, { text: 'days', value: 'd' }, ], + expiry_date: '', + expiry_type: '', + duration_unit: '', }; it('Return intraday if expiry date is today', () => { @@ -109,7 +100,7 @@ describe('getExpiryType', () => { }); it('Return daily if expiry date is tomorrow', () => { - store.expiry_date = moment().utc().add(1, 'days'); + store.expiry_date = moment().utc().add(1, 'days').toString(); store.expiry_type = 'endtime'; expect(Duration.getExpiryType(store)).to.eql('daily'); }); @@ -134,14 +125,6 @@ describe('getExpiryType', () => { }); describe('convertDurationLimit', () => { - it('Returns null when there are no arguments', () => { - expect(Duration.convertDurationLimit('', '')).to.be.null; - }); - - it('Returns null for invalid value', () => { - expect(Duration.convertDurationLimit('sdf', 't')).to.be.null; - }); - it('Returns correct value for ticks unit', () => { expect(Duration.convertDurationLimit(5, 't')).to.eql(5); }); diff --git a/packages/shared/src/utils/helpers/__tests__/format-response.js b/packages/shared/src/utils/helpers/__tests__/format-response.ts similarity index 100% rename from packages/shared/src/utils/helpers/__tests__/format-response.js rename to packages/shared/src/utils/helpers/__tests__/format-response.ts diff --git a/packages/shared/src/utils/helpers/__tests__/start-date.js b/packages/shared/src/utils/helpers/__tests__/start-date.ts similarity index 85% rename from packages/shared/src/utils/helpers/__tests__/start-date.js rename to packages/shared/src/utils/helpers/__tests__/start-date.ts index 6c143d2eac87..6feff2077ef3 100644 --- a/packages/shared/src/utils/helpers/__tests__/start-date.js +++ b/packages/shared/src/utils/helpers/__tests__/start-date.ts @@ -20,8 +20,10 @@ describe('start_date', () => { start_type: 'spot', submarket: 'major_pairs', underlying_symbol: 'frxAUDJPY', + forward_starting_options: [], }; - expect(buildForwardStartingConfig(contract, {})).to.be.empty; + /* eslint-disable no-unused-expressions */ + expect(buildForwardStartingConfig(contract, [])).to.be.empty; }); }); }); diff --git a/packages/shared/src/utils/helpers/active-symbols.js b/packages/shared/src/utils/helpers/active-symbols.ts similarity index 71% rename from packages/shared/src/utils/helpers/active-symbols.js rename to packages/shared/src/utils/helpers/active-symbols.ts index 647632944e67..a336420ec2c7 100644 --- a/packages/shared/src/utils/helpers/active-symbols.js +++ b/packages/shared/src/utils/helpers/active-symbols.ts @@ -4,10 +4,26 @@ import { redirectToLogin } from '../login'; import { WS } from '../../services'; import { getLanguage, localize } from '@deriv/translations'; +import { ActiveSymbols } from '@deriv/api-types'; + +type TResidenceList = { + residence_list: { + disabled?: string; + phone_idd?: null | string; + selected?: string; + text?: string; + tin_format?: string[]; + value?: string; + }[]; +}; + +type TIsSymbolOpen = { + exchange_is_open: 0 | 1; +}; export const showUnavailableLocationError = flow(function* (showError, is_logged_in) { const website_status = yield WS.wait('website_status'); - const residence_list = yield WS.residenceList(); + const residence_list: TResidenceList = yield WS.residenceList(); const clients_country_code = website_status.website_status.clients_country; const clients_country_text = ( @@ -15,9 +31,7 @@ export const showUnavailableLocationError = flow(function* (showError, is_logged ).text; const header = clients_country_text - ? localize('Sorry, this app is unavailable in {{clients_country}}.', { - clients_country: clients_country_text, - }) + ? localize('Sorry, this app is unavailable in {{clients_country}}.', { clients_country: clients_country_text }) : localize('Sorry, this app is unavailable in your current location.'); showError({ @@ -31,7 +45,7 @@ export const showUnavailableLocationError = flow(function* (showError, is_logged export const showMxMltUnavailableError = flow(function* (showError, can_have_mlt_account, can_have_mx_account) { const get_settings = yield WS.wait('get_settings'); - const residence_list = yield WS.residenceList(); + const residence_list: TResidenceList = yield WS.residenceList(); const clients_country_code = get_settings.get_settings.country_code; const clients_country_text = ( @@ -59,14 +73,14 @@ export const showMxMltUnavailableError = flow(function* (showError, can_have_mlt }); }); -export const isMarketClosed = (active_symbols = [], symbol) => { +export const isMarketClosed = (active_symbols: ActiveSymbols = [], symbol: string) => { if (!active_symbols.length) return false; return active_symbols.filter(x => x.symbol === symbol)[0] ? !active_symbols.filter(symbol_info => symbol_info.symbol === symbol)[0].exchange_is_open : false; }; -export const pickDefaultSymbol = async (active_symbols = []) => { +export const pickDefaultSymbol = async (active_symbols: ActiveSymbols = []) => { if (!active_symbols.length) return ''; const fav_open_symbol = await getFavoriteOpenSymbol(active_symbols); if (fav_open_symbol) return fav_open_symbol; @@ -74,11 +88,11 @@ export const pickDefaultSymbol = async (active_symbols = []) => { return default_open_symbol; }; -const getFavoriteOpenSymbol = async active_symbols => { +const getFavoriteOpenSymbol = async (active_symbols: ActiveSymbols) => { try { const chart_favorites = LocalStore.get('cq-favorites'); if (!chart_favorites) return undefined; - const client_favorite_markets = JSON.parse(chart_favorites)['chartTitle&Comparison']; + const client_favorite_markets: string[] = JSON.parse(chart_favorites)['chartTitle&Comparison']; const client_favorite_list = client_favorite_markets.map(client_fav_symbol => active_symbols.find(symbol_info => symbol_info.symbol === client_fav_symbol) @@ -86,7 +100,7 @@ const getFavoriteOpenSymbol = async active_symbols => { if (client_favorite_list) { const client_first_open_symbol = client_favorite_list.filter(symbol => symbol).find(isSymbolOpen); if (client_first_open_symbol) { - const is_symbol_offered = await isSymbolOffered(client_first_open_symbol); + const is_symbol_offered = await isSymbolOffered(client_first_open_symbol.symbol); if (is_symbol_offered) return client_first_open_symbol.symbol; } } @@ -96,48 +110,58 @@ const getFavoriteOpenSymbol = async active_symbols => { } }; -const getDefaultOpenSymbol = async active_symbols => { +const getDefaultOpenSymbol = async (active_symbols: ActiveSymbols) => { const default_open_symbol = (await findSymbol(active_symbols, '1HZ100V')) || (await findFirstSymbol(active_symbols, /random_index/)) || (await findFirstSymbol(active_symbols, /major_pairs/)); if (default_open_symbol) return default_open_symbol.symbol; - return active_symbols.find(symbol_info => symbol_info.submarket === 'major_pairs').symbol; + return active_symbols.find(symbol_info => symbol_info.submarket === 'major_pairs')?.symbol; }; -const findSymbol = async (active_symbols, symbol) => { +const findSymbol = async (active_symbols: ActiveSymbols, symbol: string) => { const first_symbol = active_symbols.find(symbol_info => symbol_info.symbol === symbol && isSymbolOpen(symbol_info)); - const is_symbol_offered = await isSymbolOffered(first_symbol); + const is_symbol_offered = await isSymbolOffered(first_symbol?.symbol); if (is_symbol_offered) return first_symbol; return undefined; }; -const findFirstSymbol = async (active_symbols, pattern) => { +const findFirstSymbol = async (active_symbols: ActiveSymbols, pattern: RegExp) => { const first_symbol = active_symbols.find( symbol_info => pattern.test(symbol_info.submarket) && isSymbolOpen(symbol_info) ); - const is_symbol_offered = await isSymbolOffered(first_symbol); + const is_symbol_offered = await isSymbolOffered(first_symbol?.symbol); if (is_symbol_offered) return first_symbol; return undefined; }; -export const findFirstOpenMarket = async (active_symbols, markets) => { +type TFindFirstOpenMarket = { category?: string; subcategory?: string } | undefined; + +export const findFirstOpenMarket = async ( + active_symbols: ActiveSymbols, + markets: string[] +): Promise => { const market = markets.shift(); const first_symbol = active_symbols.find(symbol_info => market === symbol_info.market && isSymbolOpen(symbol_info)); - const is_symbol_offered = await isSymbolOffered(first_symbol); - if (is_symbol_offered) return { category: first_symbol.market, subcategory: first_symbol.submarket }; + const is_symbol_offered = await isSymbolOffered(first_symbol?.symbol); + if (is_symbol_offered) return { category: first_symbol?.market, subcategory: first_symbol?.submarket }; else if (markets.length > 0) return findFirstOpenMarket(active_symbols, markets); return undefined; }; -const isSymbolOpen = symbol => symbol.exchange_is_open === 1; +const isSymbolOpen = (symbol?: TIsSymbolOpen) => symbol?.exchange_is_open === 1; -const isSymbolOffered = async symbol_info => { - const r = await WS.storage.contractsFor(symbol_info?.symbol); +const isSymbolOffered = async (symbol?: string) => { + const r = await WS.storage.contractsFor(symbol); return !['InvalidSymbol', 'InputValidationFailed'].includes(r.error?.code); }; -export const getSymbolDisplayName = (active_symbols = [], symbol) => +export type TActiveSymbols = { + symbol: string; + display_name: string; +}[]; + +export const getSymbolDisplayName = (active_symbols: TActiveSymbols = [], symbol: string) => ( active_symbols.find(symbol_info => symbol_info.symbol.toUpperCase() === symbol.toUpperCase()) || { display_name: '', diff --git a/packages/shared/src/utils/helpers/barrier.js b/packages/shared/src/utils/helpers/barrier.js deleted file mode 100644 index 2dff13e7393f..000000000000 --- a/packages/shared/src/utils/helpers/barrier.js +++ /dev/null @@ -1,20 +0,0 @@ -export const buildBarriersConfig = (contract, barriers = { count: contract.barriers }) => { - if (!contract.barriers) { - return undefined; - } - - const obj_barrier = {}; - - ['barrier', 'low_barrier', 'high_barrier'].forEach(field => { - if (field in contract) obj_barrier[field] = contract[field]; - }); - - return Object.assign(barriers || {}, { - [contract.expiry_type]: obj_barrier, - }); -}; - -export const getBarrierPipSize = barrier => { - if (Math.floor(barrier) === barrier || barrier.length < 1 || barrier % 1 === 0 || isNaN(barrier)) return 0; - return barrier.toString().split('.')[1].length || 0; -}; diff --git a/packages/shared/src/utils/helpers/barrier.ts b/packages/shared/src/utils/helpers/barrier.ts new file mode 100644 index 000000000000..cd4c0ef87c71 --- /dev/null +++ b/packages/shared/src/utils/helpers/barrier.ts @@ -0,0 +1,30 @@ +type TContract = { + high_barrier?: null | string; + barriers?: number; + barrier?: null | string; + low_barrier?: null | string; + expiry_type: string; +}; + +type TObjectBarrier = Pick; + +export const buildBarriersConfig = (contract: TContract, barriers = { count: contract.barriers }) => { + if (!contract.barriers) { + return undefined; + } + + const obj_barrier: TObjectBarrier = {}; + + ['barrier', 'low_barrier', 'high_barrier'].forEach(field => { + if (field in contract) obj_barrier[field as keyof TObjectBarrier] = contract[field as keyof TObjectBarrier]; + }); + + return Object.assign(barriers || {}, { + [contract.expiry_type]: obj_barrier, + }); +}; + +export const getBarrierPipSize = (barrier: string) => { + if (Math.floor(+barrier) === +barrier || barrier.length < 1 || +barrier % 1 === 0 || isNaN(+barrier)) return 0; + return barrier.toString().split('.')[1].length || 0; +}; diff --git a/packages/shared/src/utils/helpers/barriers.js b/packages/shared/src/utils/helpers/barriers.js deleted file mode 100644 index 47eec5896432..000000000000 --- a/packages/shared/src/utils/helpers/barriers.js +++ /dev/null @@ -1,28 +0,0 @@ -import { toJS } from 'mobx'; -import { isEmptyObject } from '../object'; -import { CONTRACT_SHADES } from '../constants'; - -export const isBarrierSupported = contract_type => contract_type in CONTRACT_SHADES; - -export const barriersToString = (is_relative, ...barriers_list) => - barriers_list - .filter(barrier => barrier !== undefined && barrier !== null) - .map(barrier => `${is_relative && !/^[+-]/.test(barrier) ? '+' : ''}${barrier}`); - -export const barriersObjectToArray = (barriers, reference_array) => { - Object.keys(barriers).forEach(barrier => { - const js_object = toJS(barriers[barrier]); - if (!isEmptyObject(js_object)) { - reference_array.push(js_object); - } - }); - - return reference_array; -}; - -export const removeBarrier = (barriers, key) => { - const index = barriers.findIndex(b => b.key === key); - if (index > -1) { - barriers.splice(index, 1); - } -}; diff --git a/packages/shared/src/utils/helpers/barriers.ts b/packages/shared/src/utils/helpers/barriers.ts new file mode 100644 index 000000000000..e259724993d1 --- /dev/null +++ b/packages/shared/src/utils/helpers/barriers.ts @@ -0,0 +1,15 @@ +import { CONTRACT_SHADES } from '../constants'; + +export const isBarrierSupported = (contract_type: string) => contract_type in CONTRACT_SHADES; + +export const barriersToString = (is_relative: boolean, ...barriers_list: number[]) => + barriers_list + .filter(barrier => barrier !== undefined && barrier !== null) + .map(barrier => `${is_relative && !/^[+-]/.test(barrier.toString()) ? '+' : ''}${barrier}`); + +export const removeBarrier = (barriers: { [key: string]: string | number }[], key: string) => { + const index = barriers.findIndex(b => b.key === key); + if (index > -1) { + barriers.splice(index, 1); + } +}; diff --git a/packages/shared/src/utils/helpers/chart-notifications.js b/packages/shared/src/utils/helpers/chart-notifications.tsx similarity index 100% rename from packages/shared/src/utils/helpers/chart-notifications.js rename to packages/shared/src/utils/helpers/chart-notifications.tsx diff --git a/packages/shared/src/utils/helpers/details.js b/packages/shared/src/utils/helpers/details.ts similarity index 82% rename from packages/shared/src/utils/helpers/details.js rename to packages/shared/src/utils/helpers/details.ts index cf1a585acaaf..067e191f42fc 100644 --- a/packages/shared/src/utils/helpers/details.js +++ b/packages/shared/src/utils/helpers/details.ts @@ -1,7 +1,15 @@ import { epochToMoment, formatMilliseconds, getDiffDuration } from '../date'; import { localize } from '@deriv/translations'; +import moment from 'moment'; -export const getDurationUnitValue = obj_duration => { +type TGetDurationPeriod = { + date_start: number; + purchase_time: number; + date_expiry: number; + tick_count?: number; +}; + +export const getDurationUnitValue = (obj_duration: moment.Duration) => { const duration_ms = obj_duration.asMilliseconds() / 1000; // Check with isEndTime to find out if value of duration has decimals // for days we do not require precision for End Time value since users cannot select with timepicker if not in same day @@ -29,7 +37,7 @@ export const getDurationUnitValue = obj_duration => { return Math.floor(duration_ms / 1000); }; -export const isEndTime = duration => duration % 1 !== 0; +export const isEndTime = (duration: number) => duration % 1 !== 0; export const getUnitMap = () => { return { @@ -40,7 +48,7 @@ export const getUnitMap = () => { }; }; -export const getDurationUnitText = obj_duration => { +export const getDurationUnitText = (obj_duration: moment.Duration) => { const unit_map = getUnitMap(); const duration_ms = obj_duration.asMilliseconds() / 1000; // return empty suffix string if duration is End Time set except for days and seconds, refer to L18 and L19 @@ -62,11 +70,11 @@ export const getDurationUnitText = obj_duration => { return unit_map.s.name; }; -export const getDurationPeriod = contract_info => +export const getDurationPeriod = (contract_info: TGetDurationPeriod) => getDiffDuration( - epochToMoment(contract_info.date_start || contract_info.purchase_time), - epochToMoment(contract_info.date_expiry) + +epochToMoment(contract_info.date_start || contract_info.purchase_time), + +epochToMoment(contract_info.date_expiry) ); -export const getDurationTime = contract_info => +export const getDurationTime = (contract_info: TGetDurationPeriod) => contract_info.tick_count ? contract_info.tick_count : getDurationUnitValue(getDurationPeriod(contract_info)); diff --git a/packages/shared/src/utils/helpers/duration.js b/packages/shared/src/utils/helpers/duration.ts similarity index 55% rename from packages/shared/src/utils/helpers/duration.js rename to packages/shared/src/utils/helpers/duration.ts index 6cabf969565d..847ff0bed915 100644 --- a/packages/shared/src/utils/helpers/duration.js +++ b/packages/shared/src/utils/helpers/duration.ts @@ -1,28 +1,67 @@ import { localize } from '@deriv/translations'; import { toMoment } from '../date'; +type TContract = { + max_contract_duration: string; + min_contract_duration: string; + expiry_type: string; + start_type: string; +}; + +type TMaxMin = { + min: number; + max: number; +}; + +type TUnit = { + text: string; + value: string; +}; + +type TDurations = { + min_max: { + spot?: Partial>; + forward?: Record<'intraday', TMaxMin>; + }; + units_display: Partial>; +}; + +type TDurationMinMax = { + [key: string]: { + max: string | number; + min: string | number; + }; +}; + const getDurationMaps = () => ({ - t: { display: localize('Ticks'), order: 1 }, + t: { display: localize('Ticks'), order: 1, to_second: 0 }, s: { display: localize('Seconds'), order: 2, to_second: 1 }, m: { display: localize('Minutes'), order: 3, to_second: 60 }, h: { display: localize('Hours'), order: 4, to_second: 60 * 60 }, d: { display: localize('Days'), order: 5, to_second: 60 * 60 * 24 }, }); -export const buildDurationConfig = (contract, durations = { min_max: {}, units_display: {} }) => { - durations.min_max[contract.start_type] = durations.min_max[contract.start_type] || {}; - durations.units_display[contract.start_type] = durations.units_display[contract.start_type] || []; +export const buildDurationConfig = ( + contract: TContract, + durations: TDurations = { min_max: {}, units_display: {} } +) => { + type TDurationMaps = keyof typeof duration_maps; + let duration_min_max = durations.min_max[contract.start_type as keyof typeof durations.min_max]; + let duration_units = durations.units_display[contract.start_type as keyof typeof durations.units_display]; + + duration_min_max = duration_min_max || {}; + duration_units = duration_units || []; const obj_min = getDurationFromString(contract.min_contract_duration); const obj_max = getDurationFromString(contract.max_contract_duration); - durations.min_max[contract.start_type][contract.expiry_type] = { - min: convertDurationUnit(obj_min.duration, obj_min.unit, 's'), - max: convertDurationUnit(obj_max.duration, obj_max.unit, 's'), + duration_min_max[contract.expiry_type as keyof typeof duration_min_max] = { + min: convertDurationUnit(obj_min.duration, obj_min.unit, 's') || 0, + max: convertDurationUnit(obj_max.duration, obj_max.unit, 's') || 0, }; - const arr_units = []; - durations.units_display[contract.start_type].forEach(obj => { + const arr_units: string[] = []; + duration_units.forEach(obj => { arr_units.push(obj.value); }); @@ -37,44 +76,48 @@ export const buildDurationConfig = (contract, durations = { min_max: {}, units_d if ( u !== 'd' && // when the expiray_type is intraday, the supported units are seconds, minutes and hours. arr_units.indexOf(u) === -1 && - duration_maps[u].order >= duration_maps[obj_min.unit].order && - duration_maps[u].order <= duration_maps[obj_max.unit].order + duration_maps[u as TDurationMaps].order >= duration_maps[obj_min.unit as TDurationMaps].order && + duration_maps[u as TDurationMaps].order <= duration_maps[obj_max.unit as TDurationMaps].order ) { arr_units.push(u); } }); } - durations.units_display[contract.start_type] = arr_units - .sort((a, b) => (duration_maps[a].order > duration_maps[b].order ? 1 : -1)) - .reduce((o, c) => [...o, { text: duration_maps[c].display, value: c }], []); + duration_units = arr_units + .sort((a, b) => (duration_maps[a as TDurationMaps].order > duration_maps[b as TDurationMaps].order ? 1 : -1)) + .reduce((o, c) => [...o, { text: duration_maps[c as TDurationMaps].display, value: c }], [] as TUnit[]); return durations; }; -export const convertDurationUnit = (value, from_unit, to_unit) => { - if (!value || !from_unit || !to_unit || isNaN(parseInt(value))) { +export const convertDurationUnit = (value: number, from_unit: string, to_unit: string) => { + if (!value || !from_unit || !to_unit || isNaN(value)) { return null; } const duration_maps = getDurationMaps(); - if (from_unit === to_unit || !('to_second' in duration_maps[from_unit])) { + if (from_unit === to_unit || !('to_second' in duration_maps[from_unit as keyof typeof duration_maps])) { return value; } - return (value * duration_maps[from_unit].to_second) / duration_maps[to_unit].to_second; + return ( + (value * duration_maps[from_unit as keyof typeof duration_maps].to_second) / + duration_maps[to_unit as keyof typeof duration_maps].to_second + ); }; -const getDurationFromString = duration_string => { - const duration = duration_string.toString().match(/[a-zA-Z]+|[0-9]+/g); +const getDurationFromString = (duration_string: string) => { + const duration = duration_string.toString().match(/[a-zA-Z]+|[0-9]+/g) || ''; return { duration: +duration[0], // converts string to numbers unit: duration[1], }; }; -export const getExpiryType = store => { +// TODO will change this after the global stores types get ready +export const getExpiryType = (store: any) => { const { duration_unit, expiry_date, expiry_type, duration_units_list } = store; const server_time = store.root_store.common.server_time; @@ -91,7 +134,7 @@ export const getExpiryType = store => { return contract_expiry_type; }; -export const convertDurationLimit = (value, unit) => { +export const convertDurationLimit = (value: number, unit: string) => { if (!(value >= 0) || !unit || !Number.isInteger(value)) { return null; } @@ -110,7 +153,7 @@ export const convertDurationLimit = (value, unit) => { return value; }; -export const hasIntradayDurationUnit = duration_units_list => +export const hasIntradayDurationUnit = (duration_units_list: TUnit[]) => duration_units_list.some(unit => ['m', 'h'].indexOf(unit.value) !== -1); /** @@ -120,10 +163,14 @@ export const hasIntradayDurationUnit = duration_units_list => * @param {String} expiry_type * @returns {*} */ -export const resetEndTimeOnVolatilityIndices = (symbol, expiry_type) => +export const resetEndTimeOnVolatilityIndices = (symbol: string, expiry_type: string) => /^R_/.test(symbol) && expiry_type === 'endtime' ? toMoment(null).format('DD MMM YYYY') : null; -export const getDurationMinMaxValues = (duration_min_max, contract_expiry_type, duration_unit) => { +export const getDurationMinMaxValues = ( + duration_min_max: TDurationMinMax, + contract_expiry_type: string, + duration_unit: string +) => { if (!duration_min_max[contract_expiry_type]) return []; const max_value = convertDurationLimit(+duration_min_max[contract_expiry_type].max, duration_unit); const min_value = convertDurationLimit(+duration_min_max[contract_expiry_type].min, duration_unit); diff --git a/packages/shared/src/utils/helpers/format-response.js b/packages/shared/src/utils/helpers/format-response.js deleted file mode 100644 index da9671ded31d..000000000000 --- a/packages/shared/src/utils/helpers/format-response.js +++ /dev/null @@ -1,29 +0,0 @@ -import { getUnsupportedContracts } from '../constants'; -import { getSymbolDisplayName } from './active-symbols'; -import { getMarketInformation } from './market-underlying'; - -const isUnSupportedContract = portfolio_pos => - !!getUnsupportedContracts()[portfolio_pos.contract_type] || // check unsupported contract type - !!portfolio_pos.is_forward_starting; // for forward start contracts - -export const formatPortfolioPosition = (portfolio_pos, active_symbols = [], indicative) => { - const purchase = parseFloat(portfolio_pos.buy_price); - const payout = parseFloat(portfolio_pos.payout); - const display_name = getSymbolDisplayName(active_symbols, getMarketInformation(portfolio_pos.shortcode).underlying); - const transaction_id = - portfolio_pos.transaction_id || (portfolio_pos.transaction_ids && portfolio_pos.transaction_ids.buy); - - return { - contract_info: portfolio_pos, - details: portfolio_pos.longcode.replace(/\n/g, '
'), - display_name, - id: portfolio_pos.contract_id, - indicative: isNaN(indicative) || !indicative ? 0 : indicative, - payout, - purchase, - reference: +transaction_id, - type: portfolio_pos.contract_type, - is_unsupported: isUnSupportedContract(portfolio_pos), - contract_update: portfolio_pos.limit_order, - }; -}; diff --git a/packages/shared/src/utils/helpers/format-response.ts b/packages/shared/src/utils/helpers/format-response.ts new file mode 100644 index 000000000000..5cb8f2baac36 --- /dev/null +++ b/packages/shared/src/utils/helpers/format-response.ts @@ -0,0 +1,56 @@ +import { getUnsupportedContracts } from '../constants'; +import { getSymbolDisplayName, TActiveSymbols } from './active-symbols'; +import { getMarketInformation } from './market-underlying'; + +type TPortfolioPos = { + buy_price: number; + contract_id?: number; + contract_type?: string; + longcode: string; + payout: number; + shortcode: string; + transaction_id?: number; + transaction_ids?: { + buy: number; + sell: number; + }; + limit_order?: { + stop_loss?: null | number; + take_profit?: null | number; + }; +}; + +type TIsUnSupportedContract = { + contract_type?: string; + is_forward_starting?: 0 | 1; +}; + +const isUnSupportedContract = (portfolio_pos: TIsUnSupportedContract) => + !!getUnsupportedContracts()[portfolio_pos.contract_type as keyof typeof getUnsupportedContracts] || // check unsupported contract type + !!portfolio_pos.is_forward_starting; // for forward start contracts + +export const formatPortfolioPosition = ( + portfolio_pos: TPortfolioPos, + active_symbols: TActiveSymbols = [], + indicative?: number +) => { + const purchase = portfolio_pos.buy_price; + const payout = portfolio_pos.payout; + const display_name = getSymbolDisplayName(active_symbols, getMarketInformation(portfolio_pos.shortcode).underlying); + const transaction_id = + portfolio_pos.transaction_id || (portfolio_pos.transaction_ids && portfolio_pos.transaction_ids.buy); + + return { + contract_info: portfolio_pos, + details: portfolio_pos.longcode.replace(/\n/g, '
'), + display_name, + id: portfolio_pos.contract_id, + indicative: (indicative && isNaN(indicative)) || !indicative ? 0 : indicative, + payout, + purchase, + reference: Number(transaction_id), + type: portfolio_pos.contract_type, + is_unsupported: isUnSupportedContract(portfolio_pos), + contract_update: portfolio_pos.limit_order, + }; +}; diff --git a/packages/shared/src/utils/helpers/index.js b/packages/shared/src/utils/helpers/index.ts similarity index 100% rename from packages/shared/src/utils/helpers/index.js rename to packages/shared/src/utils/helpers/index.ts diff --git a/packages/shared/src/utils/helpers/logic.js b/packages/shared/src/utils/helpers/logic.js deleted file mode 100644 index 709641fb105a..000000000000 --- a/packages/shared/src/utils/helpers/logic.js +++ /dev/null @@ -1,58 +0,0 @@ -import moment from 'moment'; -import { isEmptyObject } from '../object'; -import { isUserSold } from '../contract'; - -export const isContractElapsed = (contract_info, tick) => { - if (isEmptyObject(tick) || isEmptyObject(contract_info)) return false; - const end_time = getEndTime(contract_info); - if (end_time && tick.epoch) { - const seconds = moment.duration(moment.unix(tick.epoch).diff(moment.unix(end_time))).asSeconds(); - return seconds >= 2; - } - return false; -}; - -export const isEndedBeforeCancellationExpired = contract_info => - !!(contract_info.cancellation && getEndTime(contract_info) < contract_info.cancellation.date_expiry); - -export const isSoldBeforeStart = contract_info => - contract_info.sell_time && +contract_info.sell_time < +contract_info.date_start; - -export const isStarted = contract_info => - !contract_info.is_forward_starting || contract_info.current_spot_time > contract_info.date_start; - -export const isUserCancelled = contract_info => contract_info.status === 'cancelled'; - -export const getEndTime = contract_info => { - const { - exit_tick_time, - date_expiry, - is_expired, - is_path_dependent, - sell_time, - status, - tick_count: is_tick_contract, - } = contract_info; - - const is_finished = is_expired && status !== 'open'; - - if (!is_finished && !isUserSold(contract_info) && !isUserCancelled(contract_info)) return undefined; - - if (isUserSold(contract_info)) { - return sell_time > date_expiry ? date_expiry : sell_time; - } else if (!is_tick_contract && sell_time > date_expiry) { - return date_expiry; - } - - return date_expiry > exit_tick_time && !+is_path_dependent ? date_expiry : exit_tick_time; -}; - -export const getBuyPrice = contract_store => { - return contract_store.contract_info.buy_price; -}; - -/** - * Set contract update form initial values - * @param {object} contract_update - contract_update response - * @param {object} limit_order - proposal_open_contract.limit_order response - */ diff --git a/packages/shared/src/utils/helpers/logic.ts b/packages/shared/src/utils/helpers/logic.ts new file mode 100644 index 000000000000..78c7fdb3e1bb --- /dev/null +++ b/packages/shared/src/utils/helpers/logic.ts @@ -0,0 +1,91 @@ +import moment from 'moment'; +import { isEmptyObject } from '../object'; +import { isUserSold } from '../contract'; +import { TContractInfo } from '../contract/contract-types'; + +type TTick = { + ask?: number; + bid?: number; + epoch?: number; + id?: string; + pip_size: number; + quote?: number; + symbol?: string; +}; + +type TIsEndedBeforeCancellationExpired = TGetEndTime & { + cancellation: { + ask_price: number; + date_expiry: number; + }; +}; + +type TIsSoldBeforeStart = Required>; + +type TIsStarted = Required>; + +type TGetEndTime = Pick & + Required>; + +type TGetBuyPrice = { + contract_info: { + buy_price: number; + }; +}; + +export const isContractElapsed = (contract_info: TGetEndTime, tick: TTick) => { + if (isEmptyObject(tick) || isEmptyObject(contract_info)) return false; + const end_time = getEndTime(contract_info) || 0; + if (end_time && tick.epoch) { + const seconds = moment.duration(moment.unix(tick.epoch).diff(moment.unix(end_time))).asSeconds(); + return seconds >= 2; + } + return false; +}; + +export const isEndedBeforeCancellationExpired = (contract_info: TIsEndedBeforeCancellationExpired) => { + const end_time = getEndTime(contract_info) || 0; + return !!(contract_info.cancellation && end_time < contract_info.cancellation.date_expiry); +}; + +export const isSoldBeforeStart = (contract_info: TIsSoldBeforeStart) => + contract_info.sell_time && +contract_info.sell_time < +contract_info.date_start; + +export const isStarted = (contract_info: TIsStarted) => + !contract_info.is_forward_starting || contract_info.current_spot_time > contract_info.date_start; + +export const isUserCancelled = (contract_info: TContractInfo) => contract_info.status === 'cancelled'; + +export const getEndTime = (contract_info: TGetEndTime) => { + const { + exit_tick_time, + date_expiry, + is_expired, + is_path_dependent, + sell_time, + status, + tick_count: is_tick_contract, + } = contract_info; + + const is_finished = is_expired && status !== 'open'; + + if (!is_finished && !isUserSold(contract_info) && !isUserCancelled(contract_info)) return undefined; + + if (isUserSold(contract_info) && sell_time) { + return sell_time > date_expiry ? date_expiry : sell_time; + } else if (!is_tick_contract && sell_time && sell_time > date_expiry) { + return date_expiry; + } + + return date_expiry > exit_tick_time && !+is_path_dependent ? date_expiry : exit_tick_time; +}; + +export const getBuyPrice = (contract_store: TGetBuyPrice) => { + return contract_store.contract_info.buy_price; +}; + +/** + * Set contract update form initial values + * @param {object} contract_update - contract_update response + * @param {object} limit_order - proposal_open_contract.limit_order response + */ diff --git a/packages/shared/src/utils/helpers/market-underlying.js b/packages/shared/src/utils/helpers/market-underlying.ts similarity index 60% rename from packages/shared/src/utils/helpers/market-underlying.js rename to packages/shared/src/utils/helpers/market-underlying.ts index 7915a889d04e..c8aa7b5e4a9a 100644 --- a/packages/shared/src/utils/helpers/market-underlying.js +++ b/packages/shared/src/utils/helpers/market-underlying.ts @@ -1,5 +1,10 @@ import { getMarketNamesMap, getContractConfig } from '../constants/contract'; +type TTradeConfig = { + name: JSX.Element; + position: string; +}; + /** * Fetch market information from shortcode * @param shortcode: string @@ -7,7 +12,7 @@ import { getMarketNamesMap, getContractConfig } from '../constants/contract'; */ // TODO: Combine with extractInfoFromShortcode function in shared, both are currently used -export const getMarketInformation = shortcode => { +export const getMarketInformation = (shortcode: string) => { const market_info = { category: '', underlying: '', @@ -25,6 +30,10 @@ export const getMarketInformation = shortcode => { return market_info; }; -export const getMarketName = underlying => (underlying ? getMarketNamesMap()[underlying.toUpperCase()] : null); +export const getMarketName = (underlying: string) => + underlying ? getMarketNamesMap()[underlying.toUpperCase() as keyof typeof getMarketNamesMap] : null; -export const getTradeTypeName = category => (category ? getContractConfig()[category.toUpperCase()].name : null); +export const getTradeTypeName = (category: string) => + category + ? (getContractConfig()[category.toUpperCase() as keyof typeof getContractConfig] as TTradeConfig).name + : null; diff --git a/packages/shared/src/utils/helpers/portfolio-notifications.js b/packages/shared/src/utils/helpers/portfolio-notifications.tsx similarity index 89% rename from packages/shared/src/utils/helpers/portfolio-notifications.js rename to packages/shared/src/utils/helpers/portfolio-notifications.tsx index 60d23a5a6394..704434caa9fe 100644 --- a/packages/shared/src/utils/helpers/portfolio-notifications.js +++ b/packages/shared/src/utils/helpers/portfolio-notifications.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { localize, Localize } from '@deriv/translations'; -export const contractSold = (currency, sold_for, Money) => ({ +export const contractSold = (currency: string, sold_for: number | string, Money: React.ElementType) => ({ key: 'contract_sold', header: localize('Contract sold'), message: ( diff --git a/packages/shared/src/utils/helpers/start-date.js b/packages/shared/src/utils/helpers/start-date.ts similarity index 61% rename from packages/shared/src/utils/helpers/start-date.js rename to packages/shared/src/utils/helpers/start-date.ts index b1b748ed3d11..250d4337b988 100644 --- a/packages/shared/src/utils/helpers/start-date.js +++ b/packages/shared/src/utils/helpers/start-date.ts @@ -1,7 +1,28 @@ +import moment from 'moment'; import { toMoment } from '../date'; -export const buildForwardStartingConfig = (contract, forward_starting_dates) => { - const forward_starting_config = []; +type TForwardStartingDates = { + blackouts?: unknown[]; + close?: string; + date: string; + open?: string; +}; + +type TContract = { + forward_starting_options: TForwardStartingDates[]; +}; + +type TConfig = { + text: string; + value: number; + sessions: { + open: moment.Moment; + close: moment.Moment; + }[]; +}[]; + +export const buildForwardStartingConfig = (contract: TContract, forward_starting_dates: TForwardStartingDates[]) => { + const forward_starting_config: TConfig = []; if ((contract.forward_starting_options || []).length) { contract.forward_starting_options.forEach(option => { diff --git a/packages/shared/src/utils/helpers/validation-rules.js b/packages/shared/src/utils/helpers/validation-rules.ts similarity index 74% rename from packages/shared/src/utils/helpers/validation-rules.js rename to packages/shared/src/utils/helpers/validation-rules.ts index 8bed8e007d02..70ef892dcceb 100644 --- a/packages/shared/src/utils/helpers/validation-rules.js +++ b/packages/shared/src/utils/helpers/validation-rules.ts @@ -1,7 +1,14 @@ import { localize } from '@deriv/translations'; import { getTotalProfit } from '../contract'; +import { TGetTotalProfit } from '../contract/contract-types'; import { getBuyPrice } from './logic'; +type TContractStore = { + contract_update_stop_loss?: number; + contract_info: TGetTotalProfit; + contract_update_take_profit?: string; +}; + export const getContractValidationRules = () => ({ has_contract_update_stop_loss: { trigger: 'contract_update_stop_loss', @@ -11,14 +18,14 @@ export const getContractValidationRules = () => ({ [ 'req', { - condition: contract_store => !contract_store.contract_update_stop_loss, + condition: (contract_store: TContractStore) => !contract_store.contract_update_stop_loss, message: localize('Please enter a stop loss amount.'), }, ], [ 'custom', { - func: (value, options, contract_store) => { + func: (value: number, contract_store: TContractStore) => { const profit = getTotalProfit(contract_store.contract_info); return !(profit < 0 && -value > profit); }, @@ -28,7 +35,7 @@ export const getContractValidationRules = () => ({ [ 'custom', { - func: (value, options, contract_store) => { + func: (value: number, contract_store: TContractStore) => { const stake = getBuyPrice(contract_store); return value < stake + 1; }, @@ -45,14 +52,14 @@ export const getContractValidationRules = () => ({ [ 'req', { - condition: contract_store => !contract_store.contract_update_take_profit, + condition: (contract_store: TContractStore) => !contract_store.contract_update_take_profit, message: localize('Please enter a take profit amount.'), }, ], [ 'custom', { - func: (value, options, contract_store) => { + func: (value: string | number, contract_store: TContractStore) => { const profit = getTotalProfit(contract_store.contract_info); return !(profit > 0 && +value < profit); },