diff --git a/.eslintrc.js b/.eslintrc.js index d641ea71541d..5d58c67c65d6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,6 +25,7 @@ module.exports = { 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', + 'plugin:react/jsx-runtime', // Enables the new JSX transform runtime ], parserOptions: { ecmaversion: 2018, diff --git a/.github/actions/analyze/action.yml b/.github/actions/analyze/action.yml index 9f9cb7dcf729..67bb65435af1 100644 --- a/.github/actions/analyze/action.yml +++ b/.github/actions/analyze/action.yml @@ -27,6 +27,9 @@ inputs: DATADOG_SESSION_SAMPLE_RATE: description: 'Datadog session sample rate' required: false + GITHUB_TOKEN: + description: 'Github token for downloading artifacts' + required: true GD_API_KEY: description: 'Google drive api key' required: false @@ -54,10 +57,51 @@ inputs: IS_GROWTHBOOK_ENABLED: description: 'Is growthbook enabled' required: true + ISSUE_NUMBER: + description: 'Issue to post a comment to' + required: false + TRUSTPILOT_API_KEY: + description: 'Trustpilot api key' + required: false runs: using: composite steps: + - name: Get artifact URL + id: get_artifact_url + env: + GITHUB_TOKEN: ${{ inputs.GITHUB_TOKEN }} + shell: bash + run: | + RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ + "https://api.github.com/repos/${{ github.repository }}/actions/artifacts?name=analyse") + + ARTIFACT_URL=$(echo $RESPONSE | jq -r '.artifacts[0].archive_download_url') + + if [[ -z "$ARTIFACT_URL" ]]; then + echo "Error: No artifact URL found for the master branch with prefix 'analyse'." + exit 1 + else + echo "Artifact URL: $ARTIFACT_URL" + fi + + echo "artifact_url=$ARTIFACT_URL" >> $GITHUB_OUTPUT + echo "artifact_url=$ARTIFACT_URL" + + - name: Download artifact + if: steps.get_artifact_url.outputs.artifact_url != 'null' + env: + ARTIFACT_URL: ${{ steps.get_artifact_url.outputs.artifact_url }} + GITHUB_TOKEN: ${{ inputs.GITHUB_TOKEN }} + shell: bash + run: | + curl -L -H "Authorization: Bearer $GITHUB_TOKEN" \ + "$ARTIFACT_URL" \ + -o artifact.zip + unzip artifact.zip -d old + cd old + unzip analyse.zip + - name: Analyze all packages env: NODE_ENV: ${{ inputs.NODE_ENV }} @@ -77,18 +121,79 @@ runs: GROWTHBOOK_DECRYPTION_KEY: ${{ inputs.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ inputs.REF_NAME }} REMOTE_CONFIG_URL: ${{ inputs.REMOTE_CONFIG_URL }} + TRUSTPILOT_API_KEY: ${{ inputs.TRUSTPILOT_API_KEY }} NODE_OPTIONS: "--max_old_space_size=4096" - run: npm run build:prod && npm run analyze:stats shell: bash + run: npm run build:prod && npm run analyze:stats && npm run analyze:build + + - name: Compare report to master + id: diff + if: steps.get_artifact_url.outputs.artifact_url != 'null' + shell: bash + run: | + DIFF_OUTPUT_HTML=$(node .github/actions/analyze/compareReports.js --format=html) + ABOVE_THRESHOLD=$(node .github/actions/analyze/compareReports.js --format=boolean) || { echo "Threshold check failed"; exit 1; } + + # Output results to GITHUB_OUTPUT + echo "diff_output_html=$DIFF_OUTPUT_HTML" >> $GITHUB_OUTPUT + echo "above_threshold=$ABOVE_THRESHOLD" >> $GITHUB_OUTPUT + + - name: Comment on PR with Diff Output + if: steps.get_artifact_url.outputs.artifact_url != 'null' && inputs.ISSUE_NUMBER + uses: actions/github-script@v5 + env: + DIFF_OUTPUT_HTML: ${{ steps.diff.outputs.diff_output_html }} + ISSUE_NUMBER: ${{ inputs.ISSUE_NUMBER }} + with: + script: | + const diffOutputHtml = process.env.DIFF_OUTPUT_HTML; // Removed Buffer.from and base64 decoding + const issueNumber = process.env.ISSUE_NUMBER; + github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: `${diffOutputHtml}` + }); + continue-on-error: true + + - name: Print out differences on console + id: print_diff + if: steps.get_artifact_url.outputs.artifact_url != 'null' + shell: bash + run: | + node .github/actions/analyze/compareReports.js --format=console - - name: Zip all stats.json files + - name: Validate size changes + if: ${{ steps.diff.outputs.above_threshold == 'true' }} + uses: actions/github-script@v5 + with: + script: | + core.setFailed('Size changes exceed the defined threshold. Check above logs for details.'); + + - name: Zip all report.json files + shell: bash run: | - zip -r stats.zip packages/*/stats.json + zip -r analyse.zip packages/*/report.json + + - name: Upload analyse.zip for Master Branch + if: github.ref == 'refs/heads/master' + uses: actions/upload-artifact@v4 + with: + name: analyse + path: analyse.zip + retention-days: 20 + + - name: Set sanitized branch name + id: sanitize shell: bash + run: | + SANITIZED_REF_NAME=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9]/-/g') + echo "SANITIZED_REF_NAME=${SANITIZED_REF_NAME}" >> $GITHUB_ENV - - name: Upload stats.zip + - name: Upload analyse.zip for Feature Branches + if: github.ref != 'refs/heads/master' uses: actions/upload-artifact@v4 with: - name: stats-${{ github.sha }} - path: stats.zip - retention-days: 1 + name: analyse-${{ env.SANITIZED_REF_NAME }} + path: analyse.zip + retention-days: 5 \ No newline at end of file diff --git a/.github/actions/analyze/compareReports.js b/.github/actions/analyze/compareReports.js new file mode 100644 index 000000000000..f84087521b04 --- /dev/null +++ b/.github/actions/analyze/compareReports.js @@ -0,0 +1,170 @@ +const fs = require('fs'); +const path = require('path'); + +// read and use parameters from command line +const args = process.argv.slice(2); // Skip the first two elements +let format = args.find(arg => arg.startsWith('--format='))?.split('=')[1] || 'html'; +let orangeThreshold = +(args.find(arg => arg.startsWith('--orangeThreshold='))?.split('=')[1] || 0.5); +let redThreshold = +(args.find(arg => arg.startsWith('--redThreshold='))?.split('=')[1] || 5); + +// main function execution +main(); + +function main() { + // format: [package]: { oldSize, newSize, diff, percentage } + const sizes = analyse(); + + // format to different output based on the format parameter + // nice table in html if its for comment, nice table in console if its for console, or just true/false if its just to check validity + if (format === 'html') { + let formattedOutput = formatToTable(sizes); + console.log(formattedOutput); + } else if (format === 'console') { + let formattedOutput = formatToConsole(sizes); + console.table(formattedOutput, ['oldSize', 'newSize', 'diff', 'percentage', 'alert']); + } else if (format === 'boolean') { + const aboveRedThreshold = Object.values(sizes).some(pkg => pkg.percentage > redThreshold); + if (aboveRedThreshold) { + console.log('true'); + } else { + console.log('false'); + } + } +} + +function analyse() { + const packagesDir = './packages'; + const oldPackagesDir = './old/packages'; + + const packages = [...new Set([...fs.readdirSync(packagesDir), ...fs.readdirSync(oldPackagesDir)])]; + + const result = {}; + + for (const pkg of packages) { + const oldReport = readJsonFile(path.join(oldPackagesDir, pkg, 'report.json')); + const newReport = readJsonFile(path.join(packagesDir, pkg, 'report.json')); + + if (!newReport) { + continue; + } + + const oldSize = oldReport ? oldReport.reduce((acc, item) => acc + item.gzipSize, 0) : null; + const newSize = newReport ? newReport.reduce((acc, item) => acc + item.gzipSize, 0) : null; + + let diff = oldSize && newSize ? newSize - oldSize : oldSize || newSize; + let percentage = oldSize && newSize ? calculatePercentage(oldSize, newSize) : null; + + result[pkg] = { + oldSize, + newSize, + diff, + percentage, + }; + } + + return result; +} + +function formatToTable(sizes) { + const GREEN_SIGN = '🟢'; + const YELLOW_SIGN = '🟡'; + const RED_SIGN = '🔴'; + + let tableRows = ''; + for (const [pkg, { oldSize, newSize, diff, percentage }] of Object.entries(sizes)) { + const formattedPercentage = formatPercentageWithSign(percentage); + const lightSign = + percentage > redThreshold ? RED_SIGN : percentage > orangeThreshold ? YELLOW_SIGN : GREEN_SIGN; + + tableRows += ` + + ${pkg} + ${formatBytes(oldSize)} + ${formatBytes(newSize)} + ${formatBytes(diff, true)} + ${formattedPercentage} ${lightSign} + + `.trim(); + } + + return ` + + + + + + + + + + ${tableRows} + +
packageoldnewdiffpct change
` + .replace(/[\n\t]/g, '') + .trim(); +} + +function formatToConsole(sizes) { + Object.keys(sizes).forEach(key => { + const pkg = sizes[key]; + pkg.oldSize = formatBytes(pkg.oldSize); + pkg.newSize = formatBytes(pkg.newSize); + pkg.diff = formatBytes(pkg.diff, true); + pkg.alert = pkg.percentage > redThreshold ? 'FAIL' : pkg.percentage > orangeThreshold ? 'WARN' : 'OK'; + pkg.percentage = formatPercentageWithSign(pkg.percentage); + }); + return sizes; +} + +function readJsonFile(filePath) { + if (fs.existsSync(filePath)) { + const data = fs.readFileSync(filePath, 'utf-8'); + return JSON.parse(data); + } + return null; +} + +function calculatePercentage(oldSize, newSize) { + return ((newSize - oldSize) / oldSize) * 100; +} + +function formatBytes(bytes, sign = false) { + if (bytes === null || isNaN(bytes)) { + return 'n/a'; + } + + let formattedValue = ''; + + if (bytes < 1024) { + formattedValue = bytes + ' B'; // Bytes + } else if (bytes < 1048576) { + formattedValue = Math.round(bytes / 1024) + ' KB'; // Kilobytes + } else { + formattedValue = (bytes / 1048576).toFixed(1) + ' MB'; // Megabytes + } + + if (sign) { + if (bytes === 0) { + return '0 B'; + } + formattedValue = bytes >= 0 ? '+' + formattedValue : '-' + formattedValue; + } + + return formattedValue; +} + +function formatPercentageWithSign(percentage) { + if (percentage === null || isNaN(percentage)) { + return 'n/a'; + } + + const absPercentage = Math.abs(percentage); + const decimalPoints = absPercentage < 10 ? 1 : 2; + let formattedValue = percentage.toFixed(decimalPoints) + '%'; + + if (percentage === 0) { + return '0%'; + } + + return percentage >= 0 ? '+' + formattedValue : '−' + formattedValue; +} diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 1b570a5301da..fa3538b32bfd 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -54,6 +54,9 @@ inputs: IS_GROWTHBOOK_ENABLED: description: 'Is growthbook enabled' required: true + TRUSTPILOT_API_KEY: + description: 'Trustpilot api key' + required: false runs: using: composite @@ -77,5 +80,6 @@ runs: GROWTHBOOK_DECRYPTION_KEY: ${{ inputs.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ inputs.REF_NAME }} REMOTE_CONFIG_URL: ${{ inputs.REMOTE_CONFIG_URL }} + TRUSTPILOT_API_KEY: ${{ inputs.TRUSTPILOT_API_KEY }} run: npm run build:all shell: bash diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index f6d6da369b75..0e88fee58091 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -7,11 +7,16 @@ on: branches: - master + jobs: build_and_test: name: Analyze Bundle runs-on: Runner_16cores_Deriv-app environment: Preview + permissions: + contents: read + pull-requests: write + actions: read steps: - name: Checkout uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 @@ -31,6 +36,7 @@ jobs: DATADOG_SESSION_REPLAY_SAMPLE_RATE: ${{ vars.DATADOG_SESSION_REPLAY_SAMPLE_RATE }} DATADOG_SESSION_SAMPLE_RATE: ${{ vars.DATADOG_SESSION_SAMPLE_RATE }} DATADOG_SESSION_SAMPLE_RATE_LOGS: ${{ vars.DATADOG_SESSION_SAMPLE_RATE_LOGS }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GD_API_KEY: ${{ secrets.GD_API_KEY }} GD_APP_ID: ${{ secrets.GD_APP_ID }} GD_CLIENT_ID: ${{ secrets.GD_CLIENT_ID }} @@ -39,3 +45,5 @@ jobs: GROWTHBOOK_DECRYPTION_KEY: ${{ vars.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ github.ref_name }} REMOTE_CONFIG_URL: ${{ vars.REMOTE_CONFIG_URL }} + ISSUE_NUMBER: ${{ github.event.pull_request.number }} + TRUSTPILOT_API_KEY: ${{ secrets.TRUSTPILOT_API_KEY }} diff --git a/.github/workflows/release_production.yml b/.github/workflows/release_production.yml index 65f2a25410c5..becaf68f3502 100644 --- a/.github/workflows/release_production.yml +++ b/.github/workflows/release_production.yml @@ -42,6 +42,7 @@ jobs: GROWTHBOOK_DECRYPTION_KEY: ${{ vars.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ github.ref_name }} REMOTE_CONFIG_URL: ${{ vars.REMOTE_CONFIG_URL }} + TRUSTPILOT_API_KEY: ${{ secrets.TRUSTPILOT_API_KEY }} - name: Run tests run: npm test - name: Versioning diff --git a/.github/workflows/release_staging.yml b/.github/workflows/release_staging.yml index 7a9b91273736..d179d8b4d3b8 100644 --- a/.github/workflows/release_staging.yml +++ b/.github/workflows/release_staging.yml @@ -40,6 +40,7 @@ jobs: GROWTHBOOK_DECRYPTION_KEY: ${{ vars.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ github.ref_name }} REMOTE_CONFIG_URL: ${{ vars.REMOTE_CONFIG_URL }} + TRUSTPILOT_API_KEY: ${{ secrets.TRUSTPILOT_API_KEY }} - name: Run tests run: npm test - name: Versioning diff --git a/.github/workflows/release_test.yml b/.github/workflows/release_test.yml index a554914d34ee..3105ba94dfe5 100644 --- a/.github/workflows/release_test.yml +++ b/.github/workflows/release_test.yml @@ -36,6 +36,7 @@ jobs: GROWTHBOOK_DECRYPTION_KEY: ${{ vars.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ github.ref_name }} REMOTE_CONFIG_URL: ${{ vars.REMOTE_CONFIG_URL }} + TRUSTPILOT_API_KEY: ${{ secrets.TRUSTPILOT_API_KEY }} - name: Run tests run: npm test - name: Publish to Cloudflare Pages Test diff --git a/.github/workflows/release_uat.yml b/.github/workflows/release_uat.yml index 576692230cd4..87b9b6685092 100644 --- a/.github/workflows/release_uat.yml +++ b/.github/workflows/release_uat.yml @@ -41,6 +41,7 @@ jobs: GROWTHBOOK_DECRYPTION_KEY: ${{ vars.GROWTHBOOK_DECRYPTION_KEY }} REF_NAME: ${{ github.ref_name }} REMOTE_CONFIG_URL: ${{ vars.REMOTE_CONFIG_URL }} + TRUSTPILOT_API_KEY: ${{ secrets.TRUSTPILOT_API_KEY }} - name: Versioning uses: "./.github/actions/versioning" with: diff --git a/.github/workflows/smoketests.yml b/.github/workflows/smoketests.yml index b734e82ac895..e1c94bbfdd84 100644 --- a/.github/workflows/smoketests.yml +++ b/.github/workflows/smoketests.yml @@ -34,13 +34,14 @@ jobs: - name: Cypress run # Uses the official Cypress GitHub action https://github.com/cypress-io/github-action + if: ${{ github.event.issue.draft == false && contains(github.event.issue.labels.*.name, 'Wallets') }} uses: cypress-io/github-action@97d526c9027e1b1eedde4f37196aebe8834005ef with: # Records to Cypress Cloud # https://docs.cypress.io/guides/cloud/projects#Set-up-a-project-to-record record: true parallel: true # Runs test in parallel using settings above - spec: cypress/e2e/api/oAuthLogin.cy.js + spec: cypress/e2e/smoke/Wallets/*.cy.js group: 'Smoke Tests' env: @@ -50,9 +51,10 @@ jobs: # Creating a token https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Set Base Url from client_payload. - CYPRESS_BASE_URL: ${{ steps.vercel_preview_url.outputs.vercel_preview_url }} + CYPRESS_BASE_URL: https://staging-app.deriv.com/ # Send PR details to Cypress test run - COMMIT_INFO_MESSAGE: PR "${{ github.event.issue.number }}" Changed By "${{ github.event.issue.user.login }}" in Labels "${{ github.event.issue.lables.name }}" (draft? "${{ github.event.issue.draft }} )" + COMMIT_INFO_MESSAGE: PR "${{ github.event.issue.number }}" Changed By "${{ github.event.issue.user.login }}" in Labels "${join(github.event.issue.labels.*.name, ', ')}" (draft? "${{ github.event.issue.draft }}") + # Set login env variables E2E_OAUTH_URL: ${{ secrets.E2E_OAUTH_URL }} E2E_CONFIG_APPID: ${{ secrets.E2E_CONFIG_APPID }} @@ -60,10 +62,59 @@ jobs: E2E_MT5_LOGIN: ${{ secrets.E2E_MT5_LOGIN }} E2E_MT5_PASSWORD: ${{ secrets.E2E_MT5_PASSWORD }} E2E_MT5_BASEURL: ${{ secrets.E2E_MT5_BASEURL }} + E2E_STD_CONFIG_SERVER: ${{ env.QA_SERVER || secrets.E2E_STD_CONFIG_SERVER }} + E2E_STD_CONFIG_APPID: ${{ github.event.inputs.appid || secrets.E2E_STD_CONFIG_APPID }} + E2E_DOUGHFLOW_CONFIG_SERVER: ${{ secrets.E2E_DOUGHFLOW_CONFIG_SERVER }} + E2E_DOUGHFLOW_CONFIG_APPID: ${{ secrets.E2E_DOUGHFLOW_CONFIG_APPID }} + E2E_QABOX_URL: ${{ secrets.E2E_QABOX_URL }} + E2E_MAIN_QABOX_URL: ${{ secrets.E2E_MAIN_QABOX_URL }} + E2E_QABOX_LOGIN: ${{ secrets.E2E_QABOX_LOGIN }} + E2E_QABOX_PASSWORD: ${{ secrets.E2E_QABOX_PASSWORD }} + TEST_SUITE: ${{ github.event.inputs.suite }} + E2E_DERIV_LOGIN_PROD: ${{secrets.E2E_DERIV_LOGIN_PROD}} + E2E_DERIV_PASSWORD_PROD: ${{secrets.E2E_DERIV_PASSWORD_PROD }} + E2E_PROD_SERVER: ${{secrets.E2E_PROD_SERVER}} + E2E_PROD_APPID: ${{secrets.E2E_PROD_APPID}} + E2E_LOGIN_ID_P2P_FIXEDRATE: ${{ secrets.E2E_LOGIN_ID_P2P_FIXEDRATE }} + E2E_P2P_FLOATING: ${{secrets.E2E_P2P_FLOATING}} + E2E_LOGIN_ID_P2P_STANDARDACCOUNTWITHADS: ${{secrets.E2E_LOGIN_ID_P2P_STANDARDACCOUNTWITHADS}} + E2E_LOGIN_ID_P2P_STANDARDACCOUNTWITHOUTADS: ${{secrets.E2E_LOGIN_ID_P2P_STANDARDACCOUNTWITHOUTADS}} + E2E_LOGIN_ID_P2P_FLOATINGRATE_SELLAD_1: ${{secrets.E2E_LOGIN_ID_P2P_FLOATINGRATE_SELLAD_1}} + E2E_LOGIN_ID_P2P_FLOATINGRATE_SELLAD_2: ${{secrets.E2E_LOGIN_ID_P2P_FLOATINGRATE_SELLAD_2}} + E2E_LOGIN_ID_P2P_EMPTYSTATE: ${{secrets.E2E_LOGIN_ID_P2P_EMPTYSTATE}} + E2E_LOGIN_ID_P2P_SORT: ${{secrets.E2E_LOGIN_ID_P2P_SORT}} + E2E_CRYPTO: ${{secrets.E2E_CRYPTO}} + E2E_STG_APPID: ${{secrets.E2E_STG_APPID}} + E2E_PSWD_P2P: ${{secrets.E2E_PSWD_P2P}} + E2E_CASHIER_WITHDRAWAL_PROD: ${{secrets.E2E_CASHIER_WITHDRAWAL_PROD}} + E2E_CASHIER_PROD_PASSWORD: ${{secrets.E2E_CASHIER_PROD_PASSWORD}} + E2E_LOGIN_ID_DBOT: ${{secrets.E2E_LOGIN_ID_DBOT}} + E2E_LOGIN_ID_PROD_DBOT: ${{secrets.E2E_LOGIN_ID_PROD_DBOT}} + E2E_QA_ACCOUNT_PASSWORD: ${{secrets.E2E_QA_ACCOUNT_PASSWORD}} + E2E_LOGIN_ID_CASHIER_LEGACY: ${{secrets.E2E_LOGIN_ID_CASHIER_LEGACY}} + E2E_LOGIN_ID_CASHIER_LEGACY_NON_USD: ${{secrets.E2E_LOGIN_ID_CASHIER_LEGACY_NON_USD}} + E2E_MAILISK_NAMESPACE: ${{secrets.E2E_MAILISK_NAMESPACE}} + E2E_MAILISK_API_KEY: ${{secrets.E2E_MAILISK_API_KEY}} + E2E_DIEL_LOGIN: ${{secrets.E2E_DIEL_LOGIN}} + E2E_EU_LOGIN: ${{secrets.E2E_EU_LOGIN}} + E2E_APP_REGISTER_URL: ${{ steps.vercel_preview_url.outputs.vercel_preview_url }} + E2E_RUN_FROM_PR: ${{secrets.E2E_RUN_FROM_PR}} + E2E_DERIV_LOGIN_WALLET_MOBILE : ${{ secrets.E2E_DERIV_LOGIN_WALLET_MOBILE }} + E2E_WALLET_MIGRATION_NEWCLIENT : ${{ secrets.E2E_WALLET_MIGRATION_NEWCLIENT }} + E2E_WALLET_MIGRATION_NO_VRTC : ${{ secrets.E2E_WALLET_MIGRATION_NO_VRTC }} + E2E_WALLET_MIGRATION_VRTCONLY : ${{ secrets.E2E_WALLET_MIGRATION_VRTCONLY }} + E2E_WALLET_MIGRATION_NO_CURRENCY : ${{ secrets.E2E_WALLET_MIGRATION_NO_CURRENCY }} + E2E_WALLET_MIGRATION_NON_USD : ${{ secrets.E2E_WALLET_MIGRATION_NON_USD }} + E2E_WALLET_MIGRATION_P2P : ${{ secrets.E2E_WALLET_MIGRATION_P2P }} + E2E_WALLET_MIGRATION_PA : ${{ secrets.E2E_WALLET_MIGRATION_PA }} + E2E_WALLET_MIGRATION_PA_CLIENT : ${{ secrets.E2E_WALLET_MIGRATION_PA_CLIENT }} + E2E_DERIV_LOGIN_WALLET: ${{ secrets.E2E_DERIV_LOGIN_WALLET }} + E2E_WALLETS_LOGIN_PROD: ${{ secrets.E2E_WALLETS_LOGIN_PROD }} + E2E_WALLETS_PASSWORD_PROD: ${{ secrets.E2E_WALLETS_PASSWORD_PROD }} - name: Set comments message id: set_msg - if: always() + if: always() && ${{ github.event.issue.draft == false && contains(github.event.issue.labels.*.name, 'Wallets') }} run: | # Using shell script to conditionally set the message if [[ "${{ job.status }}" == "success" ]]; then @@ -73,7 +124,7 @@ jobs: fi - name: Leave comment - if: always() + if: always() && ${{ github.event.issue.draft == false && contains(github.event.issue.labels.*.name, 'Wallets') }} uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 with: header: Smoke tests status update diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3fddd2b24ce5..1d9c48806b34 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,22 +37,16 @@ jobs: run: npx tsc --project packages/wallets/tsconfig.json -noEmit - name: Check TypeScript for @deriv/tradershub run: npx tsc --project packages/tradershub/tsconfig.json -noEmit - - name: Check TypeScript for @deriv/account-v2 - run: npx tsc --project packages/account-v2/tsconfig.json -noEmit - name: Check TypeScript for @deriv/cashier-v2 run: npx tsc --project packages/cashier-v2/tsconfig.json -noEmit - name: Check ESLint for @deriv/wallets run: npx eslint --fix --ignore-path packages/wallets/.eslintignore --config packages/wallets/.eslintrc.js packages/wallets - name: Check ESLint for @deriv/tradershub run: npx eslint --fix --ignore-path packages/tradershub/.eslintignore --config packages/tradershub/.eslintrc.js packages/tradershub - - name: Check ESLint for @deriv/account-v2 - run: npx eslint --fix --ignore-path packages/account-v2/.eslintignore --config packages/account-v2/.eslintrc.js packages/account-v2 - name: Check ESLint for @deriv/cashier-v2 run: npx eslint --fix --ignore-path packages/cashier-v2/.eslintignore --config packages/cashier-v2/.eslintrc.js packages/cashier-v2 - name: Check Stylelint for @deriv/wallets run: npx stylelint packages/wallets/**/*.scss - - name: Check Stylelint for @deriv/account-v2 - run: npx stylelint packages/account-v2/**/*.scss - name: Check Stylelint for @deriv/cashier-v2 run: npx stylelint packages/cashier-v2/**/*.scss - name: Check tests for @deriv/hooks diff --git a/analyze.html b/analyze.html index ed275f5f9c0b..078e248e7796 100644 --- a/analyze.html +++ b/analyze.html @@ -57,9 +57,6 @@ + + + ); +}); + +export default GetStartedTradingBanner; diff --git a/packages/appstore/src/components/get-started-trading-banner/index.ts b/packages/appstore/src/components/get-started-trading-banner/index.ts new file mode 100644 index 000000000000..232e758172c7 --- /dev/null +++ b/packages/appstore/src/components/get-started-trading-banner/index.ts @@ -0,0 +1,3 @@ +import GetStartedTradingBanner from './get-started-trading-banner'; + +export default GetStartedTradingBanner; diff --git a/packages/appstore/src/components/main-title-bar/index.tsx b/packages/appstore/src/components/main-title-bar/index.tsx index afda0527277e..db7b14198c1e 100644 --- a/packages/appstore/src/components/main-title-bar/index.tsx +++ b/packages/appstore/src/components/main-title-bar/index.tsx @@ -21,9 +21,10 @@ const WalletsBanner = makeLazyLoader( const MainTitleBar = () => { const { traders_hub, client } = useStore(); + const { is_landing_company_loaded, is_switching } = client; const { state: wallet_migration_state } = useWalletMigration(); const { selected_region, handleTabItemClick, toggleRegulatorsCompareModal, content_flag } = traders_hub; - const { is_landing_company_loaded, is_switching } = client; + const is_low_risk_cr_real_account = content_flag === ContentFlag.LOW_RISK_CR_NON_EU || content_flag === ContentFlag.LOW_RISK_CR_EU; const show_wallets_banner = wallet_migration_state && wallet_migration_state !== 'ineligible'; diff --git a/packages/appstore/src/components/modals/modal-manager.tsx b/packages/appstore/src/components/modals/modal-manager.tsx index fea9915c2aa2..6e9a270cf040 100644 --- a/packages/appstore/src/components/modals/modal-manager.tsx +++ b/packages/appstore/src/components/modals/modal-manager.tsx @@ -180,7 +180,14 @@ const ModalManager = () => { const { is_eligible, is_in_progress } = useWalletMigration(); const store = useStores(); const { common, client, modules, traders_hub, ui } = store; - const { is_logged_in, is_eu, is_eu_country, is_populating_mt5_account_list, verification_code } = client; + const { + is_logged_in, + is_eu, + is_eu_country, + is_populating_mt5_account_list, + verification_code, + should_show_effortless_login_modal, + } = client; const { platform } = common; const { current_list, @@ -338,9 +345,13 @@ const ModalManager = () => { /> )} {is_failed_verification_modal_visible && } - {(is_real_wallets_upgrade_on || is_in_progress) && } - {is_wallet_migration_failed && } - {is_eligible && } + {!should_show_effortless_login_modal && ( + + {(is_real_wallets_upgrade_on || is_in_progress) && } + {is_wallet_migration_failed && } + {is_eligible && } + + )} ); }; diff --git a/packages/appstore/src/components/modals/open-positions-svg-modal/__test__/open-positions-svg-modal.spec.tsx b/packages/appstore/src/components/modals/open-positions-svg-modal/__test__/open-positions-svg-modal.spec.tsx index 11016d2dadf9..cb0e7dabd52f 100644 --- a/packages/appstore/src/components/modals/open-positions-svg-modal/__test__/open-positions-svg-modal.spec.tsx +++ b/packages/appstore/src/components/modals/open-positions-svg-modal/__test__/open-positions-svg-modal.spec.tsx @@ -108,7 +108,7 @@ describe('', () => { expect(modal_content_vanuatu).toBeInTheDocument(); }); - it('should render modal content correctly when status is migrated_with_position and market_type is derived', () => { + it('should render modal content correctly when status is migrated_with_position and market_type is standard', () => { const new_store: TStores = { ...store_config, client: { @@ -133,19 +133,19 @@ describe('', () => { ], }, }; - const mock_props_derived: React.ComponentProps = { + const mock_props_standard: React.ComponentProps = { ...mock_props, market_type: 'synthetic', }; - renderComponent({ props: mock_props_derived, store: new_store }); + renderComponent({ props: mock_props_standard, store: new_store }); - const modal_content_derived = screen.getByText( - /You can no longer open new positions with your MT5 Derived SVG account. Please use your MT5 Derived Vanuatu or MT5 Derived BVI account to open new positions./ + const modal_content_standard = screen.getByText( + /You can no longer open new positions with your MT5 Standard SVG account. Please use your MT5 Standard Vanuatu or MT5 Standard BVI account to open new positions./ ); - expect(modal_content_derived).toBeInTheDocument(); + expect(modal_content_standard).toBeInTheDocument(); }); - it('should render modal content correctly when status is migrated_without_position and market_type is derived', () => { + it('should render modal content correctly when status is migrated_without_position and market_type is standard', () => { const new_mock_props: React.ComponentProps = { ...mock_props, market_type: 'synthetic', @@ -153,7 +153,7 @@ describe('', () => { }; renderComponent({ props: new_mock_props }); const modal_content = screen.getByText( - /Your MT5 Derived SVG account will be archived after 30 days of inactivity. You can still access your trade history until the account is archived./ + /Your MT5 Standard SVG account will be archived after 30 days of inactivity. You can still access your trade history until the account is archived./ ); expect(modal_content).toBeInTheDocument(); }); diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.scss b/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.scss new file mode 100644 index 000000000000..948d5670f454 --- /dev/null +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.scss @@ -0,0 +1,6 @@ +.dc-modal__container_desktop-real-wallets-upgrade { + .dc-modal-body { + width: calc(100vw - 2rem); + max-width: 120rem; + } +} diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.tsx b/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.tsx index 3d7f7d1561c0..fecb568a80be 100644 --- a/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.tsx +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/desktop-real-wallets-upgrade/desktop-real-wallets-upgrade.tsx @@ -3,6 +3,7 @@ import { Modal } from '@deriv/components'; import { useStore, observer } from '@deriv/stores'; import { TRealWalletsUpgradeSteps } from 'Types'; import WalletSteps from '../wallet_steps'; +import './desktop-real-wallets-upgrade.scss'; const DesktopRealWalletsUpgrade = observer(({ wallet_upgrade_steps }: TRealWalletsUpgradeSteps) => { const { traders_hub } = useStore(); @@ -13,10 +14,10 @@ const DesktopRealWalletsUpgrade = observer(({ wallet_upgrade_steps }: TRealWalle return ( { - const { traders_hub: is_real_wallets_upgrade_on } = useStore(); + const { traders_hub } = useStore(); + const { is_real_wallets_upgrade_on } = traders_hub; const wallet_steps = WalletSteps(wallet_upgrade_steps); const { current_step, handleBack, handleNext, handleClose } = wallet_upgrade_steps; @@ -45,7 +46,9 @@ const MobileRealWalletsUpgrade = observer(({ wallet_upgrade_steps }: TRealWallet if (index < current_step) handleBack(); }} > - {wallet_steps.map(slide => slide.content)} + {wallet_steps.map(slide => ( + {slide.content} + ))} diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/wallet_steps.tsx b/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/wallet_steps.tsx index 24b65ee28406..0a7c88d6b025 100644 --- a/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/wallet_steps.tsx +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/components/modal-elements/wallet_steps.tsx @@ -3,14 +3,7 @@ import { WalletsUpgradeStepOneContent, WalletsUpgradeStepOneFooter } from '../wa import { WalletsUpgradeStepTwoContent, WalletsUpgradeStepTwoFooter } from '../wallets-upgrade-step-two'; import { TWalletSteps } from 'Types'; -const WalletSteps = ({ - handleBack, - handleClose, - handleNext, - is_disabled, - toggleCheckbox, - upgradeToWallets, -}: TWalletSteps) => [ +const WalletSteps = ({ handleBack, handleClose, handleNext, is_migrating, upgradeToWallets }: TWalletSteps) => [ { name: 'wallets_upgrade_step_one', content: , @@ -18,11 +11,11 @@ const WalletSteps = ({ }, { name: 'wallets_upgrade_step_two', - content: , + content: , footer: ( ), diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/__tests__/wallets-upgrade-step-two.spec.tsx b/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/__tests__/wallets-upgrade-step-two.spec.tsx index f82263288e6f..b6f456e80849 100644 --- a/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/__tests__/wallets-upgrade-step-two.spec.tsx +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/__tests__/wallets-upgrade-step-two.spec.tsx @@ -5,12 +5,11 @@ import { StoreProvider, mockStore } from '@deriv/stores'; describe('WalletsUpgradeStepTwoContent', () => { const containerReadyToEnableWallets = (mock: ReturnType) => { - const toggleCheckbox = jest.fn(); const wrapper = ({ children }: { children: JSX.Element }) => ( {children} ); - return render(, { + return render(, { wrapper, }); }; @@ -22,13 +21,6 @@ describe('WalletsUpgradeStepTwoContent', () => { expect(container).toBeInTheDocument(); }); - it('should render checkbox', () => { - const mock = mockStore({}); - containerReadyToEnableWallets(mock); - - expect(screen.getByRole('checkbox')).toBeInTheDocument(); - }); - it('should render the info sections', () => { const mock = mockStore({}); containerReadyToEnableWallets(mock); @@ -42,8 +34,5 @@ describe('WalletsUpgradeStepTwoContent', () => { expect( screen.getByText('Your open trading positions will not be affected while we are setting up your wallets.') ).toBeInTheDocument(); - expect( - screen.getByText('I acknowledge and confirm that I would like to upgrade to Wallets.') - ).toBeInTheDocument(); }); }); diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.scss b/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.scss index e3593c5545c7..1967c4bee7ad 100644 --- a/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.scss +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.scss @@ -12,6 +12,10 @@ } } + &__description { + max-width: 63.8rem; + } + &__footer { position: sticky; bottom: 0; diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.tsx b/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.tsx index b94b068c141d..21228b82b7ad 100644 --- a/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.tsx +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/components/wallets-upgrade-step-two/wallets-upgrade-step-two.tsx @@ -1,22 +1,16 @@ import React from 'react'; -import { Button, Checkbox, Icon, Modal, Text } from '@deriv/components'; -import { Localize, localize } from '@deriv/translations'; +import { Button, Icon, Modal, Text } from '@deriv/components'; +import { Localize } from '@deriv/translations'; import { observer, useStore } from '@deriv/stores'; import './wallets-upgrade-step-two.scss'; -type TWalletsUpgradeStepTwoContent = { - value: boolean; - toggleCheckbox: VoidFunction; -}; - type TWalletsUpgradeStepTwoFooter = { handleBack: VoidFunction; - is_disabled: boolean; is_migrating: boolean; upgradeToWallets: (value: boolean) => void; }; -const WalletsUpgradeStepTwoContent = observer(({ value, toggleCheckbox }: TWalletsUpgradeStepTwoContent) => { +const WalletsUpgradeStepTwoContent = observer(() => { const { ui } = useStore(); const { is_mobile } = ui; @@ -33,9 +27,11 @@ const WalletsUpgradeStepTwoContent = observer(({ value, toggleCheckbox }: TWalle - - - +
+ + + +
@@ -43,23 +39,11 @@ const WalletsUpgradeStepTwoContent = observer(({ value, toggleCheckbox }: TWalle
- ); }); -const WalletsUpgradeStepTwoFooter = ({ - handleBack, - is_disabled, - is_migrating, - upgradeToWallets, -}: TWalletsUpgradeStepTwoFooter) => { +const WalletsUpgradeStepTwoFooter = ({ handleBack, is_migrating, upgradeToWallets }: TWalletsUpgradeStepTwoFooter) => { const { ui } = useStore(); const { is_desktop } = ui; @@ -74,7 +58,7 @@ const WalletsUpgradeStepTwoFooter = ({ primary large className='wallets-upgrade-step-two__footer-button' - disabled={!is_disabled || is_migrating} + disabled={is_migrating} onClick={upgradeToWallets} is_loading={is_migrating} > diff --git a/packages/appstore/src/components/modals/real-wallets-upgrade/real-wallets-upgrade.tsx b/packages/appstore/src/components/modals/real-wallets-upgrade/real-wallets-upgrade.tsx index c5e8724b9175..666e60b80f4b 100644 --- a/packages/appstore/src/components/modals/real-wallets-upgrade/real-wallets-upgrade.tsx +++ b/packages/appstore/src/components/modals/real-wallets-upgrade/real-wallets-upgrade.tsx @@ -7,21 +7,19 @@ const RealWalletsUpgrade = observer(() => { const { traders_hub, ui } = useStore(); const { is_real_wallets_upgrade_on, toggleWalletsUpgrade } = traders_hub; const { is_mobile } = ui; - const { startMigration, is_eligible, is_migrating } = useWalletMigration(); + const { startMigration, is_in_progress, is_ineligible, is_migrated, is_migrating } = useWalletMigration(); const [current_step, setCurrentStep] = React.useState(0); - const [is_disabled, setIsDisabled] = React.useState(false); React.useEffect(() => { - if (!is_eligible) { + if (is_migrating || is_in_progress || is_ineligible || is_migrated) { toggleWalletsUpgrade(false); } - }, [is_eligible, toggleWalletsUpgrade]); + }, [is_in_progress, is_ineligible, is_migrated, is_migrating, toggleWalletsUpgrade]); React.useEffect(() => { if (!is_real_wallets_upgrade_on) { setCurrentStep(0); - setIsDisabled(false); } }, [is_real_wallets_upgrade_on]); @@ -35,18 +33,12 @@ const RealWalletsUpgrade = observer(() => { startMigration(); }; - const toggleCheckbox = () => { - setIsDisabled(prevDisabled => !prevDisabled); - }; - const wallet_upgrade_steps = { current_step, handleBack, handleClose, handleNext, - is_disabled, is_migrating, - toggleCheckbox, upgradeToWallets, }; diff --git a/packages/appstore/src/components/multi-action-button-group/__test__/multi-action-button-group.spec.tsx b/packages/appstore/src/components/multi-action-button-group/__test__/multi-action-button-group.spec.tsx index d7071ce40418..bc49d3b9d3fd 100644 --- a/packages/appstore/src/components/multi-action-button-group/__test__/multi-action-button-group.spec.tsx +++ b/packages/appstore/src/components/multi-action-button-group/__test__/multi-action-button-group.spec.tsx @@ -7,7 +7,7 @@ import { createBrowserHistory } from 'history'; import { routes } from '@deriv/shared'; const mock_props = { - link_to: routes.trader, + link_to: routes.trade, onAction: jest.fn(), is_buttons_disabled: false, is_real: true, @@ -69,10 +69,6 @@ describe('Test Cases for Multi Action Button Group:', () => { ); - - const open_btn = screen.getByText('Open'); - userEvent.click(open_btn); - - expect(history.location.pathname).toBe(routes.trade); + expect(screen.getByRole('link')).toHaveAttribute('href', routes.trade); }); }); diff --git a/packages/appstore/src/components/options-multipliers-listing-logged-out/index.tsx b/packages/appstore/src/components/options-multipliers-listing-logged-out/index.tsx new file mode 100644 index 000000000000..c4ffbe469387 --- /dev/null +++ b/packages/appstore/src/components/options-multipliers-listing-logged-out/index.tsx @@ -0,0 +1,3 @@ +import OptionsAndMultipliersListingLoggedOut from './options-multipliers-listing-logged-out'; + +export default OptionsAndMultipliersListingLoggedOut; diff --git a/packages/appstore/src/components/options-multipliers-listing-logged-out/options-multipliers-listing-logged-out.scss b/packages/appstore/src/components/options-multipliers-listing-logged-out/options-multipliers-listing-logged-out.scss new file mode 100644 index 000000000000..71d62a4eb072 --- /dev/null +++ b/packages/appstore/src/components/options-multipliers-listing-logged-out/options-multipliers-listing-logged-out.scss @@ -0,0 +1,5 @@ +.options-multipliers-listing-logged-out { + &__description { + max-width: 65rem; + } +} diff --git a/packages/appstore/src/components/options-multipliers-listing-logged-out/options-multipliers-listing-logged-out.tsx b/packages/appstore/src/components/options-multipliers-listing-logged-out/options-multipliers-listing-logged-out.tsx new file mode 100644 index 000000000000..3389cefcd389 --- /dev/null +++ b/packages/appstore/src/components/options-multipliers-listing-logged-out/options-multipliers-listing-logged-out.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useStore, observer } from '@deriv/stores'; +import ListingContainer from 'Components/containers/listing-container'; +import TradingAppCard from 'Components/containers/trading-app-card'; +import OptionsDescription from 'Components/elements/options-description'; +import OptionsTitle from 'Components/elements/options-title'; +import { BrandConfig } from 'Constants/platform-config'; +import { getHasDivider } from 'Constants/utils'; +import { isEuCountry } from '@deriv/shared'; +import './options-multipliers-listing-logged-out.scss'; + +const OptionsAndMultipliersListingLoggedOut = observer(() => { + const { traders_hub, client } = useStore(); + const { clients_country } = client; + const { available_platforms, is_eu_user } = traders_hub; + + const logged_out_available_platforms = isEuCountry(clients_country) + ? available_platforms.filter(platform => ['EU', 'All'].some(region => region === platform.availability)) + : available_platforms.filter(platform => ['Non-EU', 'All'].some(region => region === platform.availability)); + + return ( + } + description={} + > + {logged_out_available_platforms.map((available_platform: BrandConfig, index: number) => ( + + ))} + + ); +}); + +export default OptionsAndMultipliersListingLoggedOut; diff --git a/packages/appstore/src/components/options-multipliers-listing/index.tsx b/packages/appstore/src/components/options-multipliers-listing/index.tsx index 7565255d255e..128a298c8c4e 100644 --- a/packages/appstore/src/components/options-multipliers-listing/index.tsx +++ b/packages/appstore/src/components/options-multipliers-listing/index.tsx @@ -132,9 +132,7 @@ const OptionsAndMultipliersListing = observer(() => { action: 'account_open', form_name: 'traders_hub_default', account_mode: selected_account_type, - account_name: is_demo - ? `${available_platform.name} ${localize('Demo')}` - : available_platform.name, + account_name: is_demo ? `${available_platform.name} Demo` : available_platform.name, }); }} has_divider={(!is_eu_user || is_demo) && getHasDivider(index, available_platforms.length, 3)} diff --git a/packages/appstore/src/components/ordered-platform-sections/index.ts b/packages/appstore/src/components/ordered-platform-sections/index.ts new file mode 100644 index 000000000000..2348ad9ea7c3 --- /dev/null +++ b/packages/appstore/src/components/ordered-platform-sections/index.ts @@ -0,0 +1,3 @@ +import OrderedPlatformSections from './ordered-platform-sections'; + +export default OrderedPlatformSections; diff --git a/packages/appstore/src/components/ordered-platform-sections/ordered-platform-sections.scss b/packages/appstore/src/components/ordered-platform-sections/ordered-platform-sections.scss new file mode 100644 index 000000000000..012ade862281 --- /dev/null +++ b/packages/appstore/src/components/ordered-platform-sections/ordered-platform-sections.scss @@ -0,0 +1,9 @@ +.ordered-platform-sections { + display: flex; + flex-direction: column; + gap: 2.4rem; + + &__reversed { + flex-direction: column-reverse; + } +} diff --git a/packages/appstore/src/components/ordered-platform-sections/ordered-platform-sections.tsx b/packages/appstore/src/components/ordered-platform-sections/ordered-platform-sections.tsx new file mode 100644 index 000000000000..f38d19f50e56 --- /dev/null +++ b/packages/appstore/src/components/ordered-platform-sections/ordered-platform-sections.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import classNames from 'classnames'; +import { observer, useStore } from '@deriv/stores'; +import OptionsAndMultipliersListingLoggedOut from 'Components/options-multipliers-listing-logged-out'; +import CFDsListingLoggedOut from 'Components/cfds-listing-logged-out'; +import './ordered-platform-sections.scss'; + +type TGetOrderedPlatformSections = { + is_cfd_visible?: boolean; + is_options_and_multipliers_visible?: boolean; +}; + +type TOrderedPlatformSections = { + isDesktop?: boolean; +}; + +const GetOrderedPlatformSections = observer( + ({ is_cfd_visible = true, is_options_and_multipliers_visible = true }: TGetOrderedPlatformSections) => { + const { traders_hub } = useStore(); + const { is_eu_user } = traders_hub; + + return ( +
+ {is_options_and_multipliers_visible && } + {is_cfd_visible && } +
+ ); + } +); + +const OrderedPlatformSections = observer(({ isDesktop = false }: TOrderedPlatformSections) => { + const { traders_hub, client } = useStore(); + const { is_mt5_allowed, is_logged_in } = client; + const { selected_platform_type } = traders_hub; + + if ((is_logged_in && is_mt5_allowed) || !is_logged_in) { + return isDesktop ? ( + + ) : ( + + ); + } + return ; +}); + +export default OrderedPlatformSections; diff --git a/packages/appstore/src/components/real-account-creation-banner/__tests__/real-account-creation-banner.spec.tsx b/packages/appstore/src/components/real-account-creation-banner/__tests__/real-account-creation-banner.spec.tsx new file mode 100644 index 000000000000..e6d31c7b05c0 --- /dev/null +++ b/packages/appstore/src/components/real-account-creation-banner/__tests__/real-account-creation-banner.spec.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import RealAccountCreationBanner from '../real-account-creation-banner'; +import { mockStore, StoreProvider } from '@deriv/stores'; +import userEvent from '@testing-library/user-event'; + +describe('RealAccountCreationBanner', () => { + const mock = mockStore({}); + const wrapper = ({ children }: { children: JSX.Element }) => {children}; + + it('Should render the RealAccountCreationBanner', () => { + const { container } = render(, { + wrapper, + }); + expect(container).toBeInTheDocument(); + }); + it('Should render the real account creation banner and text to be visbile', () => { + render(, { + wrapper, + }); + expect(screen.getByText('Get a real account to deposit money and start trading.')).toBeInTheDocument(); + expect(screen.getByText('Get real account')).toBeInTheDocument(); + }); + + it('should call openRealAccountSignup with svg when button is clicked for svg (cr) client', () => { + render(, { + wrapper, + }); + userEvent.click(screen.getByText('Get real account')); + expect(mock.ui.openRealAccountSignup).toBeCalledWith('svg'); + }); +}); diff --git a/packages/appstore/src/components/real-account-creation-banner/index.tsx b/packages/appstore/src/components/real-account-creation-banner/index.tsx new file mode 100644 index 000000000000..04366a3984cf --- /dev/null +++ b/packages/appstore/src/components/real-account-creation-banner/index.tsx @@ -0,0 +1,3 @@ +import RealAccountCreationBanner from './real-account-creation-banner'; + +export default RealAccountCreationBanner; diff --git a/packages/appstore/src/components/real-account-creation-banner/real-account-creation-banner.scss b/packages/appstore/src/components/real-account-creation-banner/real-account-creation-banner.scss new file mode 100644 index 000000000000..785498651f19 --- /dev/null +++ b/packages/appstore/src/components/real-account-creation-banner/real-account-creation-banner.scss @@ -0,0 +1,29 @@ +.real-account-creation-banner { + position: relative; + width: 100%; + margin-bottom: 2.4rem; + display: flex; + + img { + width: 100%; + object-fit: contain; + } + + &__content { + @include mobile { + padding: 1.4rem 1.6rem; + width: 25rem; + } + + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.8rem; + position: absolute; + top: 0; + left: 0; + z-index: 1; + padding: 3rem 4.8rem; + width: 50rem; + } +} diff --git a/packages/appstore/src/components/real-account-creation-banner/real-account-creation-banner.tsx b/packages/appstore/src/components/real-account-creation-banner/real-account-creation-banner.tsx new file mode 100644 index 000000000000..5d7496054639 --- /dev/null +++ b/packages/appstore/src/components/real-account-creation-banner/real-account-creation-banner.tsx @@ -0,0 +1,56 @@ +import React, { useEffect } from 'react'; +import './real-account-creation-banner.scss'; +import { getUrlBase, Jurisdiction } from '@deriv/shared'; +import { Analytics } from '@deriv-com/analytics'; +import { Localize } from '@deriv/translations'; +import { Text, Button } from '@deriv/components'; +import { observer, useStore } from '@deriv/stores'; + +const RealAccountCreationBanner = observer(() => { + const { + ui: { is_mobile }, + } = useStore(); + + const { ui } = useStore(); + const { openRealAccountSignup, is_dark_mode_on } = ui; + const device = is_mobile ? 'mobile' : 'desktop'; + + const handleClick = () => { + Analytics.trackEvent('ce_tradershub_banner', { + action: 'click_cta', + banner_name: 'real_account_cta', + banner_type: 'with_cta', + }); + openRealAccountSignup(Jurisdiction.SVG); + }; + + useEffect(() => { + Analytics.trackEvent('ce_tradershub_banner', { + action: 'open', + banner_name: 'real_account_cta', + banner_type: 'with_cta', + }); + }, []); + + return ( +
+ Deriv real account banner + +
+ + + + +
+
+ ); +}); + +export default RealAccountCreationBanner; diff --git a/packages/appstore/src/components/routes/routes.tsx b/packages/appstore/src/components/routes/routes.tsx index 9b128ad807dc..5e9773354107 100644 --- a/packages/appstore/src/components/routes/routes.tsx +++ b/packages/appstore/src/components/routes/routes.tsx @@ -1,39 +1,48 @@ import * as React from 'react'; import { Loading } from '@deriv/components'; -import { useFeatureFlags /*useWalletsList*/ } from '@deriv/hooks'; -import { observer } from '@deriv/stores'; +import { observer, useStore } from '@deriv/stores'; import { localize } from '@deriv/translations'; import { routes } from '@deriv/shared'; -import { Switch, useHistory } from 'react-router-dom'; +import { Switch } from 'react-router-dom'; import RouteWithSubroutes from './route-with-sub-routes.jsx'; const Onboarding = React.lazy(() => import(/* webpackChunkName: "modules-onboarding" */ 'Modules/onboarding')); const TradersHub = React.lazy(() => import(/* webpackChunkName: "modules-traders-hub" */ 'Modules/traders-hub')); +const TradersHubLoggedOut = React.lazy( + () => import(/* webpackChunkName: "modules-traders-hub-logged-out" */ 'Modules/traders-hub-logged-out') +); +const Page404 = React.lazy(() => import(/* */ 'Modules/Page404')); const Routes: React.FC = observer(() => { - //TODO: Uncomment once useWalletList hook is optimized for production release. - const { /*is_wallet_enabled,*/ is_next_wallet_enabled } = useFeatureFlags(); - const history = useHistory(); - // const { has_wallet, isLoading } = useWalletsList(); - // const should_show_wallets = is_wallet_enabled && has_wallet; + const { client } = useStore(); + const { is_logged_in, is_logging_in } = client; - React.useLayoutEffect(() => { - if (is_next_wallet_enabled) history.push(routes.wallets); - }, [history, is_next_wallet_enabled]); + const title_TH = localize("Trader's Hub"); + const title_TH_logged_out = localize('Deriv App'); + + const componentToRender = () => { + if (is_logged_in || is_logging_in) { + return TradersHub; + } + return TradersHubLoggedOut; + }; return ( }> localize("Trader's Hub")} + path={routes.traders_hub} + exact + component={componentToRender()} + getTitle={() => (is_logged_in || is_logging_in ? title_TH : title_TH_logged_out)} /> localize('Onboarding')} /> + localize('Deriv App')} /> ); diff --git a/packages/appstore/src/components/tabs-or-title/index.ts b/packages/appstore/src/components/tabs-or-title/index.ts new file mode 100644 index 000000000000..1f0bd8623917 --- /dev/null +++ b/packages/appstore/src/components/tabs-or-title/index.ts @@ -0,0 +1,3 @@ +import TabsOrTitle from './tabs-or-title'; + +export default TabsOrTitle; diff --git a/packages/appstore/src/components/tabs-or-title/tabs-or-title.scss b/packages/appstore/src/components/tabs-or-title/tabs-or-title.scss new file mode 100644 index 000000000000..a8aebecd713e --- /dev/null +++ b/packages/appstore/src/components/tabs-or-title/tabs-or-title.scss @@ -0,0 +1,11 @@ +.tabs-or-title { + &__button-toggle { + border-radius: $BORDER_RADIUS; + padding: 0.4rem; + background-color: var(--general-section-1); + } + + &__mt5-not-allowed { + margin-top: 2rem; + } +} diff --git a/packages/appstore/src/components/tabs-or-title/tabs-or-title.tsx b/packages/appstore/src/components/tabs-or-title/tabs-or-title.tsx new file mode 100644 index 000000000000..b1a2ee13c7cb --- /dev/null +++ b/packages/appstore/src/components/tabs-or-title/tabs-or-title.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { observer, useStore } from '@deriv/stores'; +import { ButtonToggle, Text } from '@deriv/components'; +import { Localize } from '@deriv/translations'; +import { getPlatformToggleOptions } from 'Helpers'; +import './tabs-or-title.scss'; + +const TabsOrTitle = observer(() => { + const { traders_hub, client } = useStore(); + const { is_mt5_allowed, is_logged_in } = client; + const { selected_platform_type, setTogglePlatformType, is_eu_user } = traders_hub; + + const platform_toggle_options = getPlatformToggleOptions(is_eu_user); + const platform_toggle_options_eu = getPlatformToggleOptions(is_eu_user).reverse(); + + const platformTypeChange = (event: { + target: { + value: string; + name: string; + }; + }) => { + setTogglePlatformType(event.target.value); + }; + + return (is_logged_in && is_mt5_allowed) || !is_logged_in ? ( + + ) : ( +
+ + + +
+ ); +}); + +export default TabsOrTitle; diff --git a/packages/appstore/src/components/trustpilot-star-rating/index.ts b/packages/appstore/src/components/trustpilot-star-rating/index.ts new file mode 100644 index 000000000000..27cb7c9bfd2b --- /dev/null +++ b/packages/appstore/src/components/trustpilot-star-rating/index.ts @@ -0,0 +1,3 @@ +import TrustpilotStarRating from './trustpilot-star-rating'; + +export default TrustpilotStarRating; diff --git a/packages/appstore/src/components/trustpilot-star-rating/trustpilot-star-rating.scss b/packages/appstore/src/components/trustpilot-star-rating/trustpilot-star-rating.scss new file mode 100644 index 000000000000..c527c6373621 --- /dev/null +++ b/packages/appstore/src/components/trustpilot-star-rating/trustpilot-star-rating.scss @@ -0,0 +1,10 @@ +.trustpilot-star-rating { + display: flex; + justify-content: center; + align-items: center; + gap: 0.2rem; + + &__item { + padding: 0 0.2rem; + } +} diff --git a/packages/appstore/src/components/trustpilot-star-rating/trustpilot-star-rating.tsx b/packages/appstore/src/components/trustpilot-star-rating/trustpilot-star-rating.tsx new file mode 100644 index 000000000000..5196954fdc62 --- /dev/null +++ b/packages/appstore/src/components/trustpilot-star-rating/trustpilot-star-rating.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Icon } from '@deriv/components'; +import { observer, useStore } from '@deriv/stores'; +import './trustpilot-star-rating.scss'; + +const TrustpilotStarRating = observer(({ score }: { score: number }) => { + const { common } = useStore(); + const { current_language } = common; + const html = document.getElementsByTagName('html'); + const [is_rtl, setIsRtl] = React.useState(html?.[0]?.getAttribute('dir') === 'rtl'); + + React.useEffect(() => { + setIsRtl(html?.[0]?.getAttribute('dir') === 'rtl'); + }, [current_language]); + + return ( +
+ {[...Array(5)].map((_, idx) => ( +
+ +
+ ))} +
+ ); +}); + +export default TrustpilotStarRating; diff --git a/packages/appstore/src/components/trustpilot-widget/index.ts b/packages/appstore/src/components/trustpilot-widget/index.ts new file mode 100644 index 000000000000..d7cd36fdcb47 --- /dev/null +++ b/packages/appstore/src/components/trustpilot-widget/index.ts @@ -0,0 +1,3 @@ +import TrustpilotWidget from './trustpilot-widget'; + +export default TrustpilotWidget; diff --git a/packages/appstore/src/components/trustpilot-widget/trustpilot-widget.scss b/packages/appstore/src/components/trustpilot-widget/trustpilot-widget.scss new file mode 100644 index 000000000000..595c3a8a5682 --- /dev/null +++ b/packages/appstore/src/components/trustpilot-widget/trustpilot-widget.scss @@ -0,0 +1,13 @@ +.trustpilot-widget { + &__content { + display: flex; + justify-content: center; + align-items: center; + gap: 2.4rem; + padding: 1.6rem 0; + } + + a { + text-decoration: none; + } +} diff --git a/packages/appstore/src/components/trustpilot-widget/trustpilot-widget.tsx b/packages/appstore/src/components/trustpilot-widget/trustpilot-widget.tsx new file mode 100644 index 000000000000..6458ff0194db --- /dev/null +++ b/packages/appstore/src/components/trustpilot-widget/trustpilot-widget.tsx @@ -0,0 +1,59 @@ +import React, { useEffect, useState } from 'react'; +import { Icon, Text } from '@deriv/components'; +import { useStore, observer } from '@deriv/stores'; +import { Localize } from '@deriv/translations'; +import { TTrustpilotWidgetData } from 'Types'; +import { fetchTrustpilotData } from 'Helpers'; +import TrustpilotStarRating from 'Components/trustpilot-star-rating'; +import './trustpilot-widget.scss'; + +const TrustpilotWidget = observer(() => { + const [trustpilotData, setTrustpilotData] = useState(); + const { ui } = useStore(); + const { is_mobile } = ui; + + useEffect(() => { + const getTrustpilotData = async () => { + const res = await fetchTrustpilotData(); + setTrustpilotData(res); + }; + + getTrustpilotData(); + }, []); + + if (!trustpilotData) return null; + + return ( + + ); +}); + +export default TrustpilotWidget; diff --git a/packages/appstore/src/constants/tour-steps-config.tsx b/packages/appstore/src/constants/tour-steps-config.tsx deleted file mode 100644 index 465df8a5112c..000000000000 --- a/packages/appstore/src/constants/tour-steps-config.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import React from 'react'; -import { Step, Locale } from 'react-joyride'; -import { Text, SpanButton, Icon } from '@deriv/components'; -import { Localize, localize } from '@deriv/translations'; -import { isMobile } from '@deriv/shared'; -import 'Components/toggle-account-type/toggle-account-type.scss'; - -const stepProps = { - disableBeacon: true, - disableOverlayClose: true, -}; - -export const tour_step_config: Step[] = [ - { - title: ( - - - - -
- - ), - content: ( - - - - ), - target: '.account-type-dropdown--parent', - ...stepProps, - }, - { - title: ( - - - - -
- - ), - content: ( - - - - , - ]} - /> - - ), - target: isMobile() ? '.main-title-bar-mobile--regulator' : '.regulators-switcher__container', - ...stepProps, - }, - { - title: ( - - - - -
- - ), - content: ( - - - - ), - target: '.traders-hub-header__tradershub--onboarding--logo', - ...stepProps, - }, -]; - -export const tour_step_config_high_risk: Step[] = [ - { - title: ( - - - - -
- - ), - content: ( - - - - ), - target: '.account-type-dropdown--parent', - ...stepProps, - }, - { - title: ( - - - - -
- - ), - content: ( - - - - ), - target: '.traders-hub-header__tradershub--onboarding--logo', - ...stepProps, - }, -]; - -export const getHighRiskTourStepLocale = (): Locale => ({ - back: , - last: , - next: , -}); - -export const getTourStepLocale = (): Locale => ({ - back: , - last: , - next: , -}); diff --git a/packages/appstore/src/constants/tour-steps-styles.ts b/packages/appstore/src/constants/tour-steps-styles.ts deleted file mode 100644 index 6fbfbe1f121b..000000000000 --- a/packages/appstore/src/constants/tour-steps-styles.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Styles } from 'react-joyride'; - -type TJoyrideStyles = Pick; - -const tourStyles = { - options: { - width: 350, - }, - tooltipTitle: { - color: 'var(--brand-red-coral)', - textAlign: 'left', - }, - tooltipContent: { - textAlign: 'left', - fontSize: '1.6rem', - padding: '3rem 0 1.6rem 0', - wordBreak: 'break-word', - whiteSpace: 'pre-wrap', - }, - buttonNext: { - padding: '0.9rem', - fontSize: '1.5rem', - fontWeight: 'bold', - outline: 'none', - }, -} as const; - -export const tour_styles: TJoyrideStyles = { - options: tourStyles.options, - tooltipTitle: tourStyles.tooltipTitle, - tooltipContent: tourStyles.tooltipContent, - buttonNext: tourStyles.buttonNext, -}; - -export const tour_styles_dark_mode: TJoyrideStyles = { - options: { - ...tourStyles.options, - backgroundColor: 'var(--general-section-3)', - arrowColor: 'var(--general-section-3)', - }, - tooltipTitle: tourStyles.tooltipTitle, - tooltipContent: tourStyles.tooltipContent, - buttonNext: tourStyles.buttonNext, -}; diff --git a/packages/appstore/src/helpers/account-helper.ts b/packages/appstore/src/helpers/account-helper.ts index 1f3c68f06231..9a939d6e6157 100644 --- a/packages/appstore/src/helpers/account-helper.ts +++ b/packages/appstore/src/helpers/account-helper.ts @@ -1,4 +1,5 @@ import { isCryptocurrency } from '@deriv/shared'; +import { localize } from '@deriv/translations'; type TAccountProps = { a_currency: string; @@ -35,3 +36,8 @@ export const getSortedAccountList = (account_list: TAccountProps, accounts: TAcc return 1; }); }; + +export const getPlatformToggleOptions = (is_eu_title: boolean) => [ + { text: is_eu_title ? localize('Multipliers') : localize('Options'), value: 'options' }, + { text: localize('CFDs'), value: 'cfd' }, +]; diff --git a/packages/appstore/src/helpers/index.ts b/packages/appstore/src/helpers/index.ts index afd9ee6328ca..f4ebebf671e3 100644 --- a/packages/appstore/src/helpers/index.ts +++ b/packages/appstore/src/helpers/index.ts @@ -1,2 +1,3 @@ export * from './account-helper'; export * from './total-assets-helper'; +export * from './trustpilot-helper'; diff --git a/packages/appstore/src/helpers/trustpilot-helper.ts b/packages/appstore/src/helpers/trustpilot-helper.ts new file mode 100644 index 000000000000..39fd501f462a --- /dev/null +++ b/packages/appstore/src/helpers/trustpilot-helper.ts @@ -0,0 +1,48 @@ +import { TTrustpilotWidgetData } from 'Types'; + +export const fetchTrustpilotData = async () => { + const defaultData = { + stars: 4.5, + trustScore: 4.5, + numberOfReviews: Number(47748).toLocaleString(), + }; + + try { + const appName = 'deriv.com'; + const apiKey = process.env.TRUSTPILOT_API_KEY; + + if (!appName || !apiKey) { + return { + ...defaultData, + error: 'Trustpilot app name or API key is missing', + }; + } + + const url = `https://api.trustpilot.com/v1/business-units/find?name=${appName}&apikey=${apiKey}`; + const response = await fetch(url); + + if (!response.ok) { + return { + ...defaultData, + error: `Network response was not ok: ${response.statusText}`, + }; + } + + const result = await response.json(); + + const trustpilotData: TTrustpilotWidgetData = { + stars: result.score?.stars || defaultData.stars, + trustScore: result.score?.trustScore || defaultData.trustScore, + numberOfReviews: result.numberOfReviews?.total?.toLocaleString() || defaultData.numberOfReviews, + }; + + return trustpilotData; + } catch (error) { + const trustpilotData: TTrustpilotWidgetData = { + ...defaultData, + error: `Something wrong: error = ${error}`, + }; + + return trustpilotData; + } +}; diff --git a/packages/appstore/src/modules/Page404/Components/Page404.tsx b/packages/appstore/src/modules/Page404/Components/Page404.tsx new file mode 100644 index 000000000000..448e3952ddc7 --- /dev/null +++ b/packages/appstore/src/modules/Page404/Components/Page404.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { PageError } from '@deriv/components'; +import { routes, getUrlBase } from '@deriv/shared'; +import { localize } from '@deriv/translations'; + +const Page404 = () => ( + +); + +export default Page404; diff --git a/packages/appstore/src/modules/Page404/Components/__tests__/Page404.spec.tsx b/packages/appstore/src/modules/Page404/Components/__tests__/Page404.spec.tsx new file mode 100644 index 000000000000..9d334c1850fb --- /dev/null +++ b/packages/appstore/src/modules/Page404/Components/__tests__/Page404.spec.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Router } from 'react-router'; +import { createBrowserHistory } from 'history'; +import { render, screen } from '@testing-library/react'; +import Page404 from '../Page404'; + +describe('Page404', () => { + const browser_history = createBrowserHistory(); + + it('should render Page404', () => { + render( + + + + ); + + expect(screen.getByText('We couldn’t find that page')).toBeInTheDocument(); + }); +}); diff --git a/packages/appstore/src/modules/Page404/index.ts b/packages/appstore/src/modules/Page404/index.ts new file mode 100644 index 000000000000..04dbb70feef6 --- /dev/null +++ b/packages/appstore/src/modules/Page404/index.ts @@ -0,0 +1,3 @@ +import Page404 from './Components/Page404'; + +export default Page404; diff --git a/packages/appstore/src/modules/onboarding/onboarding.tsx b/packages/appstore/src/modules/onboarding/onboarding.tsx index d939931ccfad..97bdc80c1a3f 100644 --- a/packages/appstore/src/modules/onboarding/onboarding.tsx +++ b/packages/appstore/src/modules/onboarding/onboarding.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import { useHistory } from 'react-router-dom'; -import { routes, ContentFlag } from '@deriv/shared'; +import { routes } from '@deriv/shared'; import { useStore, observer } from '@deriv/stores'; import OnboardingSkeleton from '../../components/loader'; import TradingPlatformIcon from 'Assets/svgs/trading-platform'; @@ -9,34 +9,15 @@ import './onboarding.scss'; const Onboarding = observer(() => { const history = useHistory(); - const { traders_hub, client, ui } = useStore(); + const { traders_hub, client } = useStore(); const { is_landing_company_loaded, is_logged_in, setPrevAccountType } = client; - const { content_flag, is_demo_low_risk, selectAccountType, toggleIsTourOpen } = traders_hub; - const { is_from_signup_account } = ui; + const { is_demo_low_risk, selectAccountType } = traders_hub; useEffect(() => { if (is_logged_in && is_landing_company_loaded) { history.push(routes.traders_hub); - if (is_from_signup_account && content_flag !== ContentFlag.EU_DEMO) { - toggleIsTourOpen(true); - } - - if (is_demo_low_risk) { - selectAccountType('real'); - setPrevAccountType('demo'); - } } - }, [ - is_logged_in, - is_landing_company_loaded, - is_from_signup_account, - content_flag, - is_demo_low_risk, - history, - toggleIsTourOpen, - selectAccountType, - setPrevAccountType, - ]); + }, [is_logged_in, is_landing_company_loaded, is_demo_low_risk, history, selectAccountType, setPrevAccountType]); if (is_logged_in && !is_landing_company_loaded) return ; diff --git a/packages/appstore/src/modules/tour-guide/tour-guide.tsx b/packages/appstore/src/modules/tour-guide/tour-guide.tsx deleted file mode 100644 index a2f951eb849c..000000000000 --- a/packages/appstore/src/modules/tour-guide/tour-guide.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React from 'react'; -import Joyride, { CallBackProps, STATUS, EVENTS, ACTIONS } from 'react-joyride'; -import { observer } from 'mobx-react-lite'; -import { - tour_step_config, - getTourStepLocale, - tour_step_config_high_risk, - getHighRiskTourStepLocale, -} from 'Constants/tour-steps-config'; -import { tour_styles, tour_styles_dark_mode } from 'Constants/tour-steps-styles'; -import { useStores } from 'Stores/index'; -import { ContentFlag } from '@deriv/shared'; -import { useTradersHubTracking } from 'Hooks/index'; -import { localize } from '@deriv/translations'; -import { SpanButton } from '@deriv/components'; - -const TourGuide = () => { - const { traders_hub, ui } = useStores(); - const { is_tour_open, toggleIsTourOpen, content_flag, setIsFirstTimeVisit } = traders_hub; - const { is_dark_mode_on, should_trigger_tour_guide, setShouldTriggerTourGuide } = ui; - - const [joyride_index, setJoyrideIndex] = React.useState(0); - const tour_step_locale = getTourStepLocale(); - const high_risk_tour_step_locale = getHighRiskTourStepLocale(); - const low_risk = content_flag === ContentFlag.LOW_RISK_CR_NON_EU || content_flag === ContentFlag.LOW_RISK_CR_EU; - - const { trackLastStep, trackStepForward, trackOnboardingRestart } = useTradersHubTracking(); - - const handleNextAction = (index: number) => { - trackStepForward(7); - setJoyrideIndex(index + 1); - }; - - const handlePrevAction = (index: number) => { - if (tour_step_config.length === joyride_index + 1) { - trackLastStep(); - trackOnboardingRestart(); - setIsFirstTimeVisit(false); - setJoyrideIndex(0); - } else { - setJoyrideIndex(index - 1); - } - }; - - const callbackHandle = (data: CallBackProps) => { - const { action, index, type, status } = data; - const finishedStatuses: string[] = [STATUS.FINISHED]; - const skipTypes: string[] = [EVENTS.STEP_AFTER, EVENTS.TARGET_NOT_FOUND]; - - if (finishedStatuses.includes(status)) { - toggleIsTourOpen(false); - setShouldTriggerTourGuide(false); - setJoyrideIndex(0); - return; - } - - if (!skipTypes.includes(type)) { - return; - } - - if (action === ACTIONS.NEXT) { - handleNextAction(index); - } else if (action === ACTIONS.PREV) { - handlePrevAction(index); - } - }; - - if (tour_step_config.length === joyride_index + 1) { - tour_step_locale.back = ; - } - - return ( - - ); -}; -export default observer(TourGuide); diff --git a/packages/appstore/src/modules/traders-hub-logged-out/index.tsx b/packages/appstore/src/modules/traders-hub-logged-out/index.tsx new file mode 100644 index 000000000000..d554a7a23180 --- /dev/null +++ b/packages/appstore/src/modules/traders-hub-logged-out/index.tsx @@ -0,0 +1,3 @@ +import TradersHubLoggedOut from './traders-hub-logged-out'; + +export default TradersHubLoggedOut; diff --git a/packages/appstore/src/modules/traders-hub-logged-out/traders-hub-logged-out.scss b/packages/appstore/src/modules/traders-hub-logged-out/traders-hub-logged-out.scss new file mode 100644 index 000000000000..3e2e64035769 --- /dev/null +++ b/packages/appstore/src/modules/traders-hub-logged-out/traders-hub-logged-out.scss @@ -0,0 +1,27 @@ +.traders-hub-logged-out { + max-width: 120rem; + display: flex; + flex-direction: column; + gap: 2.4rem; + margin: auto; + padding: 4rem; + + @include mobile { + padding: 2rem; + width: 100%; + } + + &__eu-user { + @include mobile { + min-height: 650px; + } + } + + &__mobile { + overflow: scroll !important; + + &::-webkit-scrollbar { + display: none; + } + } +} diff --git a/packages/appstore/src/modules/traders-hub-logged-out/traders-hub-logged-out.tsx b/packages/appstore/src/modules/traders-hub-logged-out/traders-hub-logged-out.tsx new file mode 100644 index 000000000000..0dcd18a66c5f --- /dev/null +++ b/packages/appstore/src/modules/traders-hub-logged-out/traders-hub-logged-out.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import classNames from 'classnames'; +import { observer, useStore } from '@deriv/stores'; +import { Div100vhContainer, DesktopWrapper, MobileWrapper, Loading, Text } from '@deriv/components'; +import { isEuCountry } from '@deriv/shared'; +import { Localize } from '@deriv/translations'; +import OrderedPlatformSections from 'Components/ordered-platform-sections'; +import GetStartedTradingBanner from 'Components/get-started-trading-banner'; +import TabsOrTitle from 'Components/tabs-or-title'; +import './traders-hub-logged-out.scss'; + +const TradersHubLoggedOut = observer(() => { + const { traders_hub, client, ui } = useStore(); + const { is_desktop } = ui; + const { clients_country } = client; + const { setTogglePlatformType, selectRegion, is_eu_user } = traders_hub; + + React.useEffect(() => { + if (clients_country) { + if (isEuCountry(clients_country)) { + setTogglePlatformType('cfd'); + selectRegion('EU'); + } else { + selectRegion('Non-EU'); + } + } + }, [clients_country, setTogglePlatformType]); + + if (!clients_country) return ; + + return ( + +
+ + + + + + + + + + + +
+
+ ); +}); + +export default TradersHubLoggedOut; diff --git a/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx b/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx index 105a7c9e647f..d5701b1439af 100644 --- a/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx +++ b/packages/appstore/src/modules/traders-hub/__tests__/index.spec.tsx @@ -7,7 +7,6 @@ jest.mock('Components/modals/modal-manager', () => jest.fn(() => 'mockedModalMan jest.mock('Components/main-title-bar', () => jest.fn(() => 'mockedMainTitleBar')); jest.mock('Components/cfds-listing', () => jest.fn(() => 'mockedCFDsListing')); jest.mock('Components/options-multipliers-listing', () => jest.fn(() => 'mocked')); -jest.mock('../../tour-guide/tour-guide', () => jest.fn(() => 'mocked')); describe('TradersHub', () => { const render_container = (mock_store_override = {}) => { diff --git a/packages/appstore/src/modules/traders-hub/index.tsx b/packages/appstore/src/modules/traders-hub/index.tsx index dc7ed80b13c3..badc04abaaed 100644 --- a/packages/appstore/src/modules/traders-hub/index.tsx +++ b/packages/appstore/src/modules/traders-hub/index.tsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { lazy, Suspense } from 'react'; import { DesktopWrapper, MobileWrapper, ButtonToggle, Div100vhContainer, Text } from '@deriv/components'; -import { isDesktop, routes, ContentFlag, checkServerMaintenance, startPerformanceEventTimer } from '@deriv/shared'; +import { isDesktop, routes, checkServerMaintenance, startPerformanceEventTimer } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; import { Localize, localize } from '@deriv/translations'; import CFDsListing from 'Components/cfds-listing'; @@ -8,10 +8,12 @@ import ModalManager from 'Components/modals/modal-manager'; import MainTitleBar from 'Components/main-title-bar'; import OptionsAndMultipliersListing from 'Components/options-multipliers-listing'; import ButtonToggleLoader from 'Components/pre-loader/button-toggle-loader'; +import { useContentFlag, useGrowthbookGetFeatureValue } from '@deriv/hooks'; import classNames from 'classnames'; -import TourGuide from '../tour-guide/tour-guide'; import './traders-hub.scss'; +const RealAccountCreationBanner = lazy(() => import('Components/real-account-creation-banner')); + const TradersHub = observer(() => { const { traders_hub, client, ui } = useStore(); const { @@ -30,40 +32,44 @@ const TradersHub = observer(() => { is_mt5_allowed, has_active_real_account, website_status, + has_any_real_account, + is_eu, } = client; - const { selected_platform_type, setTogglePlatformType, is_tour_open, content_flag, is_eu_user } = traders_hub; + + const { is_cr_demo, is_eu_demo, is_eu_real } = useContentFlag(); + const { selected_platform_type, setTogglePlatformType, is_eu_user } = traders_hub; const traders_hub_ref = React.useRef(null); const can_show_notify = (!is_switching && !is_logging_in && is_account_setting_loaded && is_landing_company_loaded) || checkServerMaintenance(website_status); - const [scrolled, setScrolled] = React.useState(false); - - const handleScroll = React.useCallback(() => { - const element = traders_hub_ref?.current; - if (element && is_tour_open) { - element.scrollIntoView({ behavior: 'smooth', block: 'start' }); - } - }, [is_tour_open]); + const [direct_to_real_account_creation] = useGrowthbookGetFeatureValue({ + featureFlag: 'direct-real-account-creation-flow', + defaultValue: false, + }); React.useEffect(() => { - if (is_eu_user) setTogglePlatformType('cfd'); - if ( - !has_active_real_account && - is_logged_in && - is_from_signup_account && - content_flag === ContentFlag.EU_DEMO - ) { - openRealAccountSignup('maltainvest'); - setIsFromSignupAccount(false); + if (is_eu_user) { + setTogglePlatformType('cfd'); + } + if (!has_active_real_account && is_from_signup_account && is_logged_in) { + if (direct_to_real_account_creation && is_cr_demo) { + openRealAccountSignup('svg'); + setIsFromSignupAccount(false); + } else if (is_eu_demo) { + openRealAccountSignup('maltainvest'); + setIsFromSignupAccount(false); + } } }, [ - content_flag, + is_cr_demo, + is_eu_demo, has_active_real_account, is_eu_user, is_from_signup_account, is_logged_in, + direct_to_real_account_creation, openRealAccountSignup, setIsFromSignupAccount, setTogglePlatformType, @@ -71,23 +77,20 @@ const TradersHub = observer(() => { React.useEffect(() => { if (is_eu_user) setTogglePlatformType('cfd'); - const timer = setTimeout(() => { - handleScroll(); - setTimeout(() => { - setScrolled(true); - }, 200); - }, 100); - return () => clearTimeout(timer); - }, [handleScroll, is_eu_user, is_tour_open, setTogglePlatformType]); + }, [is_eu_user, setTogglePlatformType]); React.useLayoutEffect(() => { startPerformanceEventTimer('option_multiplier_section_loading_time'); }, []); - const eu_title = content_flag === ContentFlag.EU_DEMO || content_flag === ContentFlag.EU_REAL || is_eu_user; + const [should_show_banner] = useGrowthbookGetFeatureValue({ + featureFlag: 'traders-hub-real-account-banner', + defaultValue: false, + }); + const eu_title = is_eu_demo || is_eu_real || is_eu_user; const getPlatformToggleOptions = () => [ - { text: eu_title ? localize('Multipliers') : localize('Options & Multipliers'), value: 'options' }, + { text: eu_title ? localize('Multipliers') : localize('Options'), value: 'options' }, { text: localize('CFDs'), value: 'cfd' }, ]; const platform_toggle_options = getPlatformToggleOptions(); @@ -143,6 +146,12 @@ const TradersHub = observer(() => { })} ref={traders_hub_ref} > + {should_show_banner && !has_any_real_account && !is_eu && is_landing_company_loaded && ( + }> + + + )} + {getOrderedPlatformSections(true)} @@ -170,13 +179,12 @@ const TradersHub = observer(() => { {getOrderedPlatformSections()} - {scrolled && }
{is_eu_user && (
- +
diff --git a/packages/appstore/src/public/images/traders-hub-logged-out-banner-bg-desktop.svg b/packages/appstore/src/public/images/traders-hub-logged-out-banner-bg-desktop.svg new file mode 100644 index 000000000000..59878d9f1a4b --- /dev/null +++ b/packages/appstore/src/public/images/traders-hub-logged-out-banner-bg-desktop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/appstore/src/public/images/traders-hub-logged-out-banner-bg-responsive.svg b/packages/appstore/src/public/images/traders-hub-logged-out-banner-bg-responsive.svg new file mode 100644 index 000000000000..77a6ac786e2b --- /dev/null +++ b/packages/appstore/src/public/images/traders-hub-logged-out-banner-bg-responsive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/appstore/src/types/common.types.ts b/packages/appstore/src/types/common.types.ts index 8514a1be40fd..143d21b43710 100644 --- a/packages/appstore/src/types/common.types.ts +++ b/packages/appstore/src/types/common.types.ts @@ -1,6 +1,5 @@ import { DetailsOfEachMT5Loginid } from '@deriv/api-types'; import { useStore } from '@deriv/stores'; - import { PlatformIcons } from 'Assets/svgs/trading-platform'; import { RegionAvailability } from 'Constants/platform-config'; @@ -27,7 +26,7 @@ export type TMarketType = 'financial' | 'synthetic' | 'all'; export type TVisibilityChecker = (platform: TPlatform) => boolean; export type TMissingRealAccount = { - onClickSignup: () => void; + onClickSignup: VoidFunction; }; export type TMt5StatusServerType = Record<'all' | 'platform' | 'server_number', number>; @@ -87,7 +86,7 @@ export type TTradingPlatformAvailableAccount = { }; export type TCFDAccountsProps = { - isDerivedVisible: TVisibilityChecker; + isStandardVisible: TVisibilityChecker; isFinancialVisible: TVisibilityChecker; has_cfd_account_error: (platform: TPlatform) => boolean; current_list: Record; @@ -95,10 +94,10 @@ export type TCFDAccountsProps = { has_real_account?: boolean; }; -export type TCFDPlatforms = 'Derived' | 'Financial' | 'Deriv X' | 'CFDs'; +export type TCFDPlatforms = 'Standard' | 'Financial' | 'Deriv X' | 'CFDs'; export type TStaticAccountProps = { - name: 'Derived' | 'Financial' | 'Deriv X' | 'CFDs'; + name: 'Standard' | 'Financial' | 'Deriv X' | 'CFDs'; description: string; is_visible: boolean; disabled: boolean; @@ -107,7 +106,7 @@ export type TStaticAccountProps = { }; export type TIconTypes = - | 'Derived' + | 'Standard' | 'Financial' | 'BinaryBot' | 'BinaryBotBlue' @@ -221,15 +220,14 @@ export type TWalletButton = { name: Parameters['traders_hub']['setWalletModalActiveTab']>[0]; text: string; icon: string; - action: () => void; + action: VoidFunction; }; export type TWalletSteps = { - handleBack: () => void; - handleClose: () => void; - handleNext: () => void; - is_disabled: boolean; - toggleCheckbox: () => void; + handleBack: VoidFunction; + handleClose: VoidFunction; + handleNext: VoidFunction; + is_migrating: boolean; upgradeToWallets: (value: boolean) => void; }; @@ -238,3 +236,10 @@ export type TRealWalletsUpgradeSteps = { current_step: number; }; }; + +export type TTrustpilotWidgetData = { + stars: number; + trustScore: number; + numberOfReviews: string; + error?: string; +}; diff --git a/packages/appstore/tsconfig.json b/packages/appstore/tsconfig.json index ad130b89d98e..411e481ddc02 100644 --- a/packages/appstore/tsconfig.json +++ b/packages/appstore/tsconfig.json @@ -15,6 +15,7 @@ "Stores": ["src/stores/index"], "Types": ["src/types"], "Utils": ["src/utils"], + "Helpers": ["src/helpers"], "@deriv/*": ["../*/src"] } }, diff --git a/packages/appstore/webpack.config.js b/packages/appstore/webpack.config.js index 73609e9186ab..19b2996e2d65 100644 --- a/packages/appstore/webpack.config.js +++ b/packages/appstore/webpack.config.js @@ -61,6 +61,7 @@ module.exports = function (env) { Types: path.resolve(__dirname, 'src/types'), Utils: path.resolve(__dirname, 'src/utils'), Hooks: path.resolve(__dirname, 'src/hooks'), + Helpers: path.resolve(__dirname, 'src/helpers'), }, extensions: ['.ts', '.tsx', '.js'], }, diff --git a/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/trade_definition_restartbuysell.js b/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/trade_definition_restartbuysell.js index f0b80c851756..0e0c36d4bd86 100755 --- a/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/trade_definition_restartbuysell.js +++ b/packages/bot-skeleton/src/scratch/blocks/Binary/Trade Definition/trade_definition_restartbuysell.js @@ -22,6 +22,12 @@ Blockly.Blocks.trade_definition_restartbuysell = { this.setMovable(false); this.setDeletable(false); + this.setOnChange(() => { + const next_block = this?.getNextBlock(); + if (next_block?.type !== 'trade_definition_restartonerror') { + next_block?.unplug(true); + } + }); }, onchange(/* event */) { if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { diff --git a/packages/bot-web-ui/package.json b/packages/bot-web-ui/package.json index 12bb24a4ea11..1526724d6dc3 100644 --- a/packages/bot-web-ui/package.json +++ b/packages/bot-web-ui/package.json @@ -73,10 +73,10 @@ "dependencies": { "@datadog/browser-logs": "^5.11.0", "@deriv/api": "^1.0.0", - "@deriv/api-types": "^1.0.172", + "@deriv/api-types": "1.0.172", "@deriv/bot-skeleton": "^1.0.0", "@deriv/components": "^1.0.0", - "@deriv/deriv-charts": "^2.1.15", + "@deriv/deriv-charts": "^2.1.19", "@deriv/shared": "^1.0.0", "@deriv/stores": "^1.0.0", "@deriv/translations": "^1.0.0", diff --git a/packages/bot-web-ui/src/app/app-content.jsx b/packages/bot-web-ui/src/app/app-content.jsx index 7c9a94ec58a3..c9c96e7eddad 100644 --- a/packages/bot-web-ui/src/app/app-content.jsx +++ b/packages/bot-web-ui/src/app/app-content.jsx @@ -99,10 +99,12 @@ const AppContent = observer(() => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // use is_landing_company_loaded to know got details of accounts to identify should show an error or not - if (client.is_landing_company_loaded) { - changeActiveSymbolLoadingState(); - } + React.useEffect(() => { + if (client.is_logged_in && client.is_landing_company_loaded) { + changeActiveSymbolLoadingState(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [client.is_landing_company_loaded]); React.useEffect(() => { const onDisconnectFromNetwork = () => { diff --git a/packages/bot-web-ui/src/components/bot-notification/bot-notification-utils.ts b/packages/bot-web-ui/src/components/bot-notification/bot-notification-utils.ts index 4fea10f2bf16..50c5b487265a 100644 --- a/packages/bot-web-ui/src/components/bot-notification/bot-notification-utils.ts +++ b/packages/bot-web-ui/src/components/bot-notification/bot-notification-utils.ts @@ -1,6 +1,6 @@ import { toast, ToastPosition, TypeOptions } from 'react-toastify'; -import { localize } from '@deriv/translations'; import { isDbotRTL } from '@deriv/bot-skeleton/src/utils/workspace'; +import { localize } from '@deriv/translations'; const getToastPosition = () => { const is_RTL = isDbotRTL(); diff --git a/packages/bot-web-ui/src/components/bot-notification/bot-notification.tsx b/packages/bot-web-ui/src/components/bot-notification/bot-notification.tsx index db54658679ea..4c56742f78c6 100644 --- a/packages/bot-web-ui/src/components/bot-notification/bot-notification.tsx +++ b/packages/bot-web-ui/src/components/bot-notification/bot-notification.tsx @@ -3,6 +3,20 @@ import { toast } from 'react-toastify'; import { notification_style, TAction, TNotificationContent, TNotificationStyle } from './bot-notification-utils'; export const NotificationContent: React.FC = ({ message, primary_action, closeToast }) => { + React.useEffect(() => { + const handleToastVisibility = () => { + if (document.visibilityState === 'hidden') { + toast.dismiss(); + } + }; + + document.addEventListener('visibilitychange', handleToastVisibility); + + return () => { + document.removeEventListener('visibilitychange', handleToastVisibility); + }; + }, []); + return (
{message}
diff --git a/packages/bot-web-ui/src/components/download/download.tsx b/packages/bot-web-ui/src/components/download/download.tsx index 543e21063032..d3d36cabd3c8 100644 --- a/packages/bot-web-ui/src/components/download/download.tsx +++ b/packages/bot-web-ui/src/components/download/download.tsx @@ -96,9 +96,6 @@ const Download = observer(({ tab }: TDownloadProps) => { id='download-button' is_disabled={disabled} className='download__button' - icon={ - - } text={localize('Download')} onClick={clickFunction} secondary 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 22a9ef8b03be..4c3eec1b54d9 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 @@ -102,6 +102,14 @@ justify-content: space-between; flex-direction: column; overflow: hidden !important; + + .dc-tabs { + &__item { + overflow: hidden; + text-overflow: ellipsis; + padding: 0 0.8rem; + } + } } &__buttons { display: inline-flex !important; diff --git a/packages/bot-web-ui/src/components/transactions/transactions.scss b/packages/bot-web-ui/src/components/transactions/transactions.scss index 7de163f547a2..62b846b9035b 100644 --- a/packages/bot-web-ui/src/components/transactions/transactions.scss +++ b/packages/bot-web-ui/src/components/transactions/transactions.scss @@ -77,7 +77,7 @@ text-align: start; } @if $item == 'profit' { - text-align: end; + text-align: start; } } } diff --git a/packages/bot-web-ui/src/components/transactions/transactions.tsx b/packages/bot-web-ui/src/components/transactions/transactions.tsx index 8b24958fabe8..b3882ef26913 100644 --- a/packages/bot-web-ui/src/components/transactions/transactions.tsx +++ b/packages/bot-web-ui/src/components/transactions/transactions.tsx @@ -115,7 +115,6 @@ const Transactions = observer(({ is_drawer_open }: TTransactions) => { toggleTransactionDetailsModal(true); }} secondary - icon={} />
diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/__tests__/utils.spec.ts b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/__tests__/utils.spec.ts new file mode 100644 index 000000000000..3855ee0683d3 --- /dev/null +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/__tests__/utils.spec.ts @@ -0,0 +1,62 @@ +import { TFormStrategy } from '../constants'; +import { getTradeParameterData } from '../utils'; + +jest.mock('@deriv/bot-skeleton/src/scratch/blockly', () => jest.fn()); +jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => jest.fn()); +jest.mock('@deriv/bot-skeleton/src/scratch/hooks/block_svg', () => jest.fn()); + +describe('utils', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const form_strategy = { + form_values: { + symbol: 'BTCUSD', + tradetype: 'CALL', + type: 'MULT', + stake: '100', + }, + } as TFormStrategy; + + it('should return form values when no stored text is available', () => { + const result = getTradeParameterData(form_strategy); + + expect(result).toEqual({ + asset_type: 'BTCUSD', + trade_type: 'CALL', + purchase_condition: 'MULT', + initial_stake: '100', + }); + }); + + it('should return stored text when available', () => { + // eslint-disable-next-line no-proto + jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValue( + '{"symbol":"ETHUSD","tradetype":"PUT","type":"DIGIT","stake":200}' + ); + + const result = getTradeParameterData(form_strategy); + + expect(result).toEqual({ + asset_type: 'ETHUSD', + trade_type: 'PUT', + purchase_condition: 'DIGIT', + initial_stake: 200, + }); + }); + + it('should use form value when stored text is not present for a specific field', () => { + // eslint-disable-next-line no-proto + jest.spyOn(localStorage.__proto__, 'getItem').mockReturnValue('{"symbol":"ETHUSD"}'); + + const result = getTradeParameterData(form_strategy); + + expect(result).toEqual({ + asset_type: 'ETHUSD', + trade_type: 'CALL', + purchase_condition: 'MULT', + initial_stake: '100', + }); + }); +}); diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/constants.ts b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/constants.ts index d4d365e67b85..c2e2d9dd5c81 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/constants.ts +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/constants.ts @@ -6,6 +6,7 @@ export const STORED_ITEM_NOT_FOUND = 'No results found'; export enum ACTION { OPEN = 'open', CLOSE = 'close', + RUN_BOT = 'run_bot', RUN_QUICK_STRATEGY = 'run_quick_strategy', EDIT_QUICK_STRATEGY = 'edit_quick_strategy', SELECT_QUICK_STRATEGY_GUIDE = 'select_quick_strategy_guide', diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/rudderstack-quick-strategy.ts b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/rudderstack-quick-strategy.ts index 3df4466ff561..196a26190278 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/rudderstack-quick-strategy.ts +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/rudderstack-quick-strategy.ts @@ -73,14 +73,14 @@ export const rudderStackSendSelectQsStrategyGuideEvent = ({ selected_strategy }: Analytics.trackEvent('ce_bot_form', { action: ACTION.SELECT_QUICK_STRATEGY_GUIDE, form_name, - subform_name: 'quick_strategy', + subpage_name: 'tutorials', strategy_name: getRsStrategyType(selected_strategy), }); }; export const rudderStackSendRunBotEvent = () => { Analytics.trackEvent('ce_bot_form', { - action: ACTION.SELECT_QUICK_STRATEGY_GUIDE, + action: ACTION.RUN_BOT, form_name, subpage_name: getSubpageName(), }); diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/utils.ts b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/utils.ts index d4c4b0c220c7..a0d6f461dba9 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/utils.ts +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/analytics/utils.ts @@ -19,16 +19,14 @@ export const getQsActiveTabString = (tab: string) => (tab === 'TRADE_PARAMETERS' export const getSubpageName = () => { const pathname = window.location.hash; - switch (pathname) { - case 'dashboard': - return 'dashboard'; - case 'charts': - return 'charts'; - case 'tutorials': - return 'tutorials'; - default: - return 'bot_builder'; + if (pathname.includes('dashboard')) { + return 'dashboard'; + } else if (pathname.includes('chart')) { + return 'chart'; + } else if (pathname.includes('tutorial')) { + return 'tutorial'; } + return 'bot_builder'; }; export const getTradeParameterData = ({ form_values }: TFormStrategy) => { diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/descriptions/__tests__/strategy-description.spec.tsx b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/descriptions/__tests__/strategy-description.spec.tsx new file mode 100644 index 000000000000..fcfc0c75c9d4 --- /dev/null +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/descriptions/__tests__/strategy-description.spec.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { mockStore, StoreProvider } from '@deriv/stores'; +import { render, screen } from '@testing-library/react'; +import { mock_ws } from 'Utils/mock'; +import RootStore from 'Stores/root-store'; +import { DBotStoreProvider, mockDBotStore } from 'Stores/useDBotStore'; +import StrategyDescription from '../strategy-description'; + +jest.mock('@deriv/bot-skeleton/src/scratch/dbot', () => jest.fn()); + +window.Blockly = { + Xml: { + textToDom: jest.fn(), + domToText: jest.fn(), + }, +}; + +const mock_strategy_description_props = { + item: { + type: 'text', + content: ['test'], + src: 'test', + alt: 'test', + }, + font_size: 's', +}; + +describe('', () => { + let wrapper: ({ children }: { children: JSX.Element }) => JSX.Element, mock_DBot_store: RootStore | undefined; + + beforeEach(() => { + const mock_store = mockStore({}); + mock_DBot_store = mockDBotStore(mock_store, mock_ws); + mock_DBot_store?.quick_strategy.setFormVisibility(true); + + wrapper = ({ children }: { children: JSX.Element }) => ( + + + {children} + + + ); + }); + + it('renders the StrategyDescription component', () => { + render(, { + wrapper, + }); + + expect(screen.getByText('test')).toBeInTheDocument(); + }); + + it('renders the StrategyDescription component when item type is italic', () => { + mock_strategy_description_props.item.type = 'text_italic'; + render(, { + wrapper, + }); + + expect(screen.getByText('test')).toBeInTheDocument(); + }); + + it('renders the StrategyDescription component when item type is media', () => { + mock_strategy_description_props.item.type = 'media'; + render(, { + wrapper, + }); + + expect(screen.getByAltText('test')).toBeInTheDocument(); + }); +}); diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/desktop-form-wrapper.tsx b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/desktop-form-wrapper.tsx index 210afcbd8a39..eb53b8a46e35 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/desktop-form-wrapper.tsx +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/desktop-form-wrapper.tsx @@ -10,7 +10,6 @@ import { rudderStackSendQsEditStrategyEvent, rudderStackSendQsRunStrategyEvent, rudderStackSendQsSelectedTabEvent, - rudderStackSendSelectQsStrategyGuideEvent, } from '../analytics/rudderstack-quick-strategy'; import { getQsActiveTabString } from '../analytics/utils'; import { STRATEGIES } from '../config'; @@ -41,7 +40,6 @@ const FormWrapper: React.FC = observer(({ children, onClick const onChangeStrategy = (strategy: string) => { setSelectedStrategy(strategy); setActiveTab('TRADE_PARAMETERS'); - rudderStackSendSelectQsStrategyGuideEvent({ selected_strategy }); }; const handleTabChange = (tab: string) => { diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/mobile-form-wrapper.tsx b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/mobile-form-wrapper.tsx index b28b9db3fccc..cdf3725102ec 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/mobile-form-wrapper.tsx +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/form-wrappers/mobile-form-wrapper.tsx @@ -8,7 +8,6 @@ import { useDBotStore } from 'Stores/useDBotStore'; import { rudderStackSendQsRunStrategyEvent, rudderStackSendQsSelectedTabEvent, - rudderStackSendSelectQsStrategyGuideEvent, } from '../analytics/rudderstack-quick-strategy'; import { getQsActiveTabString } from '../analytics/utils'; import { STRATEGIES } from '../config'; @@ -38,9 +37,6 @@ const MobileFormWrapper: React.FC = observer(({ children, ac const onChangeStrategy = (strategy: string) => { setSelectedStrategy(strategy); setActiveTab('TRADE_PARAMETERS'); - rudderStackSendSelectQsStrategyGuideEvent({ - selected_strategy, - }); }; const handleTabChange = (tab: string) => { diff --git a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/quick-strategy.scss b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/quick-strategy.scss index 7b497736bdb1..fd3bf7f149a3 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/quick-strategy.scss +++ b/packages/bot-web-ui/src/pages/bot-builder/quick-strategy/quick-strategy.scss @@ -357,8 +357,8 @@ flex-direction: row-reverse; @include mobile() { - top: -2.8rem; - right: 4rem; + top: -2.6rem; + right: 5rem; } .dc-text { @@ -537,11 +537,11 @@ &__trailing-icon { @extend .seddle-actions; - right: 0.3rem; + right: 1rem; color: var(--text-general); @include mobile() { - right: 0.2rem; + right: 1.6rem; } &:hover { diff --git a/packages/bot-web-ui/src/pages/bot-builder/toolbar/workspace-group.tsx b/packages/bot-web-ui/src/pages/bot-builder/toolbar/workspace-group.tsx index 266a6c4dacf7..f40c0c77fef9 100644 --- a/packages/bot-web-ui/src/pages/bot-builder/toolbar/workspace-group.tsx +++ b/packages/bot-web-ui/src/pages/bot-builder/toolbar/workspace-group.tsx @@ -57,7 +57,7 @@ const WorkspaceGroup = observer(() => { setTradingViewModalVisibility()} diff --git a/packages/bot-web-ui/src/pages/dashboard/dashboard.scss b/packages/bot-web-ui/src/pages/dashboard/dashboard.scss index 381208eb5e4a..a45eeced0788 100644 --- a/packages/bot-web-ui/src/pages/dashboard/dashboard.scss +++ b/packages/bot-web-ui/src/pages/dashboard/dashboard.scss @@ -385,9 +385,6 @@ text-align: center; } } - &:nth-last-child(2) { - padding-inline: 0; - } } &__images { diff --git a/packages/bot-web-ui/src/pages/tutorials/quick-strategy-content/quick-strategy-guides-details.tsx b/packages/bot-web-ui/src/pages/tutorials/quick-strategy-content/quick-strategy-guides-details.tsx index 7583bb030e88..ff43197a5167 100644 --- a/packages/bot-web-ui/src/pages/tutorials/quick-strategy-content/quick-strategy-guides-details.tsx +++ b/packages/bot-web-ui/src/pages/tutorials/quick-strategy-content/quick-strategy-guides-details.tsx @@ -3,6 +3,7 @@ import { isDbotRTL } from '@deriv/bot-skeleton/src/utils/workspace'; import { Icon, Text } from '@deriv/components'; import { observer, useStore } from '@deriv/stores'; import { Localize } from '@deriv/translations'; +import { rudderStackSendSelectQsStrategyGuideEvent } from '../../bot-builder/quick-strategy/analytics/rudderstack-quick-strategy'; import { STRATEGIES } from '../../bot-builder/quick-strategy/config'; import StrategyTabContent from '../../bot-builder/quick-strategy/form-wrappers/strategy-tab-content'; @@ -32,7 +33,10 @@ const QuickStrategyGuidesDetail = observer(
setTutorialSelectedStrategy(qs_name)} + onClick={() => { + setTutorialSelectedStrategy(qs_name); + rudderStackSendSelectQsStrategyGuideEvent({ selected_strategy: qs_name }); + }} tabIndex={index} data-testid={'dt_quick_strategy_guides_details'} onKeyDown={(e: KeyboardEvent) => { diff --git a/packages/bot-web-ui/src/stores/app-store.ts b/packages/bot-web-ui/src/stores/app-store.ts index 0cec88843b7a..62357cd146fd 100644 --- a/packages/bot-web-ui/src/stores/app-store.ts +++ b/packages/bot-web-ui/src/stores/app-store.ts @@ -23,6 +23,15 @@ export default class AppStore { makeObservable(this, { onMount: action, onUnmount: action, + onBeforeUnload: action, + registerReloadOnLanguageChange: action, + registerCurrencyReaction: action, + registerOnAccountSwitch: action, + registerLandingCompanyChangeReaction: action, + registerResidenceChangeReaction: action, + setDBotEngineStores: action, + onClickOutsideBlockly: action, + showDigitalOptionsMaltainvestError: action, }); this.root_store = root_store; diff --git a/packages/cashier-v2/package.json b/packages/cashier-v2/package.json index 510a761a18e5..57a54eb8227c 100644 --- a/packages/cashier-v2/package.json +++ b/packages/cashier-v2/package.json @@ -15,12 +15,12 @@ }, "dependencies": { "@deriv-com/ui": "^1.14.5", - "@deriv-com/utils": "^0.0.20", + "@deriv-com/utils": "^0.0.24", "@deriv/api-v2": "^1.0.0", "@deriv/integration": "^1.0.0", - "@deriv/quill-icons": "^1.22.4", + "@deriv/quill-icons": "^1.22.10", "@deriv/utils": "^1.0.0", - "clsx": "^2.0.0", + "clsx": "^2.1.1", "formik": "^2.1.4", "i18next": "^22.4.6", "lodash.debounce": "^4.0.8", @@ -59,7 +59,7 @@ "eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-sonarjs": "^0.23.0", - "eslint-plugin-sort-destructure-keys": "^1.5.0", + "eslint-plugin-sort-destructure-keys": "1.5.0", "eslint-plugin-typescript-sort-keys": "^2.3.0", "postcss": "^8.4.24", "postcss-loader": "^6.2.1", diff --git a/packages/cashier-v2/src/types.ts b/packages/cashier-v2/src/types.ts index e5a1c46376e7..2cd3d189b717 100644 --- a/packages/cashier-v2/src/types.ts +++ b/packages/cashier-v2/src/types.ts @@ -34,6 +34,7 @@ declare module 'react-router-dom' { state: Record; }; push: (path: TRouteTypes.TRoutes) => void; + goBack: () => void; }; export function useRouteMatch(path: TRouteTypes.TRoutes): boolean; diff --git a/packages/cashier/package.json b/packages/cashier/package.json index 60fa1757b9d2..a195dd537f21 100644 --- a/packages/cashier/package.json +++ b/packages/cashier/package.json @@ -38,7 +38,7 @@ }, "dependencies": { "@deriv/api": "^1.0.0", - "@deriv/api-types": "^1.0.172", + "@deriv/api-types": "1.0.172", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", "@deriv/hooks": "^1.0.0", diff --git a/packages/cashier/src/assets/svgs/trading-platform/ic-appstore-standard.svg b/packages/cashier/src/assets/svgs/trading-platform/ic-appstore-standard.svg new file mode 100644 index 000000000000..624a94855937 --- /dev/null +++ b/packages/cashier/src/assets/svgs/trading-platform/ic-appstore-standard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/cashier/src/assets/svgs/trading-platform/index.tsx b/packages/cashier/src/assets/svgs/trading-platform/index.tsx index 1eb92893a296..3eac5ef637e3 100644 --- a/packages/cashier/src/assets/svgs/trading-platform/index.tsx +++ b/packages/cashier/src/assets/svgs/trading-platform/index.tsx @@ -3,6 +3,7 @@ import Derived from './ic-appstore-derived.svg'; import Financial from './ic-appstore-financial.svg'; import Options from './ic-appstore-options.svg'; import CFDs from './ic-appstore-cfds.svg'; +import Standard from './ic-appstore-standard.svg'; import SwapFree from './ic-appstore-swap-free.svg'; export interface IconProps { @@ -17,6 +18,7 @@ export const PlatformIcons = { Financial, Options, CFDs, + Standard, SwapFree, }; diff --git a/packages/cashier/src/containers/routes/binary-routes.tsx b/packages/cashier/src/containers/routes/binary-routes.tsx index b9aaad547a30..a72047a6c75b 100644 --- a/packages/cashier/src/containers/routes/binary-routes.tsx +++ b/packages/cashier/src/containers/routes/binary-routes.tsx @@ -1,10 +1,12 @@ import React, { useEffect } from 'react'; import { Switch } from 'react-router-dom'; +import { useFeatureFlags } from '@deriv/hooks'; +import { routes } from '@deriv/shared'; +import { useStore } from '@deriv/stores'; import { Localize, localize } from '@deriv/translations'; +import Page404 from 'Components/page-404'; import getRoutesConfig from 'Constants/routes-config'; import RouteWithSubRoutes from './route-with-sub-routes'; -import { useFeatureFlags } from '@deriv/hooks'; -import { routes } from '@deriv/shared'; type TBinaryRoutesProps = { is_logged_in: boolean; @@ -30,6 +32,8 @@ const cashierV2RoutesConfig = { const BinaryRoutes = (props: TBinaryRoutesProps) => { const { is_p2p_v2_enabled } = useFeatureFlags(); const [routesConfig, setRoutesConfig] = React.useState(getRoutesConfig()); + const { client } = useStore(); + const { has_wallet } = client; useEffect(() => { const isRouteAdded = (routePath: string) => routesConfig[0].routes?.some(route => route.path === routePath); @@ -43,6 +47,8 @@ const BinaryRoutes = (props: TBinaryRoutesProps) => { } }, [is_p2p_v2_enabled, routesConfig]); + if (has_wallet) return ; + return ( }> diff --git a/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx b/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx index db44d9e7dd04..f1a91ee8e34d 100644 --- a/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx +++ b/packages/cashier/src/pages/account-transfer/account-transfer-form/__tests__/account-transfer-form.spec.tsx @@ -352,7 +352,7 @@ describe('', () => { text: 'USD', currency: 'USD', value: 'MTR40013177', - platform_icon: 'Derived', + platform_icon: 'Standard', is_crypto: false, is_mt: true, is_dxtrade: false, @@ -408,7 +408,7 @@ describe('', () => { .mockReturnValue(100.0); renderAccountTransferForm(); - expect(screen.getByTestId('Derived')).toBeInTheDocument(); + expect(screen.getByTestId('Standard')).toBeInTheDocument(); }); it('should check for DerivX icon when DerivX is selected in from_dropdown', () => { diff --git a/packages/cashier/src/types/account.types.ts b/packages/cashier/src/types/account.types.ts index fcede0f73fe7..349350beed58 100644 --- a/packages/cashier/src/types/account.types.ts +++ b/packages/cashier/src/types/account.types.ts @@ -3,7 +3,7 @@ /* -------------------------------------------------------------------------- */ import { DetailsOfEachMT5Loginid, TransferBetweenAccountsResponse } from '@deriv/api-types'; -export type TPlatformIcon = 'Derived' | 'Financial' | 'Options' | 'CFDs'; +export type TPlatformIcon = 'Standard' | 'Financial' | 'Options' | 'CFDs'; export type TAccount = { balance?: string | number; diff --git a/packages/cfd/package.json b/packages/cfd/package.json index 65a945beea9a..d94291355ba4 100644 --- a/packages/cfd/package.json +++ b/packages/cfd/package.json @@ -84,11 +84,11 @@ "webpack-node-externals": "^2.5.2" }, "dependencies": { - "@deriv-com/analytics": "1.4.13", - "@deriv-com/utils": "^0.0.20", + "@deriv-com/analytics": "1.5.9", + "@deriv-com/utils": "^0.0.24", "@deriv/account": "^1.0.0", "@deriv/api": "^1.0.0", - "@deriv/api-types": "^1.0.172", + "@deriv/api-types": "1.0.172", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", "@deriv/hooks": "^1.0.0", diff --git a/packages/cfd/src/Assets/banner/migrate-card-dark.svg b/packages/cfd/src/Assets/banner/migrate-card-dark.svg index 5c8267e9e3c1..6da8a2a21781 100644 --- a/packages/cfd/src/Assets/banner/migrate-card-dark.svg +++ b/packages/cfd/src/Assets/banner/migrate-card-dark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/cfd/src/Assets/banner/migrate-card.svg b/packages/cfd/src/Assets/banner/migrate-card.svg index 5b57cb231382..676bc0ccb252 100644 --- a/packages/cfd/src/Assets/banner/migrate-card.svg +++ b/packages/cfd/src/Assets/banner/migrate-card.svg @@ -1,92 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/packages/cfd/src/Assets/svgs/trading-platform/ic-appstore-standard.svg b/packages/cfd/src/Assets/svgs/trading-platform/ic-appstore-standard.svg new file mode 100644 index 000000000000..624a94855937 --- /dev/null +++ b/packages/cfd/src/Assets/svgs/trading-platform/ic-appstore-standard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/cfd/src/Assets/svgs/trading-platform/index.tsx b/packages/cfd/src/Assets/svgs/trading-platform/index.tsx index 9e645b94b5aa..164006c8e3bb 100644 --- a/packages/cfd/src/Assets/svgs/trading-platform/index.tsx +++ b/packages/cfd/src/Assets/svgs/trading-platform/index.tsx @@ -6,6 +6,7 @@ import DerivX from './ic-appstore-deriv-x.svg'; import Derived from './ic-appstore-derived.svg'; import Financial from './ic-appstore-financial.svg'; import SwapFree from './ic-appstore-swap-free.svg'; +import Standard from './ic-appstore-standard.svg'; export interface IconProps { icon: T; @@ -21,6 +22,7 @@ export const PlatformIcons = { CTrader, SwapFree, DerivX, + Standard, }; const TradingPlatformIcon = ({ icon, className, size, onClick }: IconProps) => { diff --git a/packages/cfd/src/Components/__tests__/cfd-account-card.spec.tsx b/packages/cfd/src/Components/__tests__/cfd-account-card.spec.tsx index a5f4d5480eea..38218b08f159 100644 --- a/packages/cfd/src/Components/__tests__/cfd-account-card.spec.tsx +++ b/packages/cfd/src/Components/__tests__/cfd-account-card.spec.tsx @@ -127,7 +127,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[mt5_acc]} />, renderOptions @@ -186,7 +186,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[mt5_acc]} />, renderOptions @@ -265,7 +265,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[]} platform='mt5' />, @@ -285,7 +285,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[]} platform='mt5' />, @@ -305,7 +305,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[]} platform='mt5' />, @@ -326,7 +326,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' platform='mt5' button_label='Add real account' existing_accounts_data={null} @@ -349,7 +349,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' platform='mt5' button_label='Add demo account' existing_accounts_data={null} @@ -372,7 +372,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' platform='mt5' existing_accounts_data={[mt5_labuan_acc]} />, @@ -392,7 +392,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' platform='dxtrade' />, renderOptions @@ -412,7 +412,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[derivx_acc]} platform='dxtrade' />, @@ -434,7 +434,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={[derivx_acc]} platform='dxtrade' />, @@ -461,7 +461,7 @@ describe('CFDAccountCard', () => { {...props} type={type} descriptor={synthetic_descriptor} - title='Derived' + title='Standard' existing_accounts_data={null} platform='dxtrade' is_logged_in={false} diff --git a/packages/cfd/src/Components/migration-success-modal/migration-success-modal.tsx b/packages/cfd/src/Components/migration-success-modal/migration-success-modal.tsx index 9ecced93bc70..fec12904f013 100644 --- a/packages/cfd/src/Components/migration-success-modal/migration-success-modal.tsx +++ b/packages/cfd/src/Components/migration-success-modal/migration-success-modal.tsx @@ -63,9 +63,9 @@ const MigrationSuccessModal = observer(({ is_open, closeModal }: TMigrationSucce } else { switch (to_acc) { case Jurisdiction.BVI: - return 'IcMt5DerivedFinancialBvi'; + return 'IcMt5StandardFinancialBvi'; case Jurisdiction.VANUATU: - return 'IcMt5DerivedFinancialVanuatu'; + return 'IcMt5StandardFinancialVanuatu'; default: return ''; } diff --git a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-bvi-contents.ts b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-bvi-contents.ts index b58f262235dc..f510afb2bc28 100644 --- a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-bvi-contents.ts +++ b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-bvi-contents.ts @@ -10,10 +10,12 @@ export const getJurisdictionBviContents = ({ { key: 'assets', title: localize('Assets'), - description: localize('Synthetic indices, basket indices, and derived FX'), + description: localize( + 'Forex (standard), stock indices, commodities, cryptocurrencies, synthetic indices, basket indices and derived FX' + ), title_indicators: { type: 'displayText', - display_text: localize('40+'), + display_text: localize('210+'), display_text_skin_color: 'red-darker', }, }, @@ -32,7 +34,7 @@ export const getJurisdictionBviContents = ({ title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { @@ -85,7 +87,7 @@ export const getJurisdictionBviContents = ({ title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { diff --git a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-labuan-contents.ts b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-labuan-contents.ts index e3e19087f205..9ef14eaf7523 100644 --- a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-labuan-contents.ts +++ b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-labuan-contents.ts @@ -18,7 +18,7 @@ export const getJurisdictionLabuanContents = (): TJurisdictionCardItems => ({ title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { @@ -62,7 +62,7 @@ export const getJurisdictionLabuanContents = (): TJurisdictionCardItems => ({ title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { diff --git a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-svg-contents.ts b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-svg-contents.ts index 6b67227d3617..6c2f261a29eb 100644 --- a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-svg-contents.ts +++ b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-svg-contents.ts @@ -10,10 +10,12 @@ export const getJurisdictionSvgContents = ({ { key: 'assets', title: localize('Assets'), - description: localize('Synthetic indices, basket indices, and derived FX'), + description: localize( + 'Forex (standard), stock indices, commodities, cryptocurrencies, synthetic indices, basket indices and derived FX' + ), title_indicators: { type: 'displayText', - display_text: localize('40+'), + display_text: localize('210+'), display_text_skin_color: 'red-darker', }, }, diff --git a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-vanuatu-contents.ts b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-vanuatu-contents.ts index 584130eccc41..938351c2cd3b 100644 --- a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-vanuatu-contents.ts +++ b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction-vanuatu-contents.ts @@ -10,10 +10,12 @@ export const getJurisdictionVanuatuContents = ({ { key: 'assets', title: localize('Assets'), - description: localize('Synthetic indices, basket indices, and derived FX'), + description: localize( + 'Forex (standard), stock indices, commodities, cryptocurrencies, synthetic indices, basket indices and derived FX' + ), title_indicators: { type: 'displayText', - display_text: localize('40+'), + display_text: localize('210+'), display_text_skin_color: 'red-darker', }, }, @@ -32,7 +34,7 @@ export const getJurisdictionVanuatuContents = ({ title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { @@ -85,7 +87,7 @@ export const getJurisdictionVanuatuContents = ({ title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { diff --git a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction_maltainvest_contents.ts b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction_maltainvest_contents.ts index 08d993b3e7cf..38c0e38e6810 100644 --- a/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction_maltainvest_contents.ts +++ b/packages/cfd/src/Constants/jurisdiction-contents/jurisdiction_maltainvest_contents.ts @@ -38,7 +38,7 @@ export const getJurisdictionMaltainvestContents = (): TJurisdictionCardItems => title: localize('Verifications'), clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { @@ -82,7 +82,7 @@ export const getJurisdictionMaltainvestContents = (): TJurisdictionCardItems => title_indicators: { type: 'displayIcons' }, clickable_description: [ { type: 'link', text: localize('Learn more') }, - { type: 'text', text: localize('about verifications needed.') }, + { type: 'text', text: localize('about required verifications.') }, ], }, { diff --git a/packages/cfd/src/Containers/__tests__/cfd-password-manager-modal.spec.js b/packages/cfd/src/Containers/__tests__/cfd-password-manager-modal.spec.js index 39e07dc9598a..098c2bb84828 100644 --- a/packages/cfd/src/Containers/__tests__/cfd-password-manager-modal.spec.js +++ b/packages/cfd/src/Containers/__tests__/cfd-password-manager-modal.spec.js @@ -101,7 +101,7 @@ describe('', () => { is_visible: true, platform: 'mt5', selected_login: 'MTD20103241', - selected_account: 'Derived', + selected_account: 'Standard', toggleModal: jest.fn(), selected_account_type: 'financial', selected_account_group: 'demo', diff --git a/packages/cfd/src/Containers/__tests__/cfd-password-modal.spec.js b/packages/cfd/src/Containers/__tests__/cfd-password-modal.spec.js index e754e10622d0..46703ae3d031 100644 --- a/packages/cfd/src/Containers/__tests__/cfd-password-modal.spec.js +++ b/packages/cfd/src/Containers/__tests__/cfd-password-modal.spec.js @@ -1,7 +1,7 @@ import React from 'react'; import { Router } from 'react-router'; import { createBrowserHistory } from 'history'; -import { WS, getErrorMessages, validPassword, Jurisdiction } from '@deriv/shared'; +import { WS, getErrorMessages, validPassword, Jurisdiction, routes } from '@deriv/shared'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import CFDPasswordModal from '../cfd-password-modal'; import CFDProviders from '../../cfd-providers'; @@ -91,7 +91,24 @@ describe('', () => { document.body.removeChild(modal_root_el); }); + const originalWindowLocation = window.location; + + beforeEach(() => { + Object.defineProperty(window, 'location', { + configurable: true, + enumerable: true, + value: { + pathname: routes.trade, + }, + }); + }); + afterEach(() => { + Object.defineProperty(window, 'location', { + configurable: true, + enumerable: true, + value: originalWindowLocation, + }); jest.clearAllMocks(); }); @@ -330,7 +347,7 @@ describe('', () => { expect(await screen.findByRole('button', { name: /transfer now/i })); }); - it('should display Derived icon in Success Dialog', async () => { + it('should display Standard icon in Success Dialog', async () => { const store = mockStore(mockRootStore); store.client.account_status = { status: ['mt5_password_not_set', 'dxtrade_password_not_set'] }; @@ -347,7 +364,7 @@ describe('', () => { } ); - expect(await screen.findByText('IcMt5SyntheticPlatform')).toBeInTheDocument(); + expect(await screen.findByText('IcMt5StandardPlatform')).toBeInTheDocument(); }); it('should display icon in Success Dialog in tradershub', async () => { @@ -367,7 +384,7 @@ describe('', () => { } ); - expect(await screen.findByText('IcMt5SyntheticPlatform')).toBeInTheDocument(); + expect(await screen.findByText('IcMt5StandardPlatform')).toBeInTheDocument(); }); it('should display Financial icon in Success Dialog', async () => { diff --git a/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js b/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js index eff858764deb..ffecbc7aef77 100644 --- a/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js +++ b/packages/cfd/src/Containers/__tests__/cfd-top-up-demo-modal.spec.js @@ -21,7 +21,7 @@ describe('CFDTopUpDemoModal', () => { const synthetic_config = { account_type: 'synthetic', leverage: 500, - short_title: 'Derived', + short_title: 'Standard', }; const financial_config = { @@ -39,7 +39,7 @@ describe('CFDTopUpDemoModal', () => { synthetic: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: 'Demo Derived', + title: 'Demo Standard', short_title: synthetic_config.short_title, }, financial: { @@ -53,7 +53,7 @@ describe('CFDTopUpDemoModal', () => { synthetic: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: 'Derived', + title: 'Standard', short_title: synthetic_config.short_title, }, financial: { diff --git a/packages/cfd/src/Containers/__tests__/dmt5-trade-modal.spec.tsx b/packages/cfd/src/Containers/__tests__/dmt5-trade-modal.spec.tsx index 3f60fef2acf5..db81757422ea 100644 --- a/packages/cfd/src/Containers/__tests__/dmt5-trade-modal.spec.tsx +++ b/packages/cfd/src/Containers/__tests__/dmt5-trade-modal.spec.tsx @@ -54,8 +54,8 @@ describe('', () => { windows: 'https://download.mql5.com/cdn/web/22698/mt5/derivsvg5setup.exe', }, display_login: 40021028, - icon: 'Derived', - sub_title: 'Derived', + icon: 'Standard', + sub_title: 'Standard', short_code_and_region: 'SVG', platform: 'mt5', description: 40021028, diff --git a/packages/cfd/src/Containers/__tests__/mt5-mobile-redirect-option.spec.js b/packages/cfd/src/Containers/__tests__/mt5-mobile-redirect-option.spec.js index cc30b5ac3441..d982b0e9a41c 100644 --- a/packages/cfd/src/Containers/__tests__/mt5-mobile-redirect-option.spec.js +++ b/packages/cfd/src/Containers/__tests__/mt5-mobile-redirect-option.spec.js @@ -1,8 +1,8 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { isSafariBrowser, mobileOSDetect } from '@deriv/shared'; +import { isSafariBrowser, mobileOSDetectAsync } from '@deriv/shared'; import MT5MobileRedirectOption from '../mt5-mobile-redirect-option'; -import { DEEP_LINK, getMobileAppInstallerURL, getPlatformMt5DownloadLink } from '../../../src/Helpers/constants'; +import { getDeeplinkUrl, getMobileAppInstallerURL, getPlatformMt5DownloadLink } from '../../../src/Helpers/constants'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -63,10 +63,11 @@ describe('', () => { configurable: true, }); - expect(mobileOSDetect()).toBe('iOS'); + const os = await mobileOSDetectAsync(); + expect(os).toBe('iOS'); expect(isSafariBrowser()).toBe(true); - const expectedUrl = getMobileAppInstallerURL({ mt5_trade_account: mock_props.mt5_trade_account }); + const expectedUrl = await getMobileAppInstallerURL({ mt5_trade_account: mock_props.mt5_trade_account }); expect(expectedUrl).toBe(mock_props.mt5_trade_account.white_label_links.ios); }); @@ -75,10 +76,11 @@ describe('', () => { value: 'Mozilla/5.0 (Linux; Android 10; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36', configurable: true, }); - expect(mobileOSDetect()).toBe('Android'); + const os = await mobileOSDetectAsync(); + expect(os).toBe('Android'); expect(isSafariBrowser()).toBe(false); - const expectedUrl = getMobileAppInstallerURL({ mt5_trade_account: mock_props.mt5_trade_account }); + const expectedUrl = await getMobileAppInstallerURL({ mt5_trade_account: mock_props.mt5_trade_account }); expect(expectedUrl).toBe(mock_props.mt5_trade_account.white_label_links.android); }); @@ -87,16 +89,17 @@ describe('', () => { value: 'Mozilla/5.0 (Linux; Android 10; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36', configurable: true, }); - expect(mobileOSDetect()).toBe('huawei'); + const os = await mobileOSDetectAsync(); + expect(os).toBe('huawei'); expect(isSafariBrowser()).toBe(false); - const expectedUrl = getMobileAppInstallerURL({ mt5_trade_account: mock_props.mt5_trade_account }); + const expectedUrl = await getMobileAppInstallerURL({ mt5_trade_account: mock_props.mt5_trade_account }); expect(expectedUrl).toBe(getPlatformMt5DownloadLink('huawei')); }); it('should open MT5 app when user click "Trade with MT5 Mobile App" and has the app installed', () => { renderComponent({ props: mock_props }); - const expectedUrl = DEEP_LINK({ mt5_trade_account: mock_props.mt5_trade_account }); + const expectedUrl = getDeeplinkUrl({ mt5_trade_account: mock_props.mt5_trade_account }); expect(expectedUrl).toBe( `metatrader5://account?login=${mock_props.mt5_trade_account?.display_login}&server=${mock_props.mt5_trade_account?.server_info?.environment}` ); diff --git a/packages/cfd/src/Containers/cfd-compare-accounts/__tests__/cfd-compare-accounts-title-icon.spec.tsx b/packages/cfd/src/Containers/cfd-compare-accounts/__tests__/cfd-compare-accounts-title-icon.spec.tsx index 069246a9836e..7d6a88250072 100644 --- a/packages/cfd/src/Containers/cfd-compare-accounts/__tests__/cfd-compare-accounts-title-icon.spec.tsx +++ b/packages/cfd/src/Containers/cfd-compare-accounts/__tests__/cfd-compare-accounts-title-icon.spec.tsx @@ -17,19 +17,19 @@ const mocked_props = { describe('', () => { test('should render correct title for synthetic_svg market type and shortcode', () => { render(); - expect(screen.getByText('Derived - SVG')).toBeInTheDocument(); + expect(screen.getByText('Standard - SVG')).toBeInTheDocument(); }); test('should render correct title for synthetic_bvi market type and shortcode', () => { mocked_props.trading_platforms.shortcode = 'bvi'; render(); - expect(screen.getByText('Derived - BVI')).toBeInTheDocument(); + expect(screen.getByText('Standard - BVI')).toBeInTheDocument(); }); test('should render correct title for synthetic_vanuatu market type and shortcode', () => { mocked_props.trading_platforms.shortcode = 'vanuatu'; render(); - expect(screen.getByText('Derived - Vanuatu')).toBeInTheDocument(); + expect(screen.getByText('Standard - Vanuatu')).toBeInTheDocument(); }); test('should render correct title for financial_labuan market type and shortcode', () => { @@ -88,7 +88,7 @@ describe('', () => { mocked_props.is_demo = true; mocked_props.is_eu_user = false; render(); - expect(screen.getByText('Derived Demo')).toBeInTheDocument(); + expect(screen.getByText('Standard Demo')).toBeInTheDocument(); }); test('should render correct title for financial market type and shortcode demo account', () => { diff --git a/packages/cfd/src/Containers/cfd-password-modal.tsx b/packages/cfd/src/Containers/cfd-password-modal.tsx index 535580360954..32eb6df03269 100644 --- a/packages/cfd/src/Containers/cfd-password-modal.tsx +++ b/packages/cfd/src/Containers/cfd-password-modal.tsx @@ -33,13 +33,7 @@ import { Localize, localize } from '@deriv/translations'; import TradingPlatformIcon from '../Assets/svgs/trading-platform'; import SuccessDialog from '../Components/success-dialog.jsx'; import MigrationSuccessModal from '../Components/migration-success-modal'; -import { - getDxCompanies, - getFormattedJurisdictionCode, - getMtCompanies, - TDxCompanies, - TMtCompanies, -} from '../Stores/Modules/CFD/Helpers/cfd-config'; +import { getDxCompanies, getMtCompanies, TDxCompanies, TMtCompanies } from '../Stores/Modules/CFD/Helpers/cfd-config'; import { useCfdStore } from '../Stores/Modules/CFD/Helpers/useCfdStores'; import CFDPasswordModalTitle from './cfd-password-modal-title'; import { CFD_PLATFORMS, JURISDICTION, CATEGORY } from '../Helpers/cfd-config'; @@ -180,7 +174,7 @@ const IconType = React.memo(({ platform, type, show_eu_related_content }: TIconT } switch (type) { case 'synthetic': - return ; + return ; case 'all': return ; case 'financial': @@ -194,7 +188,7 @@ const IconType = React.memo(({ platform, type, show_eu_related_content }: TIconT } else { switch (type) { case 'synthetic': - return ; + return ; case 'all': return ; case 'financial': diff --git a/packages/cfd/src/Containers/dmt5-trade-modal.tsx b/packages/cfd/src/Containers/dmt5-trade-modal.tsx index 9ee07c66a667..10901331ca54 100644 --- a/packages/cfd/src/Containers/dmt5-trade-modal.tsx +++ b/packages/cfd/src/Containers/dmt5-trade-modal.tsx @@ -62,7 +62,7 @@ const DMT5TradeModal = observer( }); const getAccountTitle = () => { if (show_eu_related_content) return 'CFDs'; - else if (mt5_trade_account.market_type === MARKET_TYPE.SYNTHETIC) return 'Derived'; + else if (mt5_trade_account.market_type === MARKET_TYPE.SYNTHETIC) return 'Standard'; else if (mt5_trade_account.market_type === MARKET_TYPE.ALL) return 'SwapFree'; return 'Financial'; }; diff --git a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-card.spec.tsx b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-card.spec.tsx index 06635ef631da..61dc5e9a5fb5 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-card.spec.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-card.spec.tsx @@ -121,7 +121,7 @@ describe('JurisdictionCard', () => { expect(screen.getByText('0.5 pips')).toBeInTheDocument(); expect(screen.getByText('Verifications')).toBeInTheDocument(); expect(screen.getByText('Learn more')).toBeInTheDocument(); - expect(screen.getByText('about verifications needed.')).toBeInTheDocument(); + expect(screen.getByText('about required verifications.')).toBeInTheDocument(); expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); expect(screen.getByText('Vanuatu Financial Services Commission')).toBeInTheDocument(); }); @@ -141,7 +141,7 @@ describe('JurisdictionCard', () => { expect(screen.getByText('0.5 pips')).toBeInTheDocument(); expect(screen.getByText('Verifications')).toBeInTheDocument(); expect(screen.getByText('Learn more')).toBeInTheDocument(); - expect(screen.getByText('about verifications needed.')).toBeInTheDocument(); + expect(screen.getByText('about required verifications.')).toBeInTheDocument(); expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); expect( screen.getByText('Malta Financial Services Authority (MFSA) (licence no. IS/70156)') @@ -163,7 +163,7 @@ describe('JurisdictionCard', () => { expect(screen.getByText('0.5 pips')).toBeInTheDocument(); expect(screen.getByText('Verifications')).toBeInTheDocument(); expect(screen.getByText('Learn more')).toBeInTheDocument(); - expect(screen.getByText('about verifications needed.')).toBeInTheDocument(); + expect(screen.getByText('about required verifications.')).toBeInTheDocument(); expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); expect( screen.getByText('British Virgin Islands Financial Services Commission (License no. SIBA/L/18/1114)') @@ -184,7 +184,7 @@ describe('JurisdictionCard', () => { expect(screen.getByText('1.4 pips')).toBeInTheDocument(); expect(screen.getByText('Verifications')).toBeInTheDocument(); expect(screen.getByText('Learn more')).toBeInTheDocument(); - expect(screen.getByText('about verifications needed.')).toBeInTheDocument(); + expect(screen.getByText('about required verifications.')).toBeInTheDocument(); expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); expect(screen.getByText('Labuan Financial Services Authority (licence no. MB/18/0024)')).toBeInTheDocument(); }); @@ -194,8 +194,12 @@ describe('JurisdictionCard', () => { render(); expect(screen.getByText('St. Vincent & Grenadines')).toBeInTheDocument(); expect(screen.getByText('Assets')).toBeInTheDocument(); - expect(screen.getByText('40+')).toBeInTheDocument(); - expect(screen.getByText('Synthetic indices, basket indices, and derived FX')).toBeInTheDocument(); + expect(screen.getByText('210+')).toBeInTheDocument(); + expect( + screen.getByText( + 'Forex (standard), stock indices, commodities, cryptocurrencies, synthetic indices, basket indices and derived FX' + ) + ).toBeInTheDocument(); expect(screen.getByText('Leverage')).toBeInTheDocument(); expect(screen.getByText('1:1000')).toBeInTheDocument(); expect(screen.getByText('Verifications')).toBeInTheDocument(); diff --git a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx index c2cceba32c14..cb3cd7875bcc 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-content.spec.tsx @@ -201,8 +201,12 @@ describe('JurisdictionModalContent', () => { it('should display content of 3 types of jurisdiction correctly for synthetics account', () => { render(); expect(screen.getAllByText('Assets')).toHaveLength(3); - expect(screen.getAllByText('Synthetic indices, basket indices, and derived FX')).toHaveLength(3); - expect(screen.getAllByText('40+')).toHaveLength(3); + expect( + screen.getAllByText( + 'Forex (standard), stock indices, commodities, cryptocurrencies, synthetic indices, basket indices and derived FX' + ) + ).toHaveLength(3); + expect(screen.getAllByText('210+')).toHaveLength(3); expect(screen.getAllByText('Leverage')).toHaveLength(3); expect(screen.getAllByText('1:1000')).toHaveLength(3); expect(screen.getAllByText('Verifications')).toHaveLength(3); @@ -210,7 +214,7 @@ describe('JurisdictionModalContent', () => { screen.getByText('You will need to submit proof of identity and address once you reach certain thresholds.') ).toBeInTheDocument(); expect(screen.getAllByText('Learn more')).toHaveLength(2); - expect(screen.getAllByText('about verifications needed.')).toHaveLength(2); + expect(screen.getAllByText('about required verifications.')).toHaveLength(2); expect(screen.getAllByText('Regulator/EDR')).toHaveLength(3); expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); expect( @@ -251,7 +255,7 @@ describe('JurisdictionModalContent', () => { screen.getByText('You will need to submit proof of identity and address once you reach certain thresholds.') ).toBeInTheDocument(); expect(screen.getAllByText('Learn more')).toHaveLength(3); - expect(screen.getAllByText('about verifications needed.')).toHaveLength(3); + expect(screen.getAllByText('about required verifications.')).toHaveLength(3); expect(screen.getAllByText('Regulator/EDR')).toHaveLength(4); expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); expect( @@ -286,7 +290,7 @@ describe('JurisdictionModalContent', () => { expect(screen.getByText('0.5 pips')).toBeInTheDocument(); expect(screen.getByText('Verifications')).toBeInTheDocument(); expect(screen.getByText('Learn more')).toBeInTheDocument(); - expect(screen.getByText('about verifications needed.')).toBeInTheDocument(); + expect(screen.getByText('about required verifications.')).toBeInTheDocument(); expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); expect( screen.getByText('Malta Financial Services Authority (MFSA) (licence no. IS/70156)') @@ -349,8 +353,12 @@ describe('JurisdictionModalContent', () => { ).toBeInTheDocument(); expect(screen.getByText('Regulator/EDR')).toBeInTheDocument(); expect(screen.getByText('Deriv (SVG) LLC (company no. 273 LLC 2020)')).toBeInTheDocument(); - expect(screen.getByText('40+')).toBeInTheDocument(); - expect(screen.getByText('Synthetic indices, basket indices, and derived FX')).toBeInTheDocument(); + expect(screen.getByText('210+')).toBeInTheDocument(); + expect( + screen.getByText( + 'Forex (standard), stock indices, commodities, cryptocurrencies, synthetic indices, basket indices and derived FX' + ) + ).toBeInTheDocument(); }); it('should display cfd-jurisdiction-card--all__wrapper in class name', () => { diff --git a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx index 4f509ac10611..81fcf8b804ce 100644 --- a/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx +++ b/packages/cfd/src/Containers/jurisdiction-modal/__test__/jurisdiction-modal-foot-note.spec.tsx @@ -60,7 +60,7 @@ describe('JurisdictionModalFootNote', () => { { wrapper } ); expect( - screen.getByText('Add your Deriv MT5 Derived account under Deriv (SVG) LLC (company no. 273 LLC 2020).') + screen.getByText('Add your Deriv MT5 Standard account under Deriv (SVG) LLC (company no. 273 LLC 2020).') ).toBeInTheDocument(); }); @@ -75,7 +75,7 @@ describe('JurisdictionModalFootNote', () => { ); expect( screen.getByText( - 'Add your Deriv MT5 Derived account under Deriv (BVI) Ltd, regulated by the British Virgin Islands Financial Services Commission (License no. SIBA/L/18/1114).' + 'Add your Deriv MT5 Standard account under Deriv (BVI) Ltd, regulated by the British Virgin Islands Financial Services Commission (License no. SIBA/L/18/1114).' ) ).toBeInTheDocument(); }); @@ -139,7 +139,7 @@ describe('JurisdictionModalFootNote', () => { ); expect( screen.getByText( - 'Add Your Deriv MT5 Derived account under Deriv (V) Ltd, regulated by the Vanuatu Financial Services Commission.' + 'Add Your Deriv MT5 Standard account under Deriv (V) Ltd, regulated by the Vanuatu Financial Services Commission.' ) ).toBeInTheDocument(); }); @@ -203,7 +203,7 @@ describe('JurisdictionModalFootNote', () => { ); expect( screen.getByText( - 'Add your Deriv MT5 Derived STP account under Deriv (FX) Ltd regulated by Labuan Financial Services Authority (Licence no. MB/18/0024).' + 'Add your Deriv MT5 Standard STP account under Deriv (FX) Ltd regulated by Labuan Financial Services Authority (Licence no. MB/18/0024).' ) ).toBeInTheDocument(); }); diff --git a/packages/cfd/src/Containers/migration-banner/__test__/migration-banner.spec.tsx b/packages/cfd/src/Containers/migration-banner/__test__/migration-banner.spec.tsx index b909a7b7f7c6..db769088f8c5 100644 --- a/packages/cfd/src/Containers/migration-banner/__test__/migration-banner.spec.tsx +++ b/packages/cfd/src/Containers/migration-banner/__test__/migration-banner.spec.tsx @@ -51,20 +51,20 @@ describe('MigrationBanner', () => { }; }); - it('should render MigrationBanner with both MT5 Derived SVG and MT5 Financial SVG text', () => { + it('should render MigrationBanner with both MT5 Standard SVG and MT5 Financial SVG text', () => { renderComponent(); - const texts = [/Upgrade your/i, /Derived/i, /and/i, /Financial MT5/i, /account\(s\)/i]; + const texts = [/Upgrade your/i, /Standard/i, /Financial MT5/i, /account\(s\)/i]; texts.forEach(text => { expect(screen.getByText(text)).toBeInTheDocument(); }); expect(screen.getByRole('button', { name: /upgrade/i })).toBeInTheDocument(); }); - it('should render MigrationBanner with MT5 Derived SVG', () => { + it('should render MigrationBanner with MT5 Standard SVG', () => { response.has_derived_and_financial_mt5 = false; renderComponent(); - const texts = [/Upgrade your/i, /Derived MT5/i, /account\(s\)/i]; + const texts = [/Upgrade your/i, /Standard MT5/i, /account\(s\)/i]; texts.forEach(text => { expect(screen.getByText(text)).toBeInTheDocument(); }); diff --git a/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-account-icons.spec.tsx b/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-account-icons.spec.tsx index 9d0e092b73f6..ad5df60a63f3 100644 --- a/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-account-icons.spec.tsx +++ b/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-account-icons.spec.tsx @@ -42,11 +42,11 @@ describe('MT5MigrationAccountIcons', () => { }; }); - it('should render MT5MigrationAccountIcons for derived bvi account', () => { + it('should render MT5MigrationAccountIcons for standard bvi account', () => { response.eligible_svg_to_bvi_derived_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument(); - expect(screen.getByTestId('dt_migrate_to_bvi_derived')).toBeInTheDocument(); + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument(); + expect(screen.getByTestId('dt_migrate_to_bvi_standard')).toBeInTheDocument(); }); it('should render MT5MigrationAccountIcons for financial bvi account', () => { @@ -56,11 +56,11 @@ describe('MT5MigrationAccountIcons', () => { expect(screen.getByTestId('dt_migrate_to_bvi_financial')).toBeInTheDocument(); }); - it('should render MT5MigrationAccountIcons for derived vanuatu account', () => { + it('should render MT5MigrationAccountIcons for standard vanuatu account', () => { response.eligible_svg_to_vanuatu_derived_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument(); - expect(screen.getByTestId('dt_migrate_to_vanuatu_derived')).toBeInTheDocument(); + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument(); + expect(screen.getByTestId('dt_migrate_to_vanuatu_standard')).toBeInTheDocument(); }); it('should render MT5MigrationAccountIcons for financial vanuatu account', () => { @@ -70,12 +70,12 @@ describe('MT5MigrationAccountIcons', () => { expect(screen.getByTestId('dt_migrate_to_vanuatu_financial')).toBeInTheDocument(); }); - it('should render MT5MigrationAccountIcons for svg to bvi derived account and svg to bvi financial account', () => { + it('should render MT5MigrationAccountIcons for svg to bvi standard account and svg to bvi financial account', () => { response.eligible_svg_to_bvi_derived_accounts = true; response.eligible_svg_to_bvi_financial_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument(); - expect(screen.getByTestId('dt_migrate_to_bvi_derived')).toBeInTheDocument(); + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument(); + expect(screen.getByTestId('dt_migrate_to_bvi_standard')).toBeInTheDocument(); expect(screen.getByTestId('dt_migrate_from_svg_financial')).toBeInTheDocument(); expect(screen.getByTestId('dt_migrate_to_bvi_financial')).toBeInTheDocument(); }); diff --git a/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-front-side-content.spec.tsx b/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-front-side-content.spec.tsx index 0c372f42cc45..db6a74ef5055 100644 --- a/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-front-side-content.spec.tsx +++ b/packages/cfd/src/Containers/mt5-migration-modal/__test__/mt5-migration-front-side-content.spec.tsx @@ -65,7 +65,7 @@ describe('MT5MigrationFrontSideContent', () => { expect(screen.getByRole('button', { name: 'Next' })).toBeInTheDocument(); }); - it('should render MT5MigrationFrontSideContent with derived and Financial vanuatu account details', () => { + it('should render MT5MigrationFrontSideContent with Standard and Financial vanuatu account details', () => { migration_response = { eligible_account_to_migrate_label: 'Vanuatu', eligible_svg_to_bvi_derived_accounts: false, @@ -86,7 +86,7 @@ describe('MT5MigrationFrontSideContent', () => { expect(screen.getByText(/account\(s\) will remain accessible\./i)).toBeInTheDocument(); expect(screen.getByText(/By clicking/i)).toBeInTheDocument(); expect(screen.getByText(/you agree to move your/i)).toBeInTheDocument(); - expect(screen.getByText(/MT5 Derived and Financial SVG account\(s\)/i)).toBeInTheDocument(); + expect(screen.getByText(/MT5 Standard and Financial SVG account\(s\)/i)).toBeInTheDocument(); expect(screen.getByText(/under Deriv Vanuatu Ltd’s/i)).toBeInTheDocument(); const terms_conditions_link = screen.getByRole('link', { name: 'terms and conditions' }); @@ -95,11 +95,11 @@ describe('MT5MigrationFrontSideContent', () => { expect(screen.getByRole('button', { name: 'Next' })).toBeInTheDocument(); }); - it('should render svg to bvi derived Icons', () => { + it('should render svg to bvi Standard Icons', () => { migration_response.eligible_svg_to_bvi_derived_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument; - expect(screen.getByTestId('dt_migrate_to_bvi_derived')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_to_bvi_standard')).toBeInTheDocument; }); it('should render svg to bvi financial Icons', () => { @@ -109,11 +109,11 @@ describe('MT5MigrationFrontSideContent', () => { expect(screen.getByTestId('dt_migrate_to_bvi_financial')).toBeInTheDocument; }); - it('should render svg to vanuatu derived Icons', () => { + it('should render svg to vanuatu Standard Icons', () => { migration_response.eligible_svg_to_vanuatu_derived_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument; - expect(screen.getByTestId('dt_migrate_to_vanuatu_derived')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_to_vanuatu_standard')).toBeInTheDocument; }); it('should render svg to vanuatu financial Icons', () => { @@ -123,22 +123,22 @@ describe('MT5MigrationFrontSideContent', () => { expect(screen.getByTestId('dt_migrate_to_vanuatu_financial')).toBeInTheDocument; }); - it('should render both derived svg to bvi and financial svg to bvi Icons', () => { + it('should render both Standard svg to bvi and financial svg to bvi Icons', () => { migration_response.eligible_svg_to_bvi_derived_accounts = true; migration_response.eligible_svg_to_bvi_financial_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument; - expect(screen.getByTestId('dt_migrate_to_bvi_derived')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_to_bvi_standard')).toBeInTheDocument; expect(screen.getByTestId('dt_migrate_from_svg_financial')).toBeInTheDocument; expect(screen.getByTestId('dt_migrate_to_bvi_financial')).toBeInTheDocument; }); - it('should render both derived svg to vanuatu and financial svg to vanuatu Icons', () => { + it('should render both Standard svg to vanuatu and financial svg to vanuatu Icons', () => { migration_response.eligible_svg_to_vanuatu_derived_accounts = true; migration_response.eligible_svg_to_vanuatu_financial_accounts = true; renderComponent(); - expect(screen.getByTestId('dt_migrate_from_svg_derived')).toBeInTheDocument; - expect(screen.getByTestId('dt_migrate_to_vanuatu_derived')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_from_svg_standard')).toBeInTheDocument; + expect(screen.getByTestId('dt_migrate_to_vanuatu_standard')).toBeInTheDocument; expect(screen.getByTestId('dt_migrate_from_svg_financial')).toBeInTheDocument; expect(screen.getByTestId('dt_migrate_to_vanuatu_financial')).toBeInTheDocument; }); diff --git a/packages/cfd/src/Containers/mt5-migration-modal/mt5-migration-account-icons.tsx b/packages/cfd/src/Containers/mt5-migration-modal/mt5-migration-account-icons.tsx index 71677f294c07..c81dcc526612 100644 --- a/packages/cfd/src/Containers/mt5-migration-modal/mt5-migration-account-icons.tsx +++ b/packages/cfd/src/Containers/mt5-migration-modal/mt5-migration-account-icons.tsx @@ -28,7 +28,7 @@ const MT5MigrationAccountIcons = () => {
{(eligible_svg_to_bvi_derived_accounts || eligible_svg_to_vanuatu_derived_accounts) && ( - + )} {(eligible_svg_to_bvi_financial_accounts || eligible_svg_to_vanuatu_financial_accounts) && ( @@ -41,13 +41,13 @@ const MT5MigrationAccountIcons = () => {
{eligible_svg_to_bvi_derived_accounts && ( - + )} {eligible_svg_to_bvi_financial_accounts && ( )} {eligible_svg_to_vanuatu_derived_accounts && ( - + )} {eligible_svg_to_vanuatu_financial_accounts && ( diff --git a/packages/cfd/src/Containers/mt5-mobile-redirect-option.scss b/packages/cfd/src/Containers/mt5-mobile-redirect-option.scss index 9497517a860a..acd7382e3f80 100644 --- a/packages/cfd/src/Containers/mt5-mobile-redirect-option.scss +++ b/packages/cfd/src/Containers/mt5-mobile-redirect-option.scss @@ -15,6 +15,10 @@ border-radius: 0.8rem; background-color: var(--general-section-4); text-decoration: none; + border: none; + outline: none; + box-shadow: none; + .full-row { gap: 0.8rem; width: 100%; diff --git a/packages/cfd/src/Containers/mt5-mobile-redirect-option.tsx b/packages/cfd/src/Containers/mt5-mobile-redirect-option.tsx index c98d142a10d0..5f3d8a78f839 100644 --- a/packages/cfd/src/Containers/mt5-mobile-redirect-option.tsx +++ b/packages/cfd/src/Containers/mt5-mobile-redirect-option.tsx @@ -1,36 +1,39 @@ import React from 'react'; import { DetailsOfEachMT5Loginid } from '@deriv/api-types'; import { Icon, Text } from '@deriv/components'; -import { Localize } from '@deriv/translations'; -import { DEEP_LINK, WEBTRADER_URL, getMobileAppInstallerURL } from '../Helpers/constants'; +import { getDeeplinkUrl, getMobileAppInstallerUrl, getWebtraderUrl } from '../Helpers/constants'; import './mt5-mobile-redirect-option.scss'; -import { isSafariBrowser } from '@deriv/shared'; - -type TMT5MobileRedirectOptionProps = { - mt5_trade_account: DetailsOfEachMT5Loginid; -}; -const MT5MobileRedirectOption = ({ mt5_trade_account }: TMT5MobileRedirectOptionProps) => { - let mobile_url; +import { Localize } from '@deriv/translations'; - const mobileURLSet = () => { - mobile_url = window.location.replace(DEEP_LINK({ mt5_trade_account })); +const MT5MobileRedirectOption = ({ mt5_trade_account }: { mt5_trade_account: DetailsOfEachMT5Loginid }) => { + const mobileURLSet = async () => { + window.location.replace(getDeeplinkUrl({ mt5_trade_account })); + const mobileAppURL = await getMobileAppInstallerUrl({ mt5_trade_account }); const timeout = setTimeout(() => { - mobile_url = window.location.replace(getMobileAppInstallerURL({ mt5_trade_account }) as string); - }, 1500); + mobileAppURL && window.location.replace(mobileAppURL); + }, 4000); - if (!isSafariBrowser()) { - window.onblur = () => { + document.addEventListener('visibilitychange', function () { + if (document.hidden) { clearTimeout(timeout); - }; - } + } + + // iOS (17+) and certain browsers (edge) may have popups before redirecting + if (window.onblur) { + clearTimeout(timeout); // installer wont open but will redirect to MetaTrader5 + if (!document.hidden) { + mobileAppURL && window.location.replace(mobileAppURL); // if it is not redirecting then open installer + } + } + }); }; return ( - + { switch (shortcode) { case MARKET_TYPE_SHORTCODE.SYNTHETIC_SVG: - return is_demo ? localize('Derived Demo') : localize('Derived - SVG'); + return is_demo ? localize('Standard Demo') : localize('Standard - SVG'); case MARKET_TYPE_SHORTCODE.SYNTHETIC_BVI: - return localize('Derived - BVI'); + return localize('Standard - BVI'); case MARKET_TYPE_SHORTCODE.SYNTHETIC_VANUATU: - return localize('Derived - Vanuatu'); + return localize('Standard - Vanuatu'); case MARKET_TYPE_SHORTCODE.FINANCIAL_SVG: return is_demo ? localize('Financial Demo') : localize('Financial - SVG'); case MARKET_TYPE_SHORTCODE.FINANCIAL_BVI: @@ -160,7 +162,7 @@ const platformsHeaderLabel = { const getAccountIcon = (shortcode: string) => { switch (shortcode) { case MARKET_TYPE.SYNTHETIC: - return 'Derived'; + return 'Standard'; case MARKET_TYPE.FINANCIAL: return 'Financial'; case MARKET_TYPE.ALL: diff --git a/packages/cfd/src/Helpers/constants.ts b/packages/cfd/src/Helpers/constants.ts index 1b3c71ab9e9e..9dc6e632da17 100644 --- a/packages/cfd/src/Helpers/constants.ts +++ b/packages/cfd/src/Helpers/constants.ts @@ -5,11 +5,12 @@ import { validLength, validPassword, validMT5Password, - mobileOSDetect, + mobileOSDetectAsync, } from '@deriv/shared'; import { localize } from '@deriv/translations'; import { TCFDsPlatformType, TDetailsOfEachMT5Loginid, TMobilePlatforms } from 'Components/props.types'; import { CFD_PLATFORMS, MOBILE_PLATFORMS, DESKTOP_PLATFORMS, CATEGORY } from './cfd-config'; +import { white_label_links } from './url-config'; const platformsText = (platform: TCFDsPlatformType) => { switch (platform) { @@ -40,9 +41,6 @@ const getTitle = (market_type: string, is_eu_user: boolean) => { const { is_staging, is_test_link } = getPlatformFromUrl(); -const DEEP_LINK = ({ mt5_trade_account }: { mt5_trade_account: TDetailsOfEachMT5Loginid }) => - `metatrader5://account?login=${mt5_trade_account?.display_login}&server=${mt5_trade_account?.server_info?.environment}`; - const STRATEGY_PROVIDER_NOTES = [ 'When setting up a strategy, you have the option to impose fees.', 'For strategies where you impose fees, you must assign one of your existing accounts to process these fees. The same ‘Account For Fees’ can support multiple fee-based strategies.', @@ -51,9 +49,6 @@ const STRATEGY_PROVIDER_NOTES = [ 'An account cannot simultaneously be a strategy provider and serve as an ‘Account For Fees’.', ]; -const WEBTRADER_URL = ({ mt5_trade_account }: { mt5_trade_account: TDetailsOfEachMT5Loginid }) => - `${mt5_trade_account.white_label_links?.webtrader_url}?login=${mt5_trade_account?.display_login}&server=${mt5_trade_account?.server_info?.environment}`; - const REAL_DXTRADE_URL = 'https://dx.deriv.com'; const DEMO_DXTRADE_URL = 'https://dx-demo.deriv.com'; @@ -151,10 +146,12 @@ const validatePassword = (password: string): string | undefined => { } }; -const getMobileAppInstallerURL = ({ mt5_trade_account }: { mt5_trade_account: TDetailsOfEachMT5Loginid }) => { - if (mobileOSDetect() === 'iOS') { +const getMobileAppInstallerURL = async ({ mt5_trade_account }: { mt5_trade_account: TDetailsOfEachMT5Loginid }) => { + const os = await mobileOSDetectAsync(); + + if (os === 'iOS') { return mt5_trade_account?.white_label_links?.ios; - } else if (mobileOSDetect() === 'huawei') { + } else if (os === 'huawei') { return getPlatformMt5DownloadLink('huawei'); } return mt5_trade_account?.white_label_links?.android; @@ -166,7 +163,7 @@ const getDesktopDownloadOptions = ({ mt5_trade_account }: { mt5_trade_account: T icon: 'IcRebrandingMt5Logo', text: 'MetaTrader 5 web', button_text: 'Open', - href: WEBTRADER_URL({ mt5_trade_account }), + href: getWebtraderUrl({ mt5_trade_account }), }, { icon: 'IcWindowsLogo', @@ -206,8 +203,29 @@ const getMobileDownloadOptions = ({ mt5_trade_account }: { mt5_trade_account: TD }, ]; +export const getWebtraderUrl = ({ mt5_trade_account }: { mt5_trade_account: TDetailsOfEachMT5Loginid }) => { + return `${mt5_trade_account?.white_label_links?.webtrader_url}?login=${mt5_trade_account?.display_login}&server=${mt5_trade_account?.server_info?.environment}`; +}; + +export const getDeeplinkUrl = ({ mt5_trade_account }: { mt5_trade_account: TDetailsOfEachMT5Loginid }) => { + return `metatrader5://account?login=${mt5_trade_account?.display_login}&server=${mt5_trade_account?.server_info?.environment}`; +}; + +export const getMobileAppInstallerUrl = async ({ + mt5_trade_account, +}: { + mt5_trade_account: TDetailsOfEachMT5Loginid; +}) => { + const os = await mobileOSDetectAsync(); + if (os === 'iOS') { + return mt5_trade_account?.white_label_links?.ios; + } else if (os === 'huawei') { + return white_label_links?.huawei; + } + return mt5_trade_account?.white_label_links?.android; +}; + export { - DEEP_LINK, REAL_DXTRADE_URL, DEMO_DXTRADE_URL, CTRADER_URL, @@ -226,7 +244,6 @@ export { getTopUpConfig, validatePassword, getMobileAppInstallerURL, - WEBTRADER_URL, getDesktopDownloadOptions, getMobileDownloadOptions, }; diff --git a/packages/cfd/src/Helpers/url-config.ts b/packages/cfd/src/Helpers/url-config.ts new file mode 100644 index 000000000000..25e057d685ac --- /dev/null +++ b/packages/cfd/src/Helpers/url-config.ts @@ -0,0 +1,40 @@ +/** + * This file contains the URLs for different platforms and environments. + * urlConfig will be sent as a proposal to the backend. + */ + +/** + * URLs for the cTrader platform. + */ +export const ctrader_links = { + android: 'https://play.google.com/store/apps/details?id=com.deriv.ct', + ios: 'https://apps.apple.com/us/app/deriv-ctrader/id6466996509', + live: 'https://ct.deriv.com', + staging: 'https://ct-uat.deriv.com', + windows: 'https://getctrader.com/deriv/ctrader-deriv-setup.exe', + mac: 'https://getctradermac.com/deriv/ctrader-deriv-setup.dmg', +}; + +/** + * URLs for the dxTrade platform. + */ +export const dxtrade_links = { + android: 'https://play.google.com/store/apps/details?id=com.deriv.dx', + huawei: 'https://appgallery.huawei.com/app/C104633219', + ios: 'https://apps.apple.com/us/app/deriv-x/id1563337503', + demo: 'https://dx-demo.deriv.com', + live: 'https://dx.deriv.com', +}; + +/** + * URLs for the mt5 platform. + */ +export const white_label_links = { + android: 'https://download.mql5.com/cdn/mobile/mt5/android?server=Deriv-Demo,Deriv-Server,Deriv-Server-02', + huawei: 'https://appgallery.huawei.com/#/app/C102015329', + ios: 'https://download.mql5.com/cdn/mobile/mt5/ios?server=Deriv-Demo,Deriv-Server,Deriv-Server-02', + linux: 'https://www.metatrader5.com/en/terminal/help/start_advanced/install_linux', + macos: 'https://download.mql5.com/cdn/web/metaquotes.software.corp/mt5/MetaTrader5.dmg', + windows: 'https://download.mql5.com/cdn/web/deriv.com.limited/mt5/deriv5setup.exe', + webtrader_url: 'https://mt5-real01-web-svg.deriv.com/terminal', +}; diff --git a/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts b/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts index 58ce8bb2b7a2..d0963846ac5c 100644 --- a/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts +++ b/packages/cfd/src/Stores/Modules/CFD/Helpers/cfd-config.ts @@ -14,7 +14,7 @@ export const getDxCompanies = () => { const synthetic_config = { account_type: '', leverage: 500, - short_title: localize('Derived'), + short_title: localize('Standard'), }; const financial_config = { account_type: 'financial', @@ -32,7 +32,7 @@ export const getDxCompanies = () => { synthetic: { dxtrade_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Demo Derived'), + title: localize('Demo Standard'), short_title: synthetic_config.short_title, }, financial: { @@ -58,7 +58,7 @@ export const getDxCompanies = () => { synthetic: { dxtrade_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Derived'), + title: localize('Standard'), short_title: synthetic_config.short_title, }, financial: { @@ -106,7 +106,7 @@ export const getMtCompanies = (is_eu: boolean) => { const synthetic_config = { account_type: '', leverage: 500, - short_title: localize('Derived'), + short_title: localize('Standard'), }; const financial_config = { account_type: 'financial', @@ -141,7 +141,7 @@ export const getMtCompanies = (is_eu: boolean) => { synthetic: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Demo Derived'), + title: localize('Demo Standard'), short_title: synthetic_config.short_title, }, financial: { @@ -159,7 +159,7 @@ export const getMtCompanies = (is_eu: boolean) => { synthetic_svg: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Demo Derived SVG'), + title: localize('Demo Standard SVG'), short_title: synthetic_config.short_title, }, @@ -204,25 +204,25 @@ export const getMtCompanies = (is_eu: boolean) => { synthetic: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Derived'), + title: localize('Standard'), short_title: synthetic_config.short_title, }, synthetic_svg: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Derived SVG'), + title: localize('Standard SVG'), short_title: synthetic_config.short_title, }, synthetic_bvi: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Derived BVI'), + title: localize('Standard BVI'), short_title: synthetic_config.short_title, }, synthetic_v: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: localize('Derived Vanuatu'), + title: localize('Standard Vanuatu'), short_title: synthetic_config.short_title, }, financial: { diff --git a/packages/cfd/src/features/Containers/__tests__/top-up-demo-modal.spec.js b/packages/cfd/src/features/Containers/__tests__/top-up-demo-modal.spec.js index 783bbdddcd30..dcbaab022100 100644 --- a/packages/cfd/src/features/Containers/__tests__/top-up-demo-modal.spec.js +++ b/packages/cfd/src/features/Containers/__tests__/top-up-demo-modal.spec.js @@ -22,7 +22,7 @@ describe('TopUpDemoModal', () => { const synthetic_config = { account_type: 'synthetic', leverage: 500, - short_title: 'Derived', + short_title: 'Standard', }; const financial_config = { @@ -40,7 +40,7 @@ describe('TopUpDemoModal', () => { synthetic: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: 'Demo Derived', + title: 'Demo Standard', short_title: synthetic_config.short_title, }, financial: { @@ -54,7 +54,7 @@ describe('TopUpDemoModal', () => { synthetic: { mt5_account_type: synthetic_config.account_type, leverage: synthetic_config.leverage, - title: 'Derived', + title: 'Standard', short_title: synthetic_config.short_title, }, financial: { diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx index 2eff7458e285..7571775b830a 100644 --- a/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx +++ b/packages/cfd/src/features/Containers/cfd-password-modal/cfd-password-modal.tsx @@ -398,7 +398,7 @@ const CFDPasswordModal = observer(({ form_error, platform }: TCFDPasswordModalPr const mt5_platform_label = jurisdiction_selected_shortcode !== JURISDICTION.MALTA_INVEST ? 'Deriv MT5' : ''; const accountTypes = () => { - if (platform === CFD_PLATFORMS.DXTRADE && type_label === 'Derived') { + if (platform === CFD_PLATFORMS.DXTRADE && type_label === 'Standard') { return 'Synthetic'; } else if (platform === CFD_PLATFORMS.CTRADER) { return 'CFDs'; diff --git a/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx b/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx index fcf9cff6838e..0f77aa5491ee 100644 --- a/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx +++ b/packages/cfd/src/features/Containers/cfd-password-modal/icon-type.tsx @@ -19,7 +19,7 @@ export const IconType = React.memo(({ platform, type, show_eu_related_content }: } switch (type) { case 'synthetic': - return ; + return ; case 'all': return ; case 'financial': diff --git a/packages/cfd/src/features/Containers/mt5-trade-modal.tsx b/packages/cfd/src/features/Containers/mt5-trade-modal.tsx index cbbcf1de5340..3184fdafaa69 100644 --- a/packages/cfd/src/features/Containers/mt5-trade-modal.tsx +++ b/packages/cfd/src/features/Containers/mt5-trade-modal.tsx @@ -11,7 +11,7 @@ import { isMobile, } from '@deriv/shared'; import { Localize, localize } from '@deriv/translations'; -import { WEBTRADER_URL, getPlatformMt5DownloadLink } from '../../Helpers/constants'; +import { getWebtraderUrl, getPlatformMt5DownloadLink } from '../../Helpers/constants'; import SpecBox from '../../Components/specbox'; import PasswordBox from '../../Components/passwordbox'; import TradingPlatformIcon from '../../Assets/svgs/trading-platform'; @@ -75,7 +75,7 @@ const DMT5TradeModal = ({ }); const getAccountTitle = () => { if (show_eu_related_content) return 'CFDs'; - else if (mt5_trade_account.market_type === MARKET_TYPE.SYNTHETIC) return 'Derived'; + else if (mt5_trade_account.market_type === MARKET_TYPE.SYNTHETIC) return 'Standard'; else if (mt5_trade_account.market_type === MARKET_TYPE.ALL) return 'SwapFree'; return 'Financial'; }; @@ -161,7 +161,7 @@ const DMT5TradeModal = ({ diff --git a/packages/components/src/components/button/button.scss b/packages/components/src/components/button/button.scss index d7e9f086fab6..c114544c9414 100644 --- a/packages/components/src/components/button/button.scss +++ b/packages/components/src/components/button/button.scss @@ -180,6 +180,20 @@ color: var(--text-colored-background); } } + &--black { + background: var(--button-get-started-bg); + &:hover:not([disabled]) { + opacity: 0.7; + } + &:active:not([disabled]) { + opacity: 0.7; + } + + .dc-btn__text, + .dc-btn__icon { + color: var(--general-main-1); + } + } &__small { height: 2.4rem; min-width: 4.8rem; diff --git a/packages/components/src/components/button/button.tsx b/packages/components/src/components/button/button.tsx index 99997d4d9c28..97e5f4e252cd 100644 --- a/packages/components/src/components/button/button.tsx +++ b/packages/components/src/components/button/button.tsx @@ -6,6 +6,7 @@ import Text from '../text'; export type TButtonCommonProps = { alternate: boolean; + black: boolean; blue: boolean; green: boolean; has_effect: boolean; @@ -45,6 +46,7 @@ const ButtonGroup = ({ children, className }: TButtonGroupProps) => (
{children}
); const Button = ({ + black, blue, children, className = '', @@ -83,6 +85,7 @@ const Button = ({ { 'dc-btn__effect': has_effect, 'dc-btn--primary': primary, + 'dc-btn--black': black, 'dc-btn--blue': blue, 'dc-btn--secondary': secondary, 'dc-btn--tertiary': tertiary, diff --git a/packages/components/src/components/contract-card/contract-card-items/contract-card-sell.tsx b/packages/components/src/components/contract-card/contract-card-items/contract-card-sell.tsx index cb6c8778190c..a7f663e99434 100644 --- a/packages/components/src/components/contract-card/contract-card-items/contract-card-sell.tsx +++ b/packages/components/src/components/contract-card/contract-card-items/contract-card-sell.tsx @@ -32,6 +32,7 @@ const ContractCardSell = ({ contract_info, getCardLabels, is_sell_requested, onC className={classNames('dc-btn--sell', { 'dc-btn--loading': is_sell_requested, })} + data-testid='dt_contract_card_sell' is_disabled={is_sell_requested} text={getCardLabels().SELL} onClick={onClick} diff --git a/packages/components/src/components/icon/appstore/ic-appstore-logged-out-eu-coins-desktop.svg b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-eu-coins-desktop.svg new file mode 100644 index 000000000000..ef0215f62857 --- /dev/null +++ b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-eu-coins-desktop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/appstore/ic-appstore-logged-out-eu-coins-responsive.svg b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-eu-coins-responsive.svg new file mode 100644 index 000000000000..7bff5b57793b --- /dev/null +++ b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-eu-coins-responsive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/appstore/ic-appstore-logged-out-non-eu-coins-desktop.svg b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-non-eu-coins-desktop.svg new file mode 100644 index 000000000000..2cdc0131c604 --- /dev/null +++ b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-non-eu-coins-desktop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/appstore/ic-appstore-logged-out-non-eu-coins-responsive.svg b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-non-eu-coins-responsive.svg new file mode 100644 index 000000000000..1b146fa7db16 --- /dev/null +++ b/packages/components/src/components/icon/appstore/ic-appstore-logged-out-non-eu-coins-responsive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/appstore/ic-appstore-trustpilot-logo.svg b/packages/components/src/components/icon/appstore/ic-appstore-trustpilot-logo.svg new file mode 100644 index 000000000000..cbb1afe6f38b --- /dev/null +++ b/packages/components/src/components/icon/appstore/ic-appstore-trustpilot-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/appstore/ic-appstore-trustpilot-star.svg b/packages/components/src/components/icon/appstore/ic-appstore-trustpilot-star.svg new file mode 100644 index 000000000000..755fb0c57e47 --- /dev/null +++ b/packages/components/src/components/icon/appstore/ic-appstore-trustpilot-star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/common/ic-deriv-short-logo.svg b/packages/components/src/components/icon/common/ic-deriv-short-logo.svg new file mode 100644 index 000000000000..78c46cdb15c9 --- /dev/null +++ b/packages/components/src/components/icon/common/ic-deriv-short-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/flag/ic-flag-sw.svg b/packages/components/src/components/icon/flag/ic-flag-sw.svg new file mode 100644 index 000000000000..981529a4ecab --- /dev/null +++ b/packages/components/src/components/icon/flag/ic-flag-sw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/icons.js b/packages/components/src/components/icon/icons.js index 76bf9c82a119..8230b5fc560d 100644 --- a/packages/components/src/components/icon/icons.js +++ b/packages/components/src/components/icon/icons.js @@ -22,6 +22,10 @@ import './appstore/ic-appstore-home.svg'; import './appstore/ic-appstore-information.svg'; import './appstore/ic-appstore-link-wallet.svg'; import './appstore/ic-appstore-linked-wallets.svg'; +import './appstore/ic-appstore-logged-out-eu-coins-desktop.svg'; +import './appstore/ic-appstore-logged-out-eu-coins-responsive.svg'; +import './appstore/ic-appstore-logged-out-non-eu-coins-desktop.svg'; +import './appstore/ic-appstore-logged-out-non-eu-coins-responsive.svg'; import './appstore/ic-appstore-menu-homepage.svg'; import './appstore/ic-appstore-multipliers-trade-type.svg'; import './appstore/ic-appstore-option-trade-type.svg'; @@ -34,6 +38,8 @@ import './appstore/ic-appstore-traders-hub-home.svg'; import './appstore/ic-appstore-trading-hub-beta.svg'; import './appstore/ic-appstore-trading-hub-onboarding-dark.svg'; import './appstore/ic-appstore-trading-hub-onboarding.svg'; +import './appstore/ic-appstore-trustpilot-logo.svg'; +import './appstore/ic-appstore-trustpilot-star.svg'; import './appstore/ic-appstore-wallet-aud-light.svg'; import './appstore/ic-appstore-wallet-bitcoin-light.svg'; import './appstore/ic-appstore-wallet-card-placeholder-dark-red-line.svg'; @@ -371,6 +377,7 @@ import './common/ic-demo-reset-balance-done.svg'; import './common/ic-demo-reset-balance.svg'; import './common/ic-demo.svg'; import './common/ic-deriv-outline.svg'; +import './common/ic-deriv-short-logo.svg'; import './common/ic-deriv.svg'; import './common/ic-desktop-outline.svg'; import './common/ic-desktop.svg'; @@ -730,6 +737,7 @@ import './flag/ic-flag-pl.svg'; import './flag/ic-flag-pt.svg'; import './flag/ic-flag-ru.svg'; import './flag/ic-flag-si.svg'; +import './flag/ic-flag-sw.svg'; import './flag/ic-flag-th.svg'; import './flag/ic-flag-tr.svg'; import './flag/ic-flag-uk.svg'; @@ -738,6 +746,7 @@ import './flag/ic-flag-zh-cn.svg'; import './flag/ic-flag-zh-tw.svg'; import './mt5/ic-mt5-bvi-derived.svg'; import './mt5/ic-mt5-bvi-financial.svg'; +import './mt5/ic-mt5-bvi-standard.svg'; import './mt5/ic-mt5-bvi.svg'; import './mt5/ic-mt5-cfd-platform.svg'; import './mt5/ic-mt5-cfds.svg'; @@ -762,9 +771,13 @@ import './mt5/ic-mt5-one-password.svg'; import './mt5/ic-mt5-open-markets.svg'; import './mt5/ic-mt5-password-updated.svg'; import './mt5/ic-mt5-responsive.svg'; +import './mt5/ic-mt5-standard-financial-bvi.svg'; +import './mt5/ic-mt5-standard-financial-vanuatu.svg'; +import './mt5/ic-mt5-standard.svg'; import './mt5/ic-mt5-support.svg'; import './mt5/ic-mt5-svg-derived.svg'; import './mt5/ic-mt5-svg-financial.svg'; +import './mt5/ic-mt5-svg-standard.svg'; import './mt5/ic-mt5-swap-free-platform.svg'; import './mt5/ic-mt5-swap-free.svg'; import './mt5/ic-mt5-synthetic-dashboard.svg'; @@ -774,6 +787,7 @@ import './mt5/ic-mt5-synthetic.svg'; import './mt5/ic-mt5-trade-types.svg'; import './mt5/ic-mt5-vanuatu-derived.svg'; import './mt5/ic-mt5-vanuatu-financial.svg'; +import './mt5/ic-mt5-vanuatu-standard.svg'; import './mt5/ic-mt5-vanuatu.svg'; import './option/ic-option-accumulators.svg'; import './option/ic-option-call-put-reset.svg'; diff --git a/packages/components/src/components/icon/mt5/ic-mt5-bvi-financial.svg b/packages/components/src/components/icon/mt5/ic-mt5-bvi-financial.svg index 9fea42d4b22a..8d29b25b2035 100644 --- a/packages/components/src/components/icon/mt5/ic-mt5-bvi-financial.svg +++ b/packages/components/src/components/icon/mt5/ic-mt5-bvi-financial.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-bvi-standard.svg b/packages/components/src/components/icon/mt5/ic-mt5-bvi-standard.svg new file mode 100644 index 000000000000..59fea76b8445 --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-bvi-standard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-standard-financial-bvi.svg b/packages/components/src/components/icon/mt5/ic-mt5-standard-financial-bvi.svg new file mode 100644 index 000000000000..f2d2336a1ee2 --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-standard-financial-bvi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-standard-financial-vanuatu.svg b/packages/components/src/components/icon/mt5/ic-mt5-standard-financial-vanuatu.svg new file mode 100644 index 000000000000..65e447dca7db --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-standard-financial-vanuatu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-standard.svg b/packages/components/src/components/icon/mt5/ic-mt5-standard.svg new file mode 100644 index 000000000000..624a94855937 --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-standard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-svg-financial.svg b/packages/components/src/components/icon/mt5/ic-mt5-svg-financial.svg index 4d86b7fa95f3..70bbc984a4da 100644 --- a/packages/components/src/components/icon/mt5/ic-mt5-svg-financial.svg +++ b/packages/components/src/components/icon/mt5/ic-mt5-svg-financial.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-svg-standard.svg b/packages/components/src/components/icon/mt5/ic-mt5-svg-standard.svg new file mode 100644 index 000000000000..efaf3b4360f0 --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-svg-standard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-financial.svg b/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-financial.svg index 0217b890408c..bfe57c1c027a 100644 --- a/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-financial.svg +++ b/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-financial.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-standard.svg b/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-standard.svg new file mode 100644 index 000000000000..643638a0c056 --- /dev/null +++ b/packages/components/src/components/icon/mt5/ic-mt5-vanuatu-standard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/components/src/components/mobile-drawer/mobile-drawer.scss b/packages/components/src/components/mobile-drawer/mobile-drawer.scss index 580140d1dc98..47aff28108a5 100644 --- a/packages/components/src/components/mobile-drawer/mobile-drawer.scss +++ b/packages/components/src/components/mobile-drawer/mobile-drawer.scss @@ -238,6 +238,10 @@ color: var(--text-prominent); overflow-y: auto; overflow-x: hidden; + + &.no-padding { + padding-top: 0; + } } &__item { padding: 0 1.6rem 0 4.8rem; diff --git a/packages/components/src/components/remaining-time/remaining-time.tsx b/packages/components/src/components/remaining-time/remaining-time.tsx index 047336f831dd..293b11be71eb 100644 --- a/packages/components/src/components/remaining-time/remaining-time.tsx +++ b/packages/components/src/components/remaining-time/remaining-time.tsx @@ -6,13 +6,15 @@ import { formatDuration, getDiffDuration } from '@deriv/shared'; import { TGetCardLables } from '../types'; type TRemainingTimeProps = { + as?: React.ElementType; end_time?: number; start_time: moment.Moment; format?: string; getCardLabels: TGetCardLables; }; -const RemainingTime = ({ end_time, format, getCardLabels, start_time }: TRemainingTimeProps) => { +const RemainingTime = ({ as = 'div', end_time, format, getCardLabels, start_time }: TRemainingTimeProps) => { + const Tag = as; if (!end_time || start_time.unix() > +end_time) { return {''}; } @@ -24,7 +26,7 @@ const RemainingTime = ({ end_time, format, getCardLabels, start_time }: TRemaini } const is_zeroes = /^00:00$/.test(remaining_time); - return {!is_zeroes &&
{remaining_time}
}
; + return {!is_zeroes && {remaining_time}}; }; export default RemainingTime; diff --git a/packages/components/src/components/route-with-subroutes/route-with-subroutes.tsx b/packages/components/src/components/route-with-subroutes/route-with-subroutes.tsx index f0aa214b0ac2..53c3686f7546 100644 --- a/packages/components/src/components/route-with-subroutes/route-with-subroutes.tsx +++ b/packages/components/src/components/route-with-subroutes/route-with-subroutes.tsx @@ -66,7 +66,7 @@ const RouteWithSubRoutes = ({ if (should_redirect_login) { redirectToLogin(is_logged_in, language); } else { - result = ; + result = ; } } else { const default_subroute = routes.find(r => r.default); @@ -81,7 +81,7 @@ const RouteWithSubRoutes = ({ ) : ( - {should_redirect ? : } + {should_redirect ? : } )} diff --git a/packages/components/src/components/search-box/search-box.jsx b/packages/components/src/components/search-box/search-box.jsx index b07935d1529e..708d556ce25e 100644 --- a/packages/components/src/components/search-box/search-box.jsx +++ b/packages/components/src/components/search-box/search-box.jsx @@ -6,6 +6,7 @@ import Icon from '../icon'; import Input from '../input'; const SearchBox = ({ className, onClear, onSearch, placeholder }) => { + const typing_timer = React.useRef(null); const onSearchClear = setFieldValue => { setFieldValue('search', ''); @@ -15,9 +16,9 @@ const SearchBox = ({ className, onClear, onSearch, placeholder }) => { }; const onSearchKeyUpDown = submitForm => { - clearTimeout(typing_timer); + clearTimeout(typing_timer.current); - const typing_timer = setTimeout(() => { + typing_timer.current = setTimeout(() => { submitForm(); }, 500); }; @@ -35,6 +36,10 @@ const SearchBox = ({ className, onClear, onSearch, placeholder }) => { } }; + React.useEffect(() => { + return () => clearTimeout(typing_timer.current); + }, []); + return (
diff --git a/packages/core/babel.config.json b/packages/core/babel.config.json index 637bd634c8af..aa33e4652879 100644 --- a/packages/core/babel.config.json +++ b/packages/core/babel.config.json @@ -1,5 +1,14 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"], + "presets": [ + ["@babel/preset-env", { "targets": "defaults" }], + [ + "@babel/preset-react", + { + "runtime": "automatic" + } + ], + "@babel/preset-typescript" + ], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose": true }], diff --git a/packages/core/build/config.js b/packages/core/build/config.js index eb5bbaadad45..34a27fd373e2 100644 --- a/packages/core/build/config.js +++ b/packages/core/build/config.js @@ -99,10 +99,6 @@ const copyConfig = base => { from: path.resolve(__dirname, '../node_modules/@deriv/tradershub/dist/tradershub'), to: 'tradershub', }, - { - from: path.resolve(__dirname, '../node_modules/@deriv/account-v2/dist/account-v2'), - to: 'account-v2', - }, { from: path.resolve(__dirname, '../node_modules/@deriv/cashier-v2/dist/cashier-v2'), to: 'cashier-v2', diff --git a/packages/core/package.json b/packages/core/package.json index 5102814d20f3..5cefec7c96a0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -95,10 +95,12 @@ "dependencies": { "@babel/polyfill": "^7.4.4", "@datadog/browser-rum": "^5.11.0", - "@deriv-com/analytics": "1.4.13", - "@deriv-com/utils": "^0.0.20", + "@deriv-com/analytics": "1.5.9", + "@deriv-com/quill-tokens": "^2.0.4", + "@deriv-com/quill-ui": "^1.10.17", + "@deriv-com/translations": "^1.2.3", + "@deriv-com/utils": "^0.0.24", "@deriv/account": "^1.0.0", - "@deriv/account-v2": "^1.0.0", "@deriv/api": "^1.0.0", "@deriv/appstore": "^0.0.4", "@deriv/bot-web-ui": "^1.0.0", @@ -107,11 +109,12 @@ "@deriv/cfd": "^1.0.0", "@deriv/components": "^1.0.0", "@deriv/deriv-api": "^1.0.15", - "@deriv/deriv-charts": "^2.1.15", + "@deriv/deriv-charts": "^2.1.19", "@deriv/hooks": "^1.0.0", "@deriv/p2p": "^0.7.3", "@deriv/p2p-v2": "^1.0.0", "@deriv/quill-design": "^1.3.2", + "@deriv/quill-icons": "^1.22.10", "@deriv/reports": "^1.0.0", "@deriv/shared": "^1.0.0", "@deriv/stores": "^1.0.0", @@ -154,8 +157,6 @@ "react-tiny-popover": "^7.0.1", "react-transition-group": "4.4.2", "react-window": "^1.8.5", - "@simplewebauthn/browser": "^8.3.4", - "@simplewebauthn/typescript-types": "^8.3.4", "usehooks-ts": "^2.7.0" } } diff --git a/packages/core/src/App/AppContent.tsx b/packages/core/src/App/AppContent.tsx index 03f16b7fe60b..20c77a2e1096 100644 --- a/packages/core/src/App/AppContent.tsx +++ b/packages/core/src/App/AppContent.tsx @@ -1,17 +1,12 @@ import React from 'react'; -import Cookies from 'js-cookie'; import { useRemoteConfig } from '@deriv/api'; import { DesktopWrapper } from '@deriv/components'; -import { useFeatureFlags, useStoreWalletAccountsList } from '@deriv/hooks'; -import { getAppId, LocalStore, useIsMounted } from '@deriv/shared'; +import { useIsMounted } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; -import { getLanguage } from '@deriv/translations'; -import { Analytics } from '@deriv-com/analytics'; import { browserSupportsWebAuthn } from '@simplewebauthn/browser'; - import BinaryBotIFrame from 'Modules/BinaryBotIFrame'; +import P2PIFrame from 'Modules/P2PIFrame'; import SmartTraderIFrame from 'Modules/SmartTraderIFrame'; - import ErrorBoundary from './Components/Elements/Errors/error-boundary.jsx'; import AppToastMessages from './Containers/app-toast-messages.jsx'; import AppContents from './Containers/Layout/app-contents.jsx'; @@ -21,58 +16,31 @@ import AppModals from './Containers/Modals'; import Routes from './Containers/Routes/routes.jsx'; import Devtools from './Devtools'; import initDatadog from '../Utils/Datadog'; +import { ThemeProvider } from '@deriv-com/quill-ui'; +import { useGrowthbookIsOn } from '@deriv/hooks'; const AppContent: React.FC<{ passthrough: unknown }> = observer(({ passthrough }) => { - const { is_next_wallet_enabled } = useFeatureFlags(); - const { has_wallet } = useStoreWalletAccountsList(); const store = useStore(); + const { has_wallet } = store.client; + const [isWebPasskeysFFEnabled, isGBLoaded] = useGrowthbookIsOn({ + featureFlag: 'web_passkeys', + }); + const [isServicePasskeysFFEnabled] = useGrowthbookIsOn({ + featureFlag: 'service_passkeys', + }); const isMounted = useIsMounted(); const { data } = useRemoteConfig(isMounted()); - const { marketing_growthbook, tracking_datadog, tracking_rudderstack, passkeys } = data; + const { tracking_datadog } = data; const is_passkeys_supported = browserSupportsWebAuthn(); - const account_type = LocalStore?.get('active_loginid') - ?.match(/[a-zA-Z]+/g) - ?.join(''); - React.useEffect(() => { - if (process.env.RUDDERSTACK_KEY && tracking_rudderstack) { - const config = { - growthbookKey: marketing_growthbook ? process.env.GROWTHBOOK_CLIENT_KEY : undefined, - growthbookDecryptionKey: marketing_growthbook ? process.env.GROWTHBOOK_DECRYPTION_KEY : undefined, - rudderstackKey: process.env.RUDDERSTACK_KEY, - }; - Analytics.initialise(config); - const ppc_campaign_cookies = - Cookies.getJSON('utm_data') === 'null' - ? { - utm_source: 'no source', - utm_medium: 'no medium', - utm_campaign: 'no campaign', - utm_content: 'no content', - } - : Cookies.getJSON('utm_data'); - - Analytics.setAttributes({ - account_type: account_type === 'null' ? 'unlogged' : account_type, - app_id: String(getAppId()), - device_type: store?.ui?.is_mobile ? 'mobile' : 'desktop', - device_language: navigator?.language || 'en-EN', - user_language: getLanguage().toLowerCase(), - country: Cookies.get('clients_country') || Cookies?.getJSON('website_status')?.clients_country, - utm_source: ppc_campaign_cookies?.utm_source, - utm_medium: ppc_campaign_cookies?.utm_medium, - utm_campaign: ppc_campaign_cookies?.utm_campaign, - utm_content: ppc_campaign_cookies?.utm_content, - }); + if (isGBLoaded && isWebPasskeysFFEnabled && isServicePasskeysFFEnabled) { + store.client.setIsPasskeySupported( + is_passkeys_supported && isServicePasskeysFFEnabled && isWebPasskeysFFEnabled + ); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [data.marketing_growthbook, tracking_rudderstack]); - - React.useEffect(() => { - store.client.setIsPasskeySupported(is_passkeys_supported && passkeys); - }, [passkeys, is_passkeys_supported, store.client]); + }, [isServicePasskeysFFEnabled, isGBLoaded, isWebPasskeysFFEnabled, is_passkeys_supported]); React.useEffect(() => { initDatadog(tracking_datadog); @@ -91,7 +59,7 @@ const AppContent: React.FC<{ passthrough: unknown }> = observer(({ passthrough } }, [has_wallet, store.common, store.ui]); return ( - <> +
@@ -107,9 +75,10 @@ const AppContent: React.FC<{ passthrough: unknown }> = observer(({ passthrough } + - {is_next_wallet_enabled && } - + + ); }); diff --git a/packages/core/src/App/Components/Layout/Footer/toggle-fullscreen.jsx b/packages/core/src/App/Components/Layout/Footer/toggle-fullscreen.jsx index 1835de531903..eaa8e097db8f 100644 --- a/packages/core/src/App/Components/Layout/Footer/toggle-fullscreen.jsx +++ b/packages/core/src/App/Components/Layout/Footer/toggle-fullscreen.jsx @@ -13,16 +13,16 @@ const ToggleFullScreen = () => { fnc_exit: ['exitFullscreen', 'webkitExitFullscreen', 'mozCancelFullScreen', 'msExitFullscreen'], }; + const onFullScreen = React.useCallback(() => { + setIsFullScreen(fullscreen_map.element.some(el => document[el])); + }, [fullscreen_map.element]); + React.useEffect(() => { fullscreen_map.event.forEach(event => { document.addEventListener(event, onFullScreen, false); }); }, [fullscreen_map.event, onFullScreen]); - const onFullScreen = React.useCallback(() => { - setIsFullScreen(fullscreen_map.element.some(el => document[el])); - }, [fullscreen_map.element]); - const toggleFullScreen = e => { e.stopPropagation(); diff --git a/packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx b/packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx index e0ab5a84eab5..82395a4cb7f0 100644 --- a/packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx +++ b/packages/core/src/App/Components/Layout/Header/Components/ToggleMenu/menu-title.tsx @@ -1,14 +1,13 @@ import React from 'react'; -import { useStoreWalletAccountsList } from '@deriv/hooks'; import { observer, useStore } from '@deriv/stores'; import { Icon, Text } from '@deriv/components'; import { localize, Localize } from '@deriv/translations'; const MenuTitle = observer(() => { - const { common, ui } = useStore(); + const { client, common, ui } = useStore(); + const { has_wallet } = client; const { current_language } = common; const { is_mobile_language_menu_open, setMobileLanguageMenuOpen } = ui; - const { has_wallet } = useStoreWalletAccountsList(); return ( diff --git a/packages/core/src/App/Components/Layout/Header/__tests__/toggle-menu-drawer.spec.jsx b/packages/core/src/App/Components/Layout/Header/__tests__/toggle-menu-drawer.spec.jsx index d5efb52b2d39..4131731341c0 100644 --- a/packages/core/src/App/Components/Layout/Header/__tests__/toggle-menu-drawer.spec.jsx +++ b/packages/core/src/App/Components/Layout/Header/__tests__/toggle-menu-drawer.spec.jsx @@ -12,10 +12,6 @@ jest.mock('@deriv/components', () => { MobileDrawer, }; }); -jest.mock('@deriv/hooks', () => ({ - ...jest.requireActual('@deriv/hooks'), - useFeatureFlags: jest.fn(() => ({ is_next_wallet_enabled: true })), -})); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useLocation: jest.fn(() => ({ pathname: '/appstore/traders-hub' })), diff --git a/packages/core/src/App/Components/Layout/Header/menu-link.tsx b/packages/core/src/App/Components/Layout/Header/menu-link.tsx index 570024cb85c8..4aa7f32b9dde 100644 --- a/packages/core/src/App/Components/Layout/Header/menu-link.tsx +++ b/packages/core/src/App/Components/Layout/Header/menu-link.tsx @@ -85,10 +85,8 @@ const MenuLink = observer( } if (is_cashier_link && is_virtual && !has_any_real_account) { - const toggle_modal_routes = window.location.pathname === routes.root || traders_hub_path; - const handleClickCashier = () => { - if (toggle_modal_routes) { + if (traders_hub_path) { toggleReadyToDepositModal(); } onClickLink?.(); @@ -136,7 +134,7 @@ const MenuLink = observer( className={is_trade_text ? '' : 'header__menu-mobile-link-text'} as='h3' size='xs' - weight={window.location.pathname === '/' && is_trade_text ? 'bold' : undefined} + weight={window.location.pathname === routes.trade && is_trade_text ? 'bold' : undefined} > {text} @@ -152,7 +150,6 @@ const MenuLink = observer( 'header__menu-mobile-link--disabled': is_disabled, 'header__menu-mobile-link--active': is_active, })} - active_class='header__menu-mobile-link--active' onClick={onClickLink} data-testid={data_testid} > @@ -161,7 +158,7 @@ const MenuLink = observer( className={is_trade_text ? '' : 'header__menu-mobile-link-text'} as='h3' size='xs' - weight={window.location.pathname === '/' && is_trade_text ? 'bold' : undefined} + weight={window.location.pathname === routes.trade && is_trade_text ? 'bold' : undefined} > {text} diff --git a/packages/core/src/App/Components/Layout/Header/menu-links.jsx b/packages/core/src/App/Components/Layout/Header/menu-links.jsx index 2765027bd79d..1bc5efdcda79 100644 --- a/packages/core/src/App/Components/Layout/Header/menu-links.jsx +++ b/packages/core/src/App/Components/Layout/Header/menu-links.jsx @@ -5,7 +5,7 @@ import { BinaryLink } from '../../Routes'; import { observer, useStore } from '@deriv/stores'; import { routes, startPerformanceEventTimer } from '@deriv/shared'; import { localize } from '@deriv/translations'; -import { useP2PNotificationCount, useIsRealAccountNeededForCashier, useFeatureFlags } from '@deriv/hooks'; +import { useP2PNotificationCount, useIsRealAccountNeededForCashier } from '@deriv/hooks'; import './menu-links.scss'; import { useHistory } from 'react-router'; @@ -46,9 +46,7 @@ const CashierTab = observer(() => { const history = useHistory(); const toggle_modal_routes = - window.location.pathname === routes.root || - window.location.pathname === routes.traders_hub || - window.location.pathname === routes.bot; + window.location.pathname === routes.traders_hub || window.location.pathname === routes.bot; const toggleModal = () => { if (toggle_modal_routes && !has_any_real_account) { @@ -91,16 +89,15 @@ const CashierTab = observer(() => { const MenuLinks = observer(({ is_traders_hub_routes = false }) => { const { i18n } = useTranslation(); const { client, ui } = useStore(); - const { is_logged_in } = client; + const { has_wallet, is_logged_in } = client; const { is_mobile } = ui; - const { is_next_wallet_enabled } = useFeatureFlags(); if (!is_logged_in) return <>; return (
{!is_traders_hub_routes && } - {!is_mobile && !is_next_wallet_enabled && } + {!has_wallet && !is_mobile && }
); }); diff --git a/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx b/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx index ab18f28f18b7..6b1fe2c9a83c 100644 --- a/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx +++ b/packages/core/src/App/Components/Layout/Header/toggle-menu-drawer.jsx @@ -7,12 +7,10 @@ import { Div100vhContainer, Icon, MobileDrawer, ToggleSwitch } from '@deriv/comp import { useAccountTransferVisible, useAuthorize, - useFeatureFlags, useIsP2PEnabled, useOnrampVisible, usePaymentAgentTransferVisible, useP2PSettings, - useStoreWalletAccountsList, } from '@deriv/hooks'; import { getOSNameWithUAParser, getStaticUrl, routes, useIsMounted, whatsapp_url } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; @@ -40,6 +38,7 @@ const ToggleMenuDrawer = observer(({ platform_config }) => { } = ui; const { account_status, + has_wallet, is_logged_in, is_logging_in, is_virtual, @@ -62,14 +61,14 @@ const ToggleMenuDrawer = observer(({ platform_config }) => { const is_onramp_visible = useOnrampVisible(); const { data: is_payment_agent_transfer_visible } = usePaymentAgentTransferVisible(); const { is_p2p_enabled } = useIsP2PEnabled(); - const { is_next_wallet_enabled } = useFeatureFlags(); - const { has_wallet } = useStoreWalletAccountsList(); const { pathname: route } = useLocation(); + const location = useLocation(); const is_trading_hub_category = - route.startsWith(routes.traders_hub) || route.startsWith(routes.cashier) || route.startsWith(routes.account); - const is_wallets_category = route.startsWith(routes.wallets); + route === routes.traders_hub || route.startsWith(routes.cashier) || route.startsWith(routes.account); + + const should_hide_platform_switcher = location.pathname === routes.traders_hub; const isMounted = useIsMounted(); const { data } = useRemoteConfig(isMounted()); @@ -90,7 +89,7 @@ const ToggleMenuDrawer = observer(({ platform_config }) => { } = useP2PSettings(); let TradersHubIcon; - if (is_next_wallet_enabled) { + if (has_wallet) { TradersHubIcon = 'IcAppstoreTradersHubHomeUpdated'; } else if (is_dark_mode) { TradersHubIcon = 'IcAppstoreHomeDark'; @@ -109,14 +108,12 @@ const ToggleMenuDrawer = observer(({ platform_config }) => { const routes_config = getRoutesConfig(); let primary_routes = []; - const location = window.location.pathname; - - if (location === routes.traders_hub || is_trading_hub_category) { - primary_routes = [routes.account, routes.cashier]; - } else if (location === routes.wallets || is_next_wallet_enabled) { - primary_routes = [routes.reports, routes.account]; + if (is_trading_hub_category) { + primary_routes = has_wallet ? [routes.reports, routes.account] : [routes.account, routes.cashier]; } else { - primary_routes = [routes.reports, routes.account, routes.cashier]; + primary_routes = has_wallet + ? [routes.reports, routes.account] + : [routes.reports, routes.account, routes.cashier]; } setPrimaryRoutesConfig(getFilteredRoutesConfig(routes_config, primary_routes)); }; @@ -129,8 +126,8 @@ const ToggleMenuDrawer = observer(({ platform_config }) => { }, [ account_status, should_allow_authentication, + has_wallet, is_trading_hub_category, - is_next_wallet_enabled, is_mobile, is_passkey_supported, is_p2p_enabled, @@ -316,7 +313,7 @@ const ToggleMenuDrawer = observer(({ platform_config }) => {
- {!is_trading_hub_category && !is_wallets_category && ( + {!should_hide_platform_switcher && ( { /> )} - + +
+ + + {is_logged_in && ( )} - {!is_trading_hub_category && !is_wallets_category && ( + {route !== routes.traders_hub && ( )} @@ -385,7 +389,6 @@ const ToggleMenuDrawer = observer(({ platform_config }) => {
)} - {HelpCentreRoute()} {is_logged_in && ( diff --git a/packages/core/src/App/Components/Routes/route-with-sub-routes.jsx b/packages/core/src/App/Components/Routes/route-with-sub-routes.jsx index 1214b0589cf8..fa1afccc06f7 100644 --- a/packages/core/src/App/Components/Routes/route-with-sub-routes.jsx +++ b/packages/core/src/App/Components/Routes/route-with-sub-routes.jsx @@ -9,7 +9,7 @@ import { useFeatureFlags } from '@deriv/hooks'; const RouteWithSubRoutes = observer(route => { const { common } = useStore(); - const { is_next_account_enabled, is_next_cashier_enabled, is_next_tradershub_enabled } = useFeatureFlags(); + const { is_next_cashier_enabled, is_next_tradershub_enabled } = useFeatureFlags(); const { checkAppId } = common; const validateRoute = pathname => { if (pathname.startsWith('/cashier') && !pathname.includes('p2p') && !!route.routes) { @@ -22,8 +22,6 @@ const RouteWithSubRoutes = observer(route => { : cashier_subroutes?.routes.find(({ path }) => pathname === path); return route.path === pathname || !!p2p_subroutes; - } else if (pathname.includes(routes.account_v2) && !is_next_account_enabled) { - return false; } else if (pathname.includes(routes.cashier_v2) && !is_next_cashier_enabled) { return false; } else if ( diff --git a/packages/core/src/App/Constants/routes-config.js b/packages/core/src/App/Constants/routes-config.js index 06a047a4ffb9..dff74cfa965e 100644 --- a/packages/core/src/App/Constants/routes-config.js +++ b/packages/core/src/App/Constants/routes-config.js @@ -4,6 +4,7 @@ import { makeLazyLoader, routes, moduleLoader } from '@deriv/shared'; import { Loading } from '@deriv/components'; import { localize } from '@deriv/translations'; import Redirect from 'App/Containers/Redirect'; +import RootComponent from 'App/Containers/RootComponent'; import Endpoint from 'Modules/Endpoint'; const CFDCompareAccounts = React.lazy(() => @@ -53,20 +54,6 @@ const Bot = React.lazy(() => }) ); -const AppStore = React.lazy(() => - moduleLoader(() => { - // eslint-disable-next-line import/no-unresolved - return import(/* webpackChunkName: "appstore" */ '@deriv/appstore'); - }) -); - -const Wallets = React.lazy(() => - moduleLoader(() => { - // eslint-disable-next-line import/no-unresolved - return import(/* webpackChunkName: "wallets" */ '@deriv/wallets'); - }) -); - const TradersHub = React.lazy(() => moduleLoader(() => { // eslint-disable-next-line import/no-unresolved @@ -88,13 +75,6 @@ const P2P_V2 = React.lazy(() => }) ); -const Account_V2 = React.lazy(() => - moduleLoader(() => { - // eslint-disable-next-line import/no-unresolved - return import(/* webpackChunkName: "account-v2" */ '@deriv/account-v2'); - }) -); - const Cashier_V2 = React.lazy(() => moduleLoader(() => { // eslint-disable-next-line import/no-unresolved @@ -102,6 +82,10 @@ const Cashier_V2 = React.lazy(() => }) ); +const RedirectToNewTradersHub = () => { + return ; +}; + const getModules = () => { const modules = [ { @@ -283,18 +267,6 @@ const getModules = () => { }, ], }, - { - path: routes.traders_hub, - component: AppStore, - is_authenticated: true, - getTitle: () => localize("Trader's Hub"), - }, - { - path: routes.wallets, - component: Wallets, - is_authenticated: true, - getTitle: () => localize('Wallets'), - }, { path: routes.cashier_p2p_v2, component: P2P_V2, @@ -307,37 +279,12 @@ const getModules = () => { is_authenticated: true, getTitle: () => localize('Trader’s Hub V2'), }, - { - path: routes.account_v2, - component: Account_V2, - is_authenticated: true, - getTitle: () => localize('Account V2'), - }, { path: routes.cashier_v2, component: Cashier_V2, is_authenticated: true, getTitle: () => localize('Cashier'), }, - { - path: routes.onboarding, - component: AppStore, - is_authenticated: false, - getTitle: () => localize('Appstore'), - routes: [ - { - path: routes.traders_hub, - component: AppStore, - getTitle: () => localize("Trader's Hub"), - }, - { - path: routes.onboarding, - component: AppStore, - is_authenticated: false, - getTitle: () => localize('Onboarding'), - }, - ], - }, { path: routes.cashier, component: Cashier, @@ -431,18 +378,27 @@ const getModules = () => { ], }, { - path: routes.root, + path: routes.trade, component: Trader, getTitle: () => localize('Trader'), - routes: [ - { - path: routes.contract, - component: Trader, - getTitle: () => localize('Contract Details'), - is_authenticated: true, - }, - { path: routes.error404, component: Trader, getTitle: () => localize('Error 404') }, - ], + }, + { + path: routes.contract, + component: Trader, + getTitle: () => localize('Contract Details'), + is_authenticated: true, + }, + { + path: routes.old_traders_hub, + component: RedirectToNewTradersHub, + is_authenticated: false, + getTitle: () => localize("Trader's Hub"), + }, + { + path: routes.traders_hub, + component: RootComponent, + is_authenticated: false, + getTitle: () => localize("Trader's Hub"), }, ]; @@ -457,7 +413,7 @@ const lazyLoadComplaintsPolicy = makeLazyLoader( // Order matters // TODO: search tag: test-route-parent-info -> Enable test for getting route parent info when there are nested routes const initRoutesConfig = () => [ - { path: routes.index, component: RouterRedirect, getTitle: () => '', to: routes.root }, + { path: routes.index, component: RouterRedirect, getTitle: () => '', to: routes.traders_hub }, { path: routes.endpoint, component: Endpoint, getTitle: () => 'Endpoint' }, // doesn't need localization as it's for internal use { path: routes.redirect, component: Redirect, getTitle: () => localize('Redirect') }, { diff --git a/packages/core/src/App/Containers/AccountSignupModal/account-signup-modal.jsx b/packages/core/src/App/Containers/AccountSignupModal/account-signup-modal.jsx index 88835479cfa1..1893012783d5 100644 --- a/packages/core/src/App/Containers/AccountSignupModal/account-signup-modal.jsx +++ b/packages/core/src/App/Containers/AccountSignupModal/account-signup-modal.jsx @@ -126,8 +126,8 @@ const AccountSignup = ({ error_message: error, }); } else { - isModalVisible(false); setIsFromSignupAccount(true); + isModalVisible(false); SessionStore.remove('signup_query_param'); enableApp(); diff --git a/packages/core/src/App/Containers/AccountSwitcher/helpers/account-helper.js b/packages/core/src/App/Containers/AccountSwitcher/helpers/account-helper.js index c9ac72755250..dafcf46169eb 100644 --- a/packages/core/src/App/Containers/AccountSwitcher/helpers/account-helper.js +++ b/packages/core/src/App/Containers/AccountSwitcher/helpers/account-helper.js @@ -52,7 +52,6 @@ export const getCFDConfig = ( mt5_trading_servers, platform, is_eu, - trading_platform_available_accounts, getIsEligibleForMoreAccounts ) => { const cfd_config = []; diff --git a/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet-mobile.tsx b/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet-mobile.tsx index 7320d0e593e1..d3c7810e6894 100644 --- a/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet-mobile.tsx +++ b/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet-mobile.tsx @@ -26,7 +26,7 @@ export const AccountSwitcherWalletMobile = observer(({ is_visible, toggle, login const handleTradersHubRedirect = () => { closeAccountsDialog(); - history.push(routes.wallets); + history.push(routes.traders_hub); }; const handleManageFundsRedirect = () => { diff --git a/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet.tsx b/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet.tsx index 8ee7106952ce..8ed4182b10c1 100644 --- a/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet.tsx +++ b/packages/core/src/App/Containers/AccountSwitcherWallet/account-switcher-wallet.tsx @@ -40,7 +40,7 @@ export const AccountSwitcherWallet = observer(({ is_visible, toggle }: TAccountS const handleTradersHubRedirect = async () => { closeAccountsDialog(); - history.push(routes.wallets); + history.push(routes.traders_hub); }; return ( diff --git a/packages/core/src/App/Containers/Layout/default-footer.jsx b/packages/core/src/App/Containers/Layout/default-footer.jsx index 54dcfa97634e..7e2bd3773d8f 100644 --- a/packages/core/src/App/Containers/Layout/default-footer.jsx +++ b/packages/core/src/App/Containers/Layout/default-footer.jsx @@ -15,7 +15,6 @@ import NetworkStatus, { import LiveChat from 'App/Components/Elements/LiveChat'; import WhatsApp from 'App/Components/Elements/WhatsApp/index.ts'; import ServerTime from '../server-time.jsx'; -import { useStoreWalletAccountsList } from '@deriv/hooks'; import { observer, useStore } from '@deriv/stores'; import { useRemoteConfig } from '@deriv/api'; import { useIsMounted } from '@deriv/shared'; @@ -35,7 +34,7 @@ const FooterExtensionRenderer = (footer_extension, idx) => { const Footer = observer(() => { const { client, common, ui, traders_hub } = useStore(); - const { is_logged_in, landing_company_shortcode, is_eu, is_virtual } = client; + const { has_wallet, is_logged_in, landing_company_shortcode, is_eu, is_virtual } = client; const { current_language } = common; const { enableApp, @@ -53,7 +52,6 @@ const Footer = observer(() => { const { data } = useRemoteConfig(isMounted()); const { cs_chat_livechat, cs_chat_whatsapp } = data; const { show_eu_related_content } = traders_hub; - const { has_wallet } = useStoreWalletAccountsList(); let footer_extensions_left = []; let footer_extensions_right = []; diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/default-header.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/default-header.spec.tsx index 7e01b972ae7f..5b0bdd318147 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/default-header.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/default-header.spec.tsx @@ -1,7 +1,9 @@ import React from 'react'; import { StoreProvider, mockStore } from '@deriv/stores'; import { render, screen } from '@testing-library/react'; +import { useLocation } from 'react-router-dom'; import DefaultHeader from '../default-header'; +import { routes } from '@deriv/shared'; jest.mock('App/Components/Layout/Header', () => ({ MenuLinks: jest.fn(() =>
Mocked Menu Links
), @@ -11,7 +13,13 @@ jest.mock('App/Containers/RealAccountSignup', () => jest.fn(() =>
Mocked Re jest.mock('App/Components/Layout/Header/toggle-menu-drawer.jsx', () => jest.fn(() =>
Mocked Toggle Menu Drawer
) ); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn().mockReturnValue({ pathname: '' }), +})); jest.mock('../header-account-actions', () => jest.fn(() =>
Mocked Header Account Action
)); +jest.mock('../deriv-short-logo', () => jest.fn(() =>
Deriv Short Logo
)); describe('DefaultHeader', () => { const mock_store = mockStore({ ui: { is_desktop: true, is_real_acc_signup_on: true } }); @@ -22,7 +30,10 @@ describe('DefaultHeader', () => { ); - it('should render Platform switcher, Menu Links, Account action and Real Account SignUp components, in Desktop view', () => { + it('should render Platform switcher, Menu Links, Account action and Real Account SignUp components, in Desktop view for non-tradershub route', () => { + (useLocation as jest.Mock).mockReturnValue({ + pathname: routes.bot, + }); renderComponent(); expect(screen.getByText('Mocked Platform Switcher')).toBeInTheDocument(); expect(screen.getByText('Mocked Menu Links')).toBeInTheDocument(); @@ -30,6 +41,17 @@ describe('DefaultHeader', () => { expect(screen.getByText('Mocked Real Account SignUp')).toBeInTheDocument(); }); + it('should render Menu Links, Account action and Real Account SignUp components, in Desktop view for tradershub route', () => { + (useLocation as jest.Mock).mockReturnValue({ + pathname: routes.traders_hub, + }); + renderComponent(); + expect(screen.queryByText('Mocked Platform Switcher')).not.toBeInTheDocument(); + expect(screen.getByText('Mocked Menu Links')).toBeInTheDocument(); + expect(screen.getByText('Mocked Header Account Action')).toBeInTheDocument(); + expect(screen.getByText('Mocked Real Account SignUp')).toBeInTheDocument(); + }); + it('should render Toggle Menu Drawer, Menu Links, Account action and Real Account SignUp components, in Mobile view', () => { renderComponent( mockStore({ diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/default-mobile-links.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/default-mobile-links.spec.tsx index 6ac75f982491..ffa7bf63cce1 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/default-mobile-links.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/default-mobile-links.spec.tsx @@ -1,8 +1,17 @@ import React from 'react'; +import { StoreProvider, mockStore } from '@deriv/stores'; +import { BrowserHistory, createBrowserHistory } from 'history'; +import { Router } from 'react-router'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { routes } from '@deriv/shared'; import DefaultMobileLinks from '../default-mobile-links'; +import { useIsRealAccountNeededForCashier } from '@deriv/hooks'; +jest.mock('@deriv/hooks', () => ({ + ...jest.requireActual('@deriv/hooks'), + useIsRealAccountNeededForCashier: jest.fn(() => false), +})); jest.mock('App/Components/Routes', () => ({ BinaryLink: jest.fn(() =>
MockedBinaryLink to Account Settings
), })); @@ -10,37 +19,70 @@ jest.mock('../show-notifications', () => jest.fn(() =>
MockedShowNotifications
) ); -jest.mock('../traders-hub-onboarding', () => - jest.fn(() =>
MockedTradersHubOnboarding
) -); - -jest.mock('@deriv/hooks', () => ({ - useFeatureFlags: () => ({ - is_next_wallet_enabled: false, - }), -})); +jest.mock('../traders-hub-onboarding', () => jest.fn(() =>
MockedTradersHubOnboarding
)); describe('DefaultMobileLinks', () => { - const mock_props: React.ComponentProps = { - handleClickCashier: jest.fn(), + let history: BrowserHistory, mock_store: ReturnType; + + beforeEach(() => { + mock_store = mockStore({ + client: { has_wallet: false, has_any_real_account: true, is_virtual: false }, + ui: { + toggleNeedRealAccountForCashierModal: jest.fn(), + toggleReadyToDepositModal: jest.fn(), + }, + }); + history = createBrowserHistory(); + }); + + const wrapper = ({ children }: { children: React.ReactNode }) => { + return ( + + {children} + + ); }; - it('should render "DefaultMobileLinks" with Onboarding, Notifications & link to Account Settings', () => { - render(); - expect(screen.getByTestId('dt_traders_hub_onboarding')).toBeInTheDocument(); + it('should render "DefaultMobileLinks" with Onboarding, Notifications & link to Account Settings for wallet', () => { + mock_store.client.has_wallet = true; + render(, { wrapper }); expect(screen.getByText('MockedTradersHubOnboarding')).toBeInTheDocument(); expect(screen.getByText('MockedShowNotifications')).toBeInTheDocument(); expect(screen.getByText('MockedBinaryLink to Account Settings')).toBeInTheDocument(); }); + it('should render "DefaultMobileLinks" with Notifications & link to Account Settings for non wallet path', () => { + render(, { wrapper }); + expect(screen.queryByText('MockedTradersHubOnboarding')).not.toBeInTheDocument(); + expect(screen.getByText('MockedShowNotifications')).toBeInTheDocument(); + expect(screen.getByText('MockedBinaryLink to Account Settings')).toBeInTheDocument(); + }); it('should display the cashier button', () => { - render(); + render(, { wrapper }); expect(screen.getByRole('button', { name: 'Cashier' })).toBeInTheDocument(); }); - it('should fire the "handleClickCashier" event on clicking the button', () => { - render(); - userEvent.click(screen.getByRole('button', { name: 'Cashier' })); - expect(mock_props.handleClickCashier).toHaveBeenCalledTimes(1); + it('should trigger `toggleReadyToDepositModal` if user does not have any real account and active account is virtual', () => { + mock_store.client.has_any_real_account = false; + mock_store.client.is_virtual = true; + render(, { wrapper }); + const cashierButton = screen.getByRole('button', { name: 'Cashier' }); + userEvent.click(cashierButton); + expect(mock_store.ui.toggleReadyToDepositModal).toHaveBeenCalledTimes(1); + }); + + it('should trigger `toggleNeedRealAccountForCashierModal` if user does not have any real regulated account', () => { + (useIsRealAccountNeededForCashier as jest.Mock).mockReturnValueOnce(true); + render(, { wrapper }); + const cashierButton = screen.getByRole('button', { name: 'Cashier' }); + userEvent.click(cashierButton); + expect(mock_store.ui.toggleNeedRealAccountForCashierModal).toHaveBeenCalledTimes(1); + }); + + it('should navigate to `/cashier/deposit` if user has real account', () => { + render(, { wrapper }); + const cashierButton = screen.getByRole('button', { name: 'Cashier' }); + userEvent.click(cashierButton); + expect(history.location.pathname).toBe(routes.cashier_deposit); }); }); diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-contract-details-header.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-contract-details-header.spec.tsx new file mode 100644 index 000000000000..c0117b11e42a --- /dev/null +++ b/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-contract-details-header.spec.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; +import DTraderContractDetailsHeader from '../dtrader-v2-contract-detail-header'; +import userEvent from '@testing-library/user-event'; + +jest.mock('@deriv-com/quill-ui', () => ({ + Text: () =>
Contract Details
, +})); + +describe('DTraderV2Header', () => { + test('renders the header with localized text and an icon', () => { + const history = createMemoryHistory(); + render( + + + + ); + + expect(screen.getByText('Contract Details')).toBeInTheDocument(); + + const icon = screen.getByTestId('arrow'); + expect(icon).toBeInTheDocument(); + }); + + test('clicking the back arrow calls history.goBack', () => { + const history = createMemoryHistory(); + + history.goBack = jest.fn(); + + render( + + + + ); + + userEvent.click(screen.getByTestId('arrow')); + + expect(history.goBack).toHaveBeenCalled(); + }); +}); diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-header.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-header.spec.tsx index 134e1c10dc1e..dab4e5c0895f 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-header.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/dtrader-header.spec.tsx @@ -13,6 +13,7 @@ jest.mock('App/Components/Layout/Header/toggle-menu-drawer.jsx', () => ); jest.mock('../header-account-actions', () => jest.fn(() =>
Mocked Header Account Action
)); jest.mock('../traders-hub-home-button', () => jest.fn(() =>
Mocked Traders Home Button
)); +jest.mock('../deriv-short-logo', () => jest.fn(() =>
Deriv Short Logo
)); describe('DTraderHeader', () => { const mock_store = mockStore({ ui: { is_desktop: true, is_real_acc_signup_on: true } }); diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/header.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/header.spec.tsx index 0ba1db00c2a6..4490425da3e0 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/header.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/header.spec.tsx @@ -2,12 +2,13 @@ import React from 'react'; import { useLocation } from 'react-router-dom'; import { StoreProvider, mockStore } from '@deriv/stores'; import { render, screen } from '@testing-library/react'; +import { routes } from '@deriv/shared'; import Header from '../header'; jest.mock('@deriv/hooks', () => ({ ...jest.requireActual('@deriv/hooks'), - useFeatureFlags: jest.fn(() => ({ is_next_wallet_enabled: false })), useStoreWalletAccountsList: jest.fn(() => ({ data: [], has_wallet: false })), + useFeatureFlags: jest.fn(() => ({})), })); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -33,7 +34,7 @@ describe('Header', () => { it('should render the "TradersHubHeader" component if user is logged in and in traders hub route', async () => { (useLocation as jest.Mock).mockReturnValue({ - pathname: '/appstore/traders-hub', + pathname: routes.traders_hub, }); renderComponent(); expect(await screen.findByTestId('dt_traders_hub_header')).toBeInTheDocument(); @@ -42,7 +43,7 @@ describe('Header', () => { it('should render the "DTraderHeader" component if user is logged in and not in the traders hub route', async () => { (useLocation as jest.Mock).mockReturnValue({ - pathname: '/', + pathname: routes.trade, }); renderComponent(); expect(await screen.findByTestId('dt_dtrader_header')).toBeInTheDocument(); diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-header.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-header.spec.tsx index 7f00e1e8e916..88989c359593 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-header.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-header.spec.tsx @@ -28,11 +28,10 @@ jest.mock('../../../CurrencySelectionModal', () => jest.fn(() =>
MockedCurr jest.mock('../show-notifications', () => jest.fn(() =>
MockedShowNotifications
)); jest.mock('@deriv/hooks', () => ({ - useFeatureFlags: () => ({ - is_next_wallet_enabled: false, - }), - useIsRealAccountNeededForCashier: () => false, - useHasSetCurrency: () => true, + ...jest.requireActual('@deriv/hooks'), + useFeatureFlags: jest.fn(() => ({})), + useHasSetCurrency: jest.fn(() => true), + useIsRealAccountNeededForCashier: jest.fn(() => false), })); describe('TradersHubHeader', () => { @@ -43,11 +42,6 @@ describe('TradersHubHeader', () => { mock_store ?? mockStore({ ui: { is_desktop: true }, - feature_flags: { - data: { - next_wallet: true, - }, - }, traders_hub: { modal_data: { active_modal: 'currency_selection', @@ -76,11 +70,6 @@ describe('TradersHubHeader', () => { expect(await screen.findByText('MockedRealAccountSignup')).toBeInTheDocument(); }); - it('should render "View tutorial" option in the header', () => { - renderComponent(); - expect(screen.getByText('View tutorial')).toBeInTheDocument(); - }); - it('should render "Notifications" option in the header', () => { renderComponent(); expect(screen.getByText('MockedShowNotifications')).toBeInTheDocument(); diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-home-button.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-home-button.spec.tsx index b0cd400a98d2..f20b872c4e8b 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-home-button.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-home-button.spec.tsx @@ -3,24 +3,20 @@ import { StoreProvider, mockStore } from '@deriv/stores'; import { render, screen } from '@testing-library/react'; import TradersHubHomeButton from '../traders-hub-home-button'; -jest.mock('react-router', () => ({ - ...jest.requireActual('react-router'), - useHistory: () => ({ history: {} }), - useLocation: () => ({ pathname: '/appstore/traders-hub' }), -})); +jest.mock('react-router', () => { + return { + ...jest.requireActual('react-router'), + useHistory: () => ({ history: {} }), + useLocation: () => ({ pathname: '/' }), + }; +}); describe('TradersHubHomeButton', () => { + const mock_store = mockStore({}); + it("should display the text Trader's Hub in the header", () => { render( - + ); @@ -29,15 +25,7 @@ describe('TradersHubHomeButton', () => { it('should have the --active class if in traders hub route', () => { render( - + ); diff --git a/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-onboarding.spec.tsx b/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-onboarding.spec.tsx index f7cdb0d44e17..aa9f9a0795fc 100644 --- a/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-onboarding.spec.tsx +++ b/packages/core/src/App/Containers/Layout/header/__tests__/traders-hub-onboarding.spec.tsx @@ -8,13 +8,6 @@ import TradersHubOnboarding from '../traders-hub-onboarding'; import { routes } from '@deriv/shared'; import { TCoreStores } from '@deriv/stores/types'; -jest.mock('@deriv/hooks', () => ({ - ...jest.requireActual('@deriv/hooks'), - useFeatureFlags: jest.fn(() => ({ - is_next_wallet_enabled: false, - })), -})); - describe('TradersHubOnboarding', () => { const onboarding_icon_testid = 'dt_traders_hub_onboarding_icon'; const popover_wrapper_testid = 'dt_popover_wrapper'; diff --git a/packages/core/src/App/Containers/Layout/header/default-header.tsx b/packages/core/src/App/Containers/Layout/header/default-header.tsx index 14c250c7c55f..539feee59ea1 100644 --- a/packages/core/src/App/Containers/Layout/header/default-header.tsx +++ b/packages/core/src/App/Containers/Layout/header/default-header.tsx @@ -9,8 +9,9 @@ import RealAccountSignup from 'App/Containers/RealAccountSignup'; import SetAccountCurrencyModal from 'App/Containers/SetAccountCurrencyModal'; import ToggleMenuDrawer from 'App/Components/Layout/Header/toggle-menu-drawer.jsx'; import platform_config from 'App/Constants/platform-config'; -import { useHistory } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import HeaderAccountActions from './header-account-actions'; +import DerivShortLogo from './deriv-short-logo'; const DefaultHeader = observer(() => { const { client, common, notifications, traders_hub, ui } = useStore(); @@ -37,6 +38,8 @@ const DefaultHeader = observer(() => { } = ui; const history = useHistory(); + const location = useLocation(); + const should_hide_platform_switcher = location.pathname === routes.traders_hub; const addUpdateNotification = () => addNotificationMessage(client_notifications?.new_version_available); const removeUpdateNotification = React.useCallback( @@ -78,23 +81,29 @@ const DefaultHeader = observer(() => { >
- {!is_mobile && ( - - )} - {is_mobile && ( + {is_mobile ? ( {header_extension && is_logged_in && (
{header_extension}
)} + +
+ ) : ( + + +
+ {!should_hide_platform_switcher && ( + + )} )} diff --git a/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx b/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx index 814a1f0a972e..d80036a832bd 100644 --- a/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx +++ b/packages/core/src/App/Containers/Layout/header/default-mobile-links.tsx @@ -1,33 +1,53 @@ import React from 'react'; - +import { useHistory } from 'react-router-dom'; import { Button, Icon } from '@deriv/components'; +import { useIsRealAccountNeededForCashier } from '@deriv/hooks'; import { routes } from '@deriv/shared'; +import { useStore } from '@deriv/stores'; import { Localize } from '@deriv/translations'; - import { BinaryLink } from 'App/Components/Routes'; - import ShowNotifications from './show-notifications'; import TradersHubOnboarding from './traders-hub-onboarding'; -import { useFeatureFlags } from '@deriv/hooks'; -type TDefaultMobileLinks = { - handleClickCashier: () => void; -}; +const DefaultMobileLinks = React.memo(() => { + const { client, ui } = useStore(); + const { has_any_real_account, has_wallet, is_virtual } = client; + const { toggleNeedRealAccountForCashierModal, toggleReadyToDepositModal } = ui; + + const history = useHistory(); + + const real_account_needed_for_cashier = useIsRealAccountNeededForCashier(); + + const toggleModal = () => { + if (!has_any_real_account) { + toggleReadyToDepositModal(); + } else if (history.location.pathname === routes.traders_hub) { + toggleNeedRealAccountForCashierModal(); + } + }; + + const handleClickCashier = () => { + if ((!has_any_real_account && is_virtual) || real_account_needed_for_cashier) { + toggleModal(); + } else { + history.push(routes.cashier_deposit); + } + }; -const DefaultMobileLinks = React.memo(({ handleClickCashier }: TDefaultMobileLinks) => { - const { is_next_wallet_enabled } = useFeatureFlags(); return ( -
- -
+ {has_wallet && ( +
+ +
+ )}
- {!is_next_wallet_enabled && ( + {!has_wallet && (
); @@ -102,7 +100,7 @@ const ErrorCTA = ({ code, onConfirm }) => { case 'CurrencyTypeNotAllowed': return ; case 'DuplicateAccount': - return null; + return window.LC_API.open_chat_window()} />; case 'InputValidationFailed': case 'PoBoxInAddress': case 'InvalidPhone': @@ -141,7 +139,9 @@ const SignupErrorContent = ({ message, code, onConfirm, className, error_field = - +
+ +
); }; diff --git a/packages/core/src/App/Containers/Redirect/redirect.jsx b/packages/core/src/App/Containers/Redirect/redirect.jsx index d32e10499ca0..c44ccef4b683 100644 --- a/packages/core/src/App/Containers/Redirect/redirect.jsx +++ b/packages/core/src/App/Containers/Redirect/redirect.jsx @@ -1,6 +1,5 @@ import PropTypes from 'prop-types'; import { withRouter, useHistory } from 'react-router-dom'; -import { useStoreWalletAccountsList } from '@deriv/hooks'; import { loginUrl, routes, redirectToLogin, SessionStore } from '@deriv/shared'; import { observer, useStore } from '@deriv/stores'; import { getLanguage } from '@deriv/translations'; @@ -10,9 +9,9 @@ import { Analytics } from '@deriv-com/analytics'; const Redirect = observer(() => { const history = useHistory(); const { client, ui } = useStore(); - const { has_wallet } = useStoreWalletAccountsList(); - const { currency, is_logged_in, is_logging_in, setNewEmail, setVerificationCode, verification_code } = client; + const { currency, has_wallet, is_logged_in, is_logging_in, setNewEmail, setVerificationCode, verification_code } = + client; const { openRealAccountSignup, @@ -32,7 +31,7 @@ const Redirect = observer(() => { const ext_platform_url = url_params.get('ext_platform_url'); const redirectToExternalPlatform = url => { - history.push(`${routes.root}?ext_platform_url=${url}`); + history.push(`${routes.traders_hub}?ext_platform_url=${url}`); redirected_to_route = true; }; setVerificationCode(code_param, action_param); @@ -95,28 +94,19 @@ const Redirect = observer(() => { if (redirect_to) { let pathname = ''; let hash = ''; - const main_screen_route = has_wallet ? routes.wallets : routes.traders_hub; switch (redirect_to) { case '1': - pathname = routes.traders_hub; - break; - case '10': - pathname = main_screen_route; - hash = 'real'; - break; - case '11': - pathname = main_screen_route; - hash = 'demo'; - break; case '2': pathname = routes.traders_hub; break; + case '10': case '20': - pathname = main_screen_route; + pathname = routes.traders_hub; hash = 'real'; break; + case '11': case '21': - pathname = main_screen_route; + pathname = routes.traders_hub; hash = 'demo'; break; case '3': @@ -232,7 +222,7 @@ const Redirect = observer(() => { const is_demo = localStorage.getItem('cfd_reset_password_intent')?.includes('demo'); if (has_wallet) { history.push({ - pathname: routes.wallets, + pathname: routes.traders_hub, search: url_query_string, }); } else { @@ -254,9 +244,9 @@ const Redirect = observer(() => { break; } - if (!redirected_to_route && history.location.pathname !== routes.root) { + if (!redirected_to_route && history.location.pathname !== routes.traders_hub) { history.push({ - pathname: routes.root, + pathname: routes.traders_hub, search: url_query_string, }); } diff --git a/packages/core/src/App/Containers/RootComponent/index.js b/packages/core/src/App/Containers/RootComponent/index.js new file mode 100644 index 000000000000..d807a1fc3997 --- /dev/null +++ b/packages/core/src/App/Containers/RootComponent/index.js @@ -0,0 +1,3 @@ +import RootComponent from './root-component.jsx'; + +export default RootComponent; diff --git a/packages/core/src/App/Containers/RootComponent/root-component.jsx b/packages/core/src/App/Containers/RootComponent/root-component.jsx new file mode 100644 index 000000000000..8a46dac2ed87 --- /dev/null +++ b/packages/core/src/App/Containers/RootComponent/root-component.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { moduleLoader } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; + +const AppStore = React.lazy(() => + moduleLoader(() => { + // eslint-disable-next-line import/no-unresolved + return import(/* webpackChunkName: "appstore" */ '@deriv/appstore'); + }) +); + +const Wallets = React.lazy(() => + moduleLoader(() => { + // eslint-disable-next-line import/no-unresolved + return import(/* webpackChunkName: "wallets" */ '@deriv/wallets'); + }) +); + +const RootComponent = observer(props => { + const { client } = useStore(); + const { has_wallet } = client; + + return has_wallet ? : ; +}); + +export default RootComponent; diff --git a/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-content.jsx b/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-content.jsx index 365ffadf7b4b..1eb55ade91e6 100644 --- a/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-content.jsx +++ b/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-content.jsx @@ -3,63 +3,70 @@ import { localize } from '@deriv/translations'; import { Modal, FormSubmitButton, Text } from '@deriv/components'; import { routes, isNavigationFromExternalPlatform } from '@deriv/shared'; import { Analytics } from '@deriv-com/analytics'; +import { useStore, observer } from '@deriv/stores'; -const WarningMessageModal = ({ - is_closing_create_real_account_modal, - setIsClosingCreateRealAccountModal, - closeRealAccountSignup, - routing_history, - real_account_signup_target, -}) => { - const closeModal = () => { - real_account_signup_target !== 'maltainvest' && - Analytics.trackEvent('ce_real_account_signup_form', { - action: 'close', - form_source: document.referrer, - form_name: 'real_account_signup_form', - }); +const WarningMessageModal = observer( + ({ + is_closing_create_real_account_modal, + setIsClosingCreateRealAccountModal, + closeRealAccountSignup, + routing_history, + real_account_signup_target, + }) => { + const { traders_hub } = useStore(); + const { selectAccountType } = traders_hub; + const closeModal = async () => { + real_account_signup_target !== 'maltainvest' && + Analytics.trackEvent('ce_real_account_signup_form', { + action: 'close', + form_source: document.referrer, + form_name: 'real_account_signup_form', + }); - setIsClosingCreateRealAccountModal(false); + await selectAccountType('demo'); - closeRealAccountSignup(); + setIsClosingCreateRealAccountModal(false); - if (isNavigationFromExternalPlatform(routing_history, routes.smarttrader)) { - window.location = routes.smarttrader; - } + closeRealAccountSignup(); - if (isNavigationFromExternalPlatform(routing_history, routes.binarybot)) { - window.location = routes.binarybot; - } - }; + if (isNavigationFromExternalPlatform(routing_history, routes.smarttrader)) { + window.location = routes.smarttrader; + } - return ( - -
- - {localize('Stop creating an account?')} - -
- - {localize('If you hit Yes, the info you entered will be lost.')} + if (isNavigationFromExternalPlatform(routing_history, routes.binarybot)) { + window.location = routes.binarybot; + } + }; + + return ( + +
+ + {localize('Take me to Demo account')} +
+ + {localize('I will setup my real account later.')} + +
+ setIsClosingCreateRealAccountModal(false)} + onCancel={closeModal} + />
- setIsClosingCreateRealAccountModal(false)} - onCancel={closeModal} - /> -
- - ); -}; + + ); + } +); export default WarningMessageModal; diff --git a/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-modal.scss b/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-modal.scss index 26b9baf55fd9..a69a2a91111e 100644 --- a/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-modal.scss +++ b/packages/core/src/App/Containers/WarningCloseCreateRealAccountModal/warning-close-create-real-account-modal.scss @@ -5,7 +5,7 @@ padding: 2.4rem; width: 44rem; @include mobile { - width: unset; + max-width: 35rem; } .dc-form-submit-button { diff --git a/packages/core/src/Constants/cfd-text.js b/packages/core/src/Constants/cfd-text.js index 677af6e20fd0..04e27190baa2 100644 --- a/packages/core/src/Constants/cfd-text.js +++ b/packages/core/src/Constants/cfd-text.js @@ -6,11 +6,11 @@ export const CFD_TEXT = { mt5_cfds: () => localize('MT5 CFDs'), ctrader: () => localize('Deriv cTrader'), cfd: () => localize('CFDs'), - synthetic: () => localize('Derived'), - synthetic_demo: () => localize('Derived Demo'), - synthetic_bvi: () => localize('Derived BVI'), - synthetic_svg: () => localize('Derived SVG'), - synthetic_v: () => localize('Derived Vanuatu'), + synthetic: () => localize('Standard'), + synthetic_demo: () => localize('Standard Demo'), + synthetic_bvi: () => localize('Standard BVI'), + synthetic_svg: () => localize('Standard SVG'), + synthetic_v: () => localize('Standard Vanuatu'), financial: () => localize('Financial'), financial_demo: () => localize('Financial Demo'), financial_bvi: () => localize('Financial BVI'), diff --git a/packages/core/src/Modules/Endpoint/__tests__/FeatureFlagsSection.spec.tsx b/packages/core/src/Modules/Endpoint/__tests__/FeatureFlagsSection.spec.tsx index 8dce95caaa33..cce9ed5ade08 100644 --- a/packages/core/src/Modules/Endpoint/__tests__/FeatureFlagsSection.spec.tsx +++ b/packages/core/src/Modules/Endpoint/__tests__/FeatureFlagsSection.spec.tsx @@ -7,28 +7,35 @@ import { website_domain } from '@deriv/shared'; const FLAGS = { WALLET: 'wallet', - NEXT_WALLET: 'next_wallet', SHARKFIN: 'sharkfin', DTRADER_V2: 'dtrader_v2', }; const feature_flags_title = 'Feature flags'; describe('', () => { - const original_window_location = window.location; + const original_window_location = { ...window.location }; + const original_location = { ...location }; let default_mock_store: ReturnType; beforeEach(() => { Object.defineProperty(window, 'location', { + value: { ...original_window_location }, configurable: true, - enumerable: true, - value: new URL('https://localhost:8443'), + writable: true, }); + Object.defineProperty(global, 'location', { + value: { ...original_location }, + configurable: true, + writable: true, + }); + + window.location.href = 'https://localhost:8443'; + default_mock_store = mockStore({ feature_flags: { data: { wallet: false, - next_wallet: false, sharkfin: false, dtrader_v2: false, }, @@ -37,10 +44,18 @@ describe('', () => { }); afterEach(() => { + jest.clearAllMocks(); + Object.defineProperty(window, 'location', { + value: { ...original_window_location }, + configurable: true, + writable: true, + }); + + Object.defineProperty(global, 'location', { + value: { ...original_location }, configurable: true, - enumerable: true, - value: original_window_location, + writable: true, }); }); @@ -56,19 +71,17 @@ describe('', () => { render(mockFeatureFlagsSection()); expect(screen.getByText(feature_flags_title)).toBeInTheDocument(); - expect(screen.getByRole('checkbox', { name: FLAGS.NEXT_WALLET })).not.toBeChecked(); expect(screen.getByRole('checkbox', { name: FLAGS.SHARKFIN })).not.toBeChecked(); expect(screen.getByRole('checkbox', { name: FLAGS.DTRADER_V2 })).not.toBeChecked(); expect(screen.queryByRole('checkbox', { name: FLAGS.WALLET })).not.toBeInTheDocument(); }); - it('should render checked next_wallet, sharkfin & dtrader_v2 flags on localhost', () => { + it('should render checked sharkfin & dtrader_v2 flags on localhost', () => { render( mockFeatureFlagsSection( mockStore({ feature_flags: { data: { wallet: false, - next_wallet: true, sharkfin: true, dtrader_v2: true, }, @@ -77,7 +90,6 @@ describe('', () => { ) ); - expect(screen.getByRole('checkbox', { name: FLAGS.NEXT_WALLET })).toBeChecked(); expect(screen.getByRole('checkbox', { name: FLAGS.SHARKFIN })).toBeChecked(); expect(screen.getByRole('checkbox', { name: FLAGS.DTRADER_V2 })).toBeChecked(); expect(screen.queryByRole('checkbox', { name: FLAGS.WALLET })).not.toBeInTheDocument(); @@ -86,7 +98,6 @@ describe('', () => { location.hostname = 'test.binary.sx'; render(mockFeatureFlagsSection()); - expect(screen.getByRole('checkbox', { name: FLAGS.NEXT_WALLET })).toBeInTheDocument(); expect(screen.getByRole('checkbox', { name: FLAGS.SHARKFIN })).toBeInTheDocument(); expect(screen.getByRole('checkbox', { name: FLAGS.DTRADER_V2 })).toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.WALLET })).not.toBeInTheDocument(); @@ -95,16 +106,14 @@ describe('', () => { location.hostname = 'staging-app.deriv.com'; render(mockFeatureFlagsSection()); - expect(screen.getByRole('checkbox', { name: FLAGS.NEXT_WALLET })).toBeInTheDocument(); expect(screen.getByRole('checkbox', { name: FLAGS.SHARKFIN })).toBeInTheDocument(); expect(screen.getByRole('checkbox', { name: FLAGS.DTRADER_V2 })).toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.WALLET })).not.toBeInTheDocument(); }); - it('should render a single next_wallet flag on production', () => { + it('should render none of the flags on production', () => { location.hostname = website_domain; render(mockFeatureFlagsSection()); - expect(screen.getByRole('checkbox', { name: FLAGS.NEXT_WALLET })).toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.SHARKFIN })).not.toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.DTRADER_V2 })).not.toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.WALLET })).not.toBeInTheDocument(); @@ -114,7 +123,6 @@ describe('', () => { render(mockFeatureFlagsSection()); expect(screen.queryByRole(feature_flags_title)).not.toBeInTheDocument(); - expect(screen.queryByRole('checkbox', { name: FLAGS.NEXT_WALLET })).not.toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.SHARKFIN })).not.toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.DTRADER_V2 })).not.toBeInTheDocument(); expect(screen.queryByRole('checkbox', { name: FLAGS.WALLET })).not.toBeInTheDocument(); @@ -124,7 +132,7 @@ describe('', () => { default_mock_store.feature_flags.update = update; render(mockFeatureFlagsSection()); - userEvent.click(screen.getByRole('checkbox', { name: FLAGS.NEXT_WALLET })); + userEvent.click(screen.getByRole('checkbox', { name: FLAGS.DTRADER_V2 })); expect(update).toBeCalled(); }); diff --git a/packages/core/src/Modules/P2PIFrame/P2PIFrame.tsx b/packages/core/src/Modules/P2PIFrame/P2PIFrame.tsx new file mode 100644 index 000000000000..1f205eee954f --- /dev/null +++ b/packages/core/src/Modules/P2PIFrame/P2PIFrame.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { getUrlP2P } from '@deriv/shared'; + +const P2PIFrame = () => { + const base_link = getUrlP2P(); + + return ( +