diff --git a/.github/workflows/all_packages.yaml b/.github/workflows/all_packages.yaml index fb3c00d1d..038db27dd 100644 --- a/.github/workflows/all_packages.yaml +++ b/.github/workflows/all_packages.yaml @@ -30,7 +30,9 @@ jobs: - name: Set SSH Key uses: webfactory/ssh-agent@fd34b8dee206fe74b288a5e61bc95fba2f1911eb with: - ssh-private-key: ${{secrets.SSH_PRIVATE_KEY}} + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + ${{ secrets.SSH_CHART_PRIVATE_KEY }} - name: Install Melos and run pub get uses: bluefireteam/melos-action@dd3c344d731938d2ab2567a261f54a19a68b5f6a diff --git a/.github/workflows/check_versioning_pr.yml b/.github/workflows/check_versioning_pr.yml new file mode 100644 index 000000000..78871d2b7 --- /dev/null +++ b/.github/workflows/check_versioning_pr.yml @@ -0,0 +1,61 @@ +name: versioning_pr_exists + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +jobs: + check-version-pr: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + + - name: Set up Node.js + uses: actions/setup-node@eff380dfbcf941bf8832e4acb788cebe13dfd758 + with: + node-version: "14" + + - name: Check if PR with title chore(version) exists + id: check-pr + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea + with: + github-token: ${{ secrets.PAT }} + script: | + const { data: pullRequests } = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + per_page: 100 + }); + + const existingPR = pullRequests.find(pr => pr.title === 'chore(version): bump version and update changelog'); + + if (existingPR) { + core.setOutput("VERSIONING_PR_EXISTS", true); + console.log("Version bump pull request exists. Please merge that pr first to pass the check.: #${existingPR.number}"); + } else { + core.setOutput("VERSIONING_PR_EXISTS", false); + console.log("No version bump pull request found."); + } + + - name: Delete comment if exists + uses: izhangzhihao/delete-comment@d075704468e1cf74e60944d9f335351213c34d85 + with: + github_token: ${{ secrets.PAT }} + delete_user_name: mobile-apps-deriv + issue_number: ${{ github.event.number }} + + - name: Comment if versioning PR exists + if: ${{ contains(steps.check-pr.outputs.VERSIONING_PR_EXISTS, 'true') }} + uses: marocchino/sticky-pull-request-comment@4b7290acd5c5b99ef9995db30e52150e705d2475 + with: + GITHUB_TOKEN: ${{ secrets.PAT }} + message: | + **Merge Is BLOCKED : we still have a chore(version) pr open, please merge that pr first ** + + - name: Fail if versioning PR exists + if: ${{ contains(steps.check-pr.outputs.VERSIONING_PR_EXISTS, 'true') }} + run: | + echo "A PR with the title 'chore(version)' already exists. Exiting." + exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d955c80..3f10163ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,348 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2024-06-28 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_mobile_chart_wrapper` - `v0.0.2`](#deriv_mobile_chart_wrapper---v002) + +--- + +#### `deriv_mobile_chart_wrapper` - `v0.0.2` + + - **FEAT**(deriv_mobile_chart_wrapper): [DERG-2498] create deriv_mobile_chart_wrapper package ([#626](https://github.com/regentmarkets/flutter-deriv-packages/issues/626)). ([01aec1ed](https://github.com/regentmarkets/flutter-deriv-packages/commit/01aec1edbd8f02e21951918ec86f00fb28ef2c58)) + + +## 2024-06-28 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`analytics` - `v2.1.0`](#analytics---v210) + - [`deriv_auth` - `v6.7.6`](#deriv_auth---v676) + - [`deriv_passkeys` - `v0.0.2+8`](#deriv_passkeys---v0028) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.7.6` + - `deriv_passkeys` - `v0.0.2+8` + +--- + +#### `analytics` - `v2.1.0` + + - **FEAT**(analytics): ilya/DERG-2409/Add_tracking_to_real_account_sign_up_on_Deriv_Go ([#603](https://github.com/regentmarkets/flutter-deriv-packages/issues/603)). ([9ada725d](https://github.com/regentmarkets/flutter-deriv-packages/commit/9ada725ded271e2b94cffed622eff2e75539cb55)) + + +## 2024-06-27 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_passkeys` - `v0.0.2+7`](#deriv_passkeys---v0027) + - [`deriv_auth` - `v6.7.5`](#deriv_auth---v675) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.7.5` + +--- + +#### `deriv_passkeys` - `v0.0.2+7` + + - **FIX**(deriv_passkeys): Fix_logout_issue_by_fetching_refresh_token ([#630](https://github.com/regentmarkets/flutter-deriv-packages/issues/630)). ([282278ae](https://github.com/regentmarkets/flutter-deriv-packages/commit/282278aeb27256eaa37660f58eff704d27e92c93)) + + +## 2024-06-26 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_localizations` - `v1.5.1`](#deriv_localizations---v151) + - [`deriv_auth` - `v6.7.4`](#deriv_auth---v674) + - [`deriv_passkeys` - `v0.0.2+6`](#deriv_passkeys---v0026) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.7.4` + - `deriv_passkeys` - `v0.0.2+6` + +--- + +#### `deriv_localizations` - `v1.5.1` + + - **REFACTOR**(deriv_localizations): Crowdin Localization Generated ([#648](https://github.com/regentmarkets/flutter-deriv-packages/issues/648)). ([c925c22a](https://github.com/regentmarkets/flutter-deriv-packages/commit/c925c22a97e6e13568fefa0d86d993985093617c)) + + +## 2024-06-26 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_localizations` - `v1.5.0`](#deriv_localizations---v150) + - [`deriv_auth` - `v6.7.3`](#deriv_auth---v673) + - [`deriv_passkeys` - `v0.0.2+5`](#deriv_passkeys---v0025) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.7.3` + - `deriv_passkeys` - `v0.0.2+5` + +--- + +#### `deriv_localizations` - `v1.5.0` + + - **FEAT**(deriv_localizations): add localization for deriv mobile chart wrapper package ([#627](https://github.com/regentmarkets/flutter-deriv-packages/issues/627)). ([33f5e3a1](https://github.com/regentmarkets/flutter-deriv-packages/commit/33f5e3a1bc0765cb8559b5a39e300b8c088aa705)) + + +## 2024-06-26 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_passkeys` - `v0.0.2+4`](#deriv_passkeys---v0024) + - [`deriv_auth` - `v6.7.2`](#deriv_auth---v672) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.7.2` + +--- + +#### `deriv_passkeys` - `v0.0.2+4` + + - **FIX**(deriv_passkeys): fix passkeys success button styles ([#629](https://github.com/regentmarkets/flutter-deriv-packages/issues/629)). ([d1fb8590](https://github.com/regentmarkets/flutter-deriv-packages/commit/d1fb8590eb5eb55f9bd9db3a255f33cf152f364d)) + + +## 2024-06-25 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_auth` - `v6.7.1`](#deriv_auth---v671) + - [`deriv_web_view` - `v0.2.2+3`](#deriv_web_view---v0223) + - [`deriv_ui` - `v0.0.7+9`](#deriv_ui---v0079) + - [`deriv_widgetbook` - `v0.0.2+9`](#deriv_widgetbook---v0029) + - [`deriv_passkeys` - `v0.0.2+3`](#deriv_passkeys---v0023) + - [`deriv_language_selector` - `v0.0.2+7`](#deriv_language_selector---v0027) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_ui` - `v0.0.7+9` + - `deriv_widgetbook` - `v0.0.2+9` + - `deriv_passkeys` - `v0.0.2+3` + - `deriv_language_selector` - `v0.0.2+7` + +--- + +#### `deriv_auth` - `v6.7.1` + + - **FIX**(deriv_auth): Fix login provider null issue ([#641](https://github.com/regentmarkets/flutter-deriv-packages/issues/641)). ([e4181541](https://github.com/regentmarkets/flutter-deriv-packages/commit/e41815416eb76b3724481f8a0b980e943311ebeb)) + - **FIX**(deriv_web_view): update deriv ui version in deriv auth ([#639](https://github.com/regentmarkets/flutter-deriv-packages/issues/639)). ([11b46a9f](https://github.com/regentmarkets/flutter-deriv-packages/commit/11b46a9f7bd00482d3cac7820cf4cd5d61da0cad)) + +#### `deriv_web_view` - `v0.2.2+3` + + - **FIX**(deriv_web_view): update deriv web view readme file ([#637](https://github.com/regentmarkets/flutter-deriv-packages/issues/637)). ([b0b66dad](https://github.com/regentmarkets/flutter-deriv-packages/commit/b0b66dadb6cb1b30dc3ad759f2eee031673b6bbf)) + + +## 2024-06-25 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_auth` - `v6.7.0`](#deriv_auth---v670) + - [`deriv_web_view` - `v0.2.2+2`](#deriv_web_view---v0222) + - [`update_checker` - `v1.4.0`](#update_checker---v140) + - [`deriv_ui` - `v0.0.7+8`](#deriv_ui---v0078) + - [`deriv_widgetbook` - `v0.0.2+8`](#deriv_widgetbook---v0028) + - [`deriv_passkeys` - `v0.0.2+2`](#deriv_passkeys---v0022) + - [`deriv_language_selector` - `v0.0.2+6`](#deriv_language_selector---v0026) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_ui` - `v0.0.7+8` + - `deriv_widgetbook` - `v0.0.2+8` + - `deriv_passkeys` - `v0.0.2+2` + - `deriv_language_selector` - `v0.0.2+6` + +--- + +#### `deriv_auth` - `v6.7.0` + + - **FIX**(deriv_web_view): update deriv ui version in deriv auth ([#639](https://github.com/regentmarkets/flutter-deriv-packages/issues/639)). ([11b46a9f](https://github.com/regentmarkets/flutter-deriv-packages/commit/11b46a9f7bd00482d3cac7820cf4cd5d61da0cad)) + - **FEAT**(deriv_auth): add log in user tracking events. ([#620](https://github.com/regentmarkets/flutter-deriv-packages/issues/620)). ([ae9556cf](https://github.com/regentmarkets/flutter-deriv-packages/commit/ae9556cf3af98196bb22e351d9a8eccbf534889c)) + +#### `deriv_web_view` - `v0.2.2+2` + + - **FIX**(deriv_web_view): update deriv web view readme file ([#637](https://github.com/regentmarkets/flutter-deriv-packages/issues/637)). ([b0b66dad](https://github.com/regentmarkets/flutter-deriv-packages/commit/b0b66dadb6cb1b30dc3ad759f2eee031673b6bbf)) + - **FIX**(deriv_webview): add language parameter to needed classes ([#600](https://github.com/regentmarkets/flutter-deriv-packages/issues/600)). ([9b857999](https://github.com/regentmarkets/flutter-deriv-packages/commit/9b857999f479486830671f8b8e8b7fc26dcd769b)) + +#### `update_checker` - `v1.4.0` + + - **FEAT**(update_checker): add the ability to change the key from the app side ([#628](https://github.com/regentmarkets/flutter-deriv-packages/issues/628)). ([b18609a0](https://github.com/regentmarkets/flutter-deriv-packages/commit/b18609a00533aaab6d6962eb89f323e2f560df8b)) + + +## 2024-06-24 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_ui` - `v0.0.7+7`](#deriv_ui---v0077) + - [`deriv_auth` - `v6.6.9`](#deriv_auth---v669) + - [`deriv_passkeys` - `v0.0.2+1`](#deriv_passkeys---v0021) + - [`deriv_widgetbook` - `v0.0.2+7`](#deriv_widgetbook---v0027) + - [`deriv_language_selector` - `v0.0.2+5`](#deriv_language_selector---v0025) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.6.9` + - `deriv_passkeys` - `v0.0.2+1` + - `deriv_widgetbook` - `v0.0.2+7` + - `deriv_language_selector` - `v0.0.2+5` + +--- + +#### `deriv_ui` - `v0.0.7+7` + + - **REFACTOR**(deriv_ui): [DERG-3500] return inputted amount based on the formatter in numpad ([#624](https://github.com/regentmarkets/flutter-deriv-packages/issues/624)). ([fdf4df19](https://github.com/regentmarkets/flutter-deriv-packages/commit/fdf4df1979ddb8710dba8925ef779cca25d60615)) + + +## 2024-06-21 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_passkeys` - `v0.0.2`](#deriv_passkeys---v002) + - [`deriv_auth` - `v6.6.8`](#deriv_auth---v668) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `deriv_auth` - `v6.6.8` + +--- + +#### `deriv_passkeys` - `v0.0.2` + + - **REFACTOR**(deriv_passkeys): Removed deprecated linter rules ([#625](https://github.com/regentmarkets/flutter-deriv-packages/issues/625)). ([2fdc28f2](https://github.com/regentmarkets/flutter-deriv-packages/commit/2fdc28f20b8efe2ddc3a9a261c40b533307f25e3)) + - **FEAT**(deriv_passkeys): add user tracking events. ([#607](https://github.com/regentmarkets/flutter-deriv-packages/issues/607)). ([d86b51e2](https://github.com/regentmarkets/flutter-deriv-packages/commit/d86b51e2fe4ca4d18768d0ba17567a388a8d360d)) + + +## 2024-06-20 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`deriv_logger` - `v0.0.2`](#deriv_logger---v002) + - [`update_checker` - `v1.3.0`](#update_checker---v130) + +--- + +#### `deriv_logger` - `v0.0.2` + + - **FEAT**(deriv_logger): add ability to print prettified logs in console and UI ([#608](https://github.com/regentmarkets/flutter-deriv-packages/issues/608)). ([5a91c24b](https://github.com/regentmarkets/flutter-deriv-packages/commit/5a91c24bde607ff37940edf18f8dfac67d3fc4fa)) + +#### `update_checker` - `v1.3.0` + + - **FEAT**(deriv_logger): add ability to print prettified logs in console and UI ([#608](https://github.com/regentmarkets/flutter-deriv-packages/issues/608)). ([5a91c24b](https://github.com/regentmarkets/flutter-deriv-packages/commit/5a91c24bde607ff37940edf18f8dfac67d3fc4fa)) + + ## 2024-06-14 ### Changes diff --git a/README.md b/README.md index 564d181a1..ca0f1cd5a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This repository contains private packages & plugins that are used by the company ## Using the packages -Each package has been released as git tag with convention as **packageName-vVersionNumber**`(Example: deriv_auth-v6.6.7)`. To use the package, add the following to your pubspec.yaml file: +Each package has been released as git tag with convention as **packageName-vVersionNumber**`(Example: deriv_auth-v6.7.6)`. To use the package, add the following to your pubspec.yaml file: ```yaml @@ -12,15 +12,15 @@ deriv_ui: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.7+6 #your prefered version + ref: deriv_ui-v0.0.7+9 #your prefered version ``` ## Packages | Name | Description | Version | | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| [analytics](./packages/analytics) | Used to collect and send analytical information to 'Firebase' and 'Segment'. | [v2.0.0](./packages/analytics/CHANGELOG.md) | -| [deriv_auth](./packages/deriv_auth) | A Dart package that provides Authentication logic for Deriv applications. | [v6.6.7 ](./packages/deriv_auth/CHANGELOG.md) | +| [analytics](./packages/analytics) | Used to collect and send analytical information to 'Firebase' and 'Segment'. | [v2.1.0](./packages/analytics/CHANGELOG.md) | +| [deriv_auth](./packages/deriv_auth) | A Dart package that provides Authentication logic for Deriv applications. | [v6.7.6 ](./packages/deriv_auth/CHANGELOG.md) | | [deriv_banner](./packages/deriv_banner) | A widget to show banner in apps. | [v0.0.1+1](./packages/deriv_banner/CHANGELOG.md) | | [deriv_bloc_manager](./packages/deriv_bloc_manager) | Provides some tools to manage blocs. | [v0.0.1](./packages/deriv_bloc_manager/CHANGELOG.md) | | [deriv_datadog](./packages/deriv_datadog) | A package that helps you monitor the performance and user interactions of your Flutter app by sending data to Datadog. | [v0.0.1](./packages/deriv_datadog/CHANGELOG.md) | @@ -31,20 +31,20 @@ deriv_ui: | [deriv_http_client](./packages/deriv_http_client) | A package that provides a wrapper for http package. | [v2.0.0](./packages/deriv_http_client/CHANGELOG.md) | | [deriv_lint](./packages/deriv_lint) | A Dart package that provides lint rules for Dart and Flutter. | [v1.0.0](./packages/deriv_lint/CHANGELOG.md) | | [deriv_live_chat](./packages/deriv_live_chat) | A plugin for live chat SDK support to dart. | [v0.0.1+2](./packages/deriv_live_chat/CHANGELOG.md) | -| [deriv_language_selector](./packages/deriv_language_selector) | A package to handle language change of the app. | [v0.0.2+4](./packages/deriv_language_selector/CHANGELOG.md) | -| [deriv_localizations](./packages/deriv_localizations) | A Package that contains the localization arb(coming from Crowdin) and dart generated files for flutter_deriv_packages. | [v1.1.1](./packages/deriv_localizations/CHANGELOG.md) | | [v1.4.4](./packages/deriv_localizations/CHANGELOG.md) | +| [deriv_language_selector](./packages/deriv_language_selector) | A package to handle language change of the app. | [v0.0.2+7](./packages/deriv_language_selector/CHANGELOG.md) | +| [deriv_localizations](./packages/deriv_localizations) | A Package that contains the localization arb(coming from Crowdin) and dart generated files for flutter_deriv_packages. | [v1.1.1](./packages/deriv_localizations/CHANGELOG.md) | | [v1.5.1](./packages/deriv_localizations/CHANGELOG.md) | | [deriv_numpad](./packages/deriv_numpad) | Number Pad Widget for number input. | [v1.1.5](./packages/deriv_numpad/CHANGELOG.md) | | [deriv_rudderstack](./packages/deriv_rudderstack) | A plugin that add RudderStack SDK support to Flutter. | [v1.1.0](./packages/deriv_rudderstack/CHANGELOG.md) | | [deriv_store_launcher](./packages/deriv_store_launcher) | A plugin to launch app stores base on platform and manufacturer. | [v0.0.1+1](./packages/deriv_store_launcher/CHANGELOG.md) | | [deriv_technical_analysis](./packages/deriv_technical_analysis) | A Dart package for Technical Analysis. | [v0.0.1](./packages/deriv_technical_analysis/CHANGELOG.md) | | [deriv_theme](./packages/deriv_theme) | A package that contains the theme used by Deriv products. | [v2.4.0](./packages/deriv_theme/CHANGELOG.md) | -| [deriv_ui](./packages/deriv_ui) | A package that contains the UI components used by Deriv products. | [v0.0.7+6](./packages/deriv_ui/CHANGELOG.md) | +| [deriv_ui](./packages/deriv_ui) | A package that contains the UI components used by Deriv products. | [v0.0.7+9](./packages/deriv_ui/CHANGELOG.md) | | [deriv_utilities](./packages/deriv_utilities) | A package that contains the utilities including helper functions, mixins, and extensions. | [v1.0.0](./packages/deriv_utilities/CHANGELOG.md) | | [deriv_websocket](./packages/deriv_web_socket_client) | A package that provides a easy to use websocket client. | [v1.0.1](./packages/deriv_web_socket_client/CHANGELOG.md) | -| [deriv_web_view](./packages/deriv_web_view) | Deriv web view package. | [v0.2.2+1](./packages/deriv_web_view/CHANGELOG.md) | -| [deriv_widgetbook](./packages/deriv_widgetbook) |Storybook for Deriv UI Widgets and Components | [v0.0.2+6](./packages/deriv_widgetbook/CHANGELOG.md) | +| [deriv_web_view](./packages/deriv_web_view) | Deriv web view package. | [v0.2.2+3](./packages/deriv_web_view/CHANGELOG.md) | +| [deriv_widgetbook](./packages/deriv_widgetbook) |Storybook for Deriv UI Widgets and Components | [v0.0.2+9](./packages/deriv_widgetbook/CHANGELOG.md) | | [form_builder](./packages/form_builder) | A simpler and cleaner way to create, validate and submit forms. | [v1.0.0+1](./packages/form_builder/CHANGELOG.md) | -| [update_checker](./packages/update_checker) | Check and retrieve update information from the server for the given package. | [v1.2.2](./packages/update_checker/CHANGELOG.md) | +| [update_checker](./packages/update_checker) | Check and retrieve update information from the server for the given package. | [v1.4.0](./packages/update_checker/CHANGELOG.md) | | [deriv_feature_flag](./packages/deriv_feature_flag) | A package to provide feature flag functionality for apps. | [v0.1.1](./packages/deriv_feature_flag/CHANGELOG.md) | ## Environment Setup diff --git a/melos.yaml b/melos.yaml index 8e75065c7..c2d73fcdb 100644 --- a/melos.yaml +++ b/melos.yaml @@ -36,6 +36,7 @@ scripts: description: Run flutter test for all packages. run: flutter test --coverage exec: + concurrency: 6 failFast: true packageFilters: dirExists: @@ -43,3 +44,4 @@ scripts: ignore: # Ignore until we have a proper test. - "deriv_feature_flag" + - "deriv_auth" diff --git a/packages/analytics/CHANGELOG.md b/packages/analytics/CHANGELOG.md index 37d435e42..d638d6943 100644 --- a/packages/analytics/CHANGELOG.md +++ b/packages/analytics/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + + - **FEAT**(analytics): ilya/DERG-2409/Add_tracking_to_real_account_sign_up_on_Deriv_Go ([#603](https://github.com/regentmarkets/flutter-deriv-packages/issues/603)). ([9ada725d](https://github.com/regentmarkets/flutter-deriv-packages/commit/9ada725ded271e2b94cffed622eff2e75539cb55)) + ## 2.0.0 > Note: This release has breaking changes. diff --git a/packages/analytics/lib/sdk/rudderstack/events/rudderstack_events.dart b/packages/analytics/lib/sdk/rudderstack/events/rudderstack_events.dart index 425373151..1b2692f5b 100644 --- a/packages/analytics/lib/sdk/rudderstack/events/rudderstack_events.dart +++ b/packages/analytics/lib/sdk/rudderstack/events/rudderstack_events.dart @@ -20,7 +20,7 @@ class DerivRudderstackEvents { properties: { 'action': 'open', 'form_source': 'mobile_derivgo', - 'form_name': 'virtual_signup_derivgo' + 'form_name': 'common_events_derivgo' }, ); } @@ -56,7 +56,7 @@ class DerivRudderstackEvents { DerivRudderstack().track( eventName: 'ce_virtual_signup_form', properties: { - 'action': 'tab_referral_toggle', + 'action': 'tap_referral_toggle', 'form_source': 'mobile_derivgo', 'form_name': 'virtual_signup_derivgo' }, @@ -182,4 +182,98 @@ class DerivRudderstackEvents { }, ); } + + /// Tracks when the real signup form opened. + void logOpenRealSignUp() { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'open_real_sign_up', + 'form_source': 'mobile_derivgo', + 'form_name': 'real_signup_derivgo' + }, + ); + } + + /// Tracks when the user presses the next button + /// and the step is successfully passed. + void logStepPassedRealSignUp( + [String? stepNum, + String? stepCodename, + Map? userChoice]) { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'step_passed', + 'step_codename': stepCodename, + 'step_num': stepNum, + 'user_choice': userChoice, + 'form_source': 'mobile_derivgo', + 'form_name': 'real_signup_derivgo' + }, + ); + } + + /// Tracks when user tap the 'previous' button and go to previous screen. + void logStepBackRealSignUp(String stepCodeName) { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'step_back', + 'form_source': 'mobile_derivgo', + 'form_name': 'real_signup_derivgo' + }, + ); + } + + /// Tracks if the user presses the close button on the signup form. + void logCloseRealSignUp() { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'close', + 'form_source': 'mobile_derivgo', + 'form_name': 'real_signup_derivgo' + }, + ); + } + + /// Tracks if any logical error has happened on the form, + /// validation error for instance. + void logValidationErrorDuringRealSignUp() { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'real_signup_error', + 'form_source': 'mobile_derivgo', + 'form_name': 'real_signup_derivgo' + }, + ); + } + + /// Tracks system error has happened, + /// like no connection to the server and etc. + void logError(String error) { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'other_error', + 'error_message': error, + 'form_source': 'mobile_derivgo', + 'form_name': 'common_events_derivgo' + }, + ); + } + + /// Tracks when the signup flow is finished. + void logRealSignUpFinished() { + DerivRudderstack().track( + eventName: 'ce_real_account_signup_form', + properties: { + 'action': 'real_signup_finished', + 'form_source': 'mobile_derivgo', + 'form_name': 'real_signup_derivgo' + }, + ); + } } diff --git a/packages/analytics/pubspec.yaml b/packages/analytics/pubspec.yaml index c49dcbcb8..8376e99b0 100644 --- a/packages/analytics/pubspec.yaml +++ b/packages/analytics/pubspec.yaml @@ -1,6 +1,6 @@ name: analytics description: A new Flutter package for collecting and sending analytical information from the app. -version: 2.0.0 +version: 2.1.0 homepage: https://deriv.com/ publish_to: "none" diff --git a/packages/deriv_auth/CHANGELOG.md b/packages/deriv_auth/CHANGELOG.md index c6e1aba6d..3f8b7612d 100644 --- a/packages/deriv_auth/CHANGELOG.md +++ b/packages/deriv_auth/CHANGELOG.md @@ -1,3 +1,41 @@ +## 6.7.6 + + - Update a dependency to the latest release. + +## 6.7.5 + + - Update a dependency to the latest release. + +## 6.7.4 + + - Update a dependency to the latest release. + +## 6.7.3 + + - Update a dependency to the latest release. + +## 6.7.2 + + - Update a dependency to the latest release. + +## 6.7.1 + + - **FIX**(deriv_auth): Fix login provider null issue ([#641](https://github.com/regentmarkets/flutter-deriv-packages/issues/641)). ([e4181541](https://github.com/regentmarkets/flutter-deriv-packages/commit/e41815416eb76b3724481f8a0b980e943311ebeb)) + - **FIX**(deriv_web_view): update deriv ui version in deriv auth ([#639](https://github.com/regentmarkets/flutter-deriv-packages/issues/639)). ([11b46a9f](https://github.com/regentmarkets/flutter-deriv-packages/commit/11b46a9f7bd00482d3cac7820cf4cd5d61da0cad)) + +## 6.7.0 + + - **FIX**(deriv_web_view): update deriv ui version in deriv auth ([#639](https://github.com/regentmarkets/flutter-deriv-packages/issues/639)). ([11b46a9f](https://github.com/regentmarkets/flutter-deriv-packages/commit/11b46a9f7bd00482d3cac7820cf4cd5d61da0cad)) + - **FEAT**(deriv_auth): add log in user tracking events. ([#620](https://github.com/regentmarkets/flutter-deriv-packages/issues/620)). ([ae9556cf](https://github.com/regentmarkets/flutter-deriv-packages/commit/ae9556cf3af98196bb22e351d9a8eccbf534889c)) + +## 6.6.9 + + - Update a dependency to the latest release. + +## 6.6.8 + + - Update a dependency to the latest release. + ## 6.6.7 - Update a dependency to the latest release. diff --git a/packages/deriv_auth/example/pubspec.yaml b/packages/deriv_auth/example/pubspec.yaml index bd7100588..ce0a6b59c 100644 --- a/packages/deriv_auth/example/pubspec.yaml +++ b/packages/deriv_auth/example/pubspec.yaml @@ -18,18 +18,6 @@ dependencies: flutter_bloc: ^8.1.3 http: ^0.13.6 device_preview: ^1.1.0 - deriv_http_client: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_http_client - ref: deriv_http_client-v2.0.1 - -dependency_overrides: - deriv_ui: - git: - url: git@github.com:regentmarkets/flutter-deriv-packages.git - path: packages/deriv_ui - ref: deriv_ui-v0.0.7+5 dev_dependencies: flutter_test: diff --git a/packages/deriv_auth/lib/core/analytics/data/auth_tracking_data.dart b/packages/deriv_auth/lib/core/analytics/data/auth_tracking_data.dart new file mode 100644 index 000000000..9ce8e5491 --- /dev/null +++ b/packages/deriv_auth/lib/core/analytics/data/auth_tracking_data.dart @@ -0,0 +1,164 @@ +import 'package:deriv_auth/core/analytics/enums.dart'; + +/// Get user tracking data. +Map getUserTrackingData( + LoginAction action, + String appId, { + LoginProvider? provider, + String? errorMessage, +}) => + switch (action) { + LoginAction.openLoginForm => { + 'event': 'open', + 'properties': { + 'app_id': appId, + }, + }, + LoginAction.startLogin => _getLoginStartedTrackingData( + provider, + appId, + ), + LoginAction.finishLogin => _getLoginFinishedTrackingData( + provider, + appId, + ), + LoginAction.loginError => _getLoginErrorTrackingData( + provider, + appId, + errorMessage ?? (throw Exception('Error message is required.')), + ), + }; + +/// Get login started tracking data. +Map _getLoginStartedTrackingData( + LoginProvider? provider, String appId) => + switch (provider) { + LoginProvider.email => { + 'event': 'login_started', + 'properties': { + 'app_id': appId, + 'login_provider': 'email', + }, + }, + LoginProvider.passkey => { + 'event': 'login_started', + 'properties': { + 'app_id': appId, + 'login_provider': 'passkey', + }, + }, + LoginProvider.google => { + 'event': 'login_started', + 'properties': { + 'app_id': appId, + 'login_provider': 'google', + }, + }, + LoginProvider.facebook => { + 'event': 'login_started', + 'properties': { + 'app_id': appId, + 'login_provider': 'facebook', + }, + }, + LoginProvider.apple => { + 'event': 'login_started', + 'properties': { + 'app_id': appId, + 'login_provider': 'apple', + }, + }, + null => throw Exception('Null LoginProvider is being passed.'), + }; + +/// Get login finished tracking data. +Map _getLoginFinishedTrackingData( + LoginProvider? provider, String appId) => + switch (provider) { + LoginProvider.email => { + 'event': 'login_finished', + 'properties': { + 'app_id': appId, + 'login_provider': 'email', + }, + }, + LoginProvider.passkey => { + 'event': 'login_finished', + 'properties': { + 'app_id': appId, + 'login_provider': 'passkey', + }, + }, + LoginProvider.google => { + 'event': 'login_finished', + 'properties': { + 'app_id': appId, + 'login_provider': 'google', + }, + }, + LoginProvider.facebook => { + 'event': 'login_finished', + 'properties': { + 'app_id': appId, + 'login_provider': 'facebook', + }, + }, + LoginProvider.apple => { + 'event': 'login_finished', + 'properties': { + 'app_id': appId, + 'login_provider': 'apple', + }, + }, + null => throw Exception('Null LoginProvider is being passed.'), + }; + +/// Get login error tracking data. +Map _getLoginErrorTrackingData( + LoginProvider? provider, + String appId, + String errorMessage, +) => + switch (provider) { + LoginProvider.email => { + 'event': 'login_flow_error', + 'properties': { + 'app_id': appId, + 'login_provider': 'email', + 'error_message': errorMessage, + }, + }, + LoginProvider.passkey => { + 'event': 'login_flow_error', + 'properties': { + 'app_id': appId, + 'login_provider': 'passkey', + 'error_message': errorMessage, + }, + }, + LoginProvider.google => { + 'event': 'login_flow_error', + 'properties': { + 'app_id': appId, + 'login_provider': 'google', + 'error_message': errorMessage, + }, + }, + LoginProvider.facebook => { + 'event': 'login_flow_error', + 'properties': { + 'app_id': appId, + 'login_provider': 'facebook', + 'error_message': errorMessage, + }, + }, + LoginProvider.apple => { + 'event': 'login_flow_error', + 'properties': { + 'app_id': appId, + 'login_provider': 'apple', + 'error_message': errorMessage, + }, + }, + null => throw Exception('Null LoginProvider is being passed.'), + }; diff --git a/packages/deriv_auth/lib/core/analytics/data/auth_tracking_repository.dart b/packages/deriv_auth/lib/core/analytics/data/auth_tracking_repository.dart new file mode 100644 index 000000000..6e898f633 --- /dev/null +++ b/packages/deriv_auth/lib/core/analytics/data/auth_tracking_repository.dart @@ -0,0 +1,132 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_auth/core/services/token/models/enums.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_data.dart'; +import 'package:deriv_auth/core/analytics/domain/auth_user_tracking_interface.dart'; +import 'package:deriv_auth/core/analytics/enums.dart'; + +/// Repository to track user events. +class AuthTrackingRepository implements AuthUserTrackingInterface { + /// Constructor for [AnalyticsRepository]. + AuthTrackingRepository._( + this._appId, + this._derivRudderstack, + ); + + static AuthTrackingRepository? _instance; + + /// Singleton instance of [AnalyticsRepository]. + static AuthTrackingRepository get instance => _instance ??= + throw Exception('AuthTrackingRepository is not initialized'); + + /// Instance of [DerivRudderstack]. + final DerivRudderstack _derivRudderstack; + + /// Initialize [AnalyticsRepository]. + static void init( + String appId, { + required DerivRudderstack derivRudderstack, + }) => + _instance = AuthTrackingRepository._( + appId, + derivRudderstack, + ); + + /// Deriv client app ID. + final String _appId; + + LoginProvider? _loginProvider; + + @override + void trackUserOpenedLoginForm() { + final Map data = getUserTrackingData( + LoginAction.openLoginForm, + _appId, + ); + + _derivRudderstack.track( + eventName: data['event'] as String, + properties: data['properties'] as Map, + ); + } + + @override + void trackError(String errorMessage) { + final Map data = getUserTrackingData( + LoginAction.loginError, + _appId, + provider: _loginProvider, + errorMessage: errorMessage, + ); + + _derivRudderstack.track( + eventName: data['event'] as String, + properties: data['properties'] as Map, + ); + } + + @override + void trackSystemLoginPressed() { + final Map data = getUserTrackingData( + LoginAction.startLogin, + _appId, + provider: LoginProvider.email, + ); + + _loginProvider = LoginProvider.email; + + _derivRudderstack.track( + eventName: data['event'] as String, + properties: data['properties'] as Map, + ); + } + + @override + void trackPasskeyLoginPressed() { + final Map data = getUserTrackingData( + LoginAction.startLogin, + _appId, + provider: LoginProvider.passkey, + ); + + _loginProvider = LoginProvider.passkey; + + _derivRudderstack.track( + eventName: data['event'] as String, + properties: data['properties'] as Map, + ); + } + + @override + void trackSocialLoginPressed(SocialAuthProvider type) { + _loginProvider = switch (type) { + SocialAuthProvider.google => LoginProvider.google, + SocialAuthProvider.facebook => LoginProvider.facebook, + SocialAuthProvider.apple => LoginProvider.apple, + }; + + final Map data = getUserTrackingData( + LoginAction.startLogin, + _appId, + provider: _loginProvider, + ); + + _derivRudderstack.track( + eventName: data['event'] as String, + properties: data['properties'] as Map, + ); + } + + @override + void trackLoginFinished() { + final Map data = getUserTrackingData( + LoginAction.finishLogin, + _appId, + provider: _loginProvider, + ); + + _derivRudderstack.track( + eventName: data['event'] as String, + properties: data['properties'] as Map, + ); + } +} diff --git a/packages/deriv_auth/lib/core/analytics/domain/auth_user_tracking_interface.dart b/packages/deriv_auth/lib/core/analytics/domain/auth_user_tracking_interface.dart new file mode 100644 index 000000000..3db7ab4f6 --- /dev/null +++ b/packages/deriv_auth/lib/core/analytics/domain/auth_user_tracking_interface.dart @@ -0,0 +1,24 @@ +import 'package:deriv_auth/deriv_auth.dart'; + +/// Interface to define the methods for tracking user events. +abstract interface class AuthUserTrackingInterface { + /// Track user opened login form. + void trackUserOpenedLoginForm(); + + /// Track user clicks log after entering email and password. + void trackSystemLoginPressed(); + + /// Track user clicks on Passkey login button. + void trackPasskeyLoginPressed(); + + /// Track user clicks on social login button. + void trackSocialLoginPressed( + SocialAuthProvider type, + ); + + /// Track login finished. + void trackLoginFinished(); + + /// Track login error. + void trackError(String errorMessage); +} diff --git a/packages/deriv_auth/lib/core/analytics/enums.dart b/packages/deriv_auth/lib/core/analytics/enums.dart new file mode 100644 index 000000000..7d0d6c118 --- /dev/null +++ b/packages/deriv_auth/lib/core/analytics/enums.dart @@ -0,0 +1,32 @@ +/// Enum for login actions +enum LoginAction { + /// Open login form. + openLoginForm, + + /// Start system login. + startLogin, + + /// Finish login. + finishLogin, + + /// Login error. + loginError, +} + +/// Enum for login provider. +enum LoginProvider { + /// Email login. + email, + + /// Passkey login. + passkey, + + /// Google login. + google, + + /// Facebook login. + facebook, + + /// Apple login. + apple, +} diff --git a/packages/deriv_auth/lib/core/analytics/service/auth_tracking_mixin.dart b/packages/deriv_auth/lib/core/analytics/service/auth_tracking_mixin.dart new file mode 100644 index 000000000..5c5c931de --- /dev/null +++ b/packages/deriv_auth/lib/core/analytics/service/auth_tracking_mixin.dart @@ -0,0 +1,36 @@ +import 'package:deriv_auth/deriv_auth.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_repository.dart'; + +/// Mixin for tracking user authentication actions. +mixin AuthTrackingMixin { + late final AuthTrackingRepository _repository = + AuthTrackingRepository.instance; + + /// Track user opened login form. + void trackUserOpenedLoginForm() => _repository.trackUserOpenedLoginForm(); + + /// Track login error. + void trackError(String errorMessage) => _repository.trackError(errorMessage); + + /// Track login started. + void trackLoginWithEmailAndPassword() => + _repository.trackSystemLoginPressed(); + + /// Track Passkey login started. + void trackLoginWithPasskey() => _repository.trackPasskeyLoginPressed(); + + /// Track Google login started. + void trackLoginWithGoogle() => + _repository.trackSocialLoginPressed(SocialAuthProvider.google); + + /// Track Facebook login started. + void trackLoginWithFacebook() => + _repository.trackSocialLoginPressed(SocialAuthProvider.facebook); + + /// Track Apple login started. + void trackLoginWithApple() => + _repository.trackSocialLoginPressed(SocialAuthProvider.apple); + + /// Track login finished. + void trackLoginFinished() => _repository.trackLoginFinished(); +} diff --git a/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart b/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart index 736fa9c3f..b6634b85c 100644 --- a/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart +++ b/packages/deriv_auth/lib/features/auth/cubit/deriv_auth_cubit.dart @@ -1,4 +1,7 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; import 'package:bloc/bloc.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_repository.dart'; +import 'package:deriv_auth/core/analytics/service/auth_tracking_mixin.dart'; import 'package:deriv_auth/core/exceptions/deriv_auth_exception.dart'; import 'package:deriv_auth/core/models/account_model.dart'; @@ -9,17 +12,27 @@ import 'package:deriv_auth/core/services/token/models/enums.dart'; import 'package:deriv_auth/core/services/token/models/login_request.dart'; import 'package:deriv_auth/features/auth/deriv_auth_io.dart'; import 'package:deriv_auth/features/auth/services/base_auth_service.dart'; +import 'package:deriv_auth/features/auth/services/deriv_auth_service.dart'; import 'package:deriv_auth/features/social_auth/models/social_auth_dto.dart'; part 'deriv_auth_state.dart'; /// This Cubit is the single source of truth for user login status, /// and it is responsible for all login functionality. -class DerivAuthCubit extends Cubit implements DerivAuthIO { +class DerivAuthCubit extends Cubit + with AuthTrackingMixin + implements DerivAuthIO { /// Initialize a [DerivAuthCubit]. DerivAuthCubit({ required this.authService, - }) : super(DerivAuthLoadingState()); + }) : super(DerivAuthLoadingState()) { + AuthTrackingRepository.init( + authService is DerivAuthService + ? (authService as DerivAuthService).connectionInfo.appId + : (throw Exception('Connection Info is not provided.')), + derivRudderstack: DerivRudderstack(), + ); + } /// [BaseAuthService] handles all login logic of cubit. final BaseAuthService authService; @@ -33,6 +46,8 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { String? otp, String? userAgent, }) async { + trackLoginWithEmailAndPassword(); + emit(DerivAuthLoadingState()); await _loginRequest( @@ -124,6 +139,9 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { final LandingCompanyEntity landingCompanyEntity = await authService.getLandingCompany(authorizeEntity.country); _isUserMigrated = _checkUserMigrated(authorizeEntity); + + trackLoginFinished(); + emit(DerivAuthLoggedInState( DerivAuthModel( authorizeEntity: authorizeEntity, @@ -151,6 +169,8 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { await authService.getLandingCompany(authorizeEntity.country); _isUserMigrated = _checkUserMigrated(authorizeEntity); + trackLoginFinished(); + emit( DerivAuthLoggedInState( DerivAuthModel( @@ -229,4 +249,15 @@ class DerivAuthCubit extends Cubit implements DerivAuthIO { @override Stream get output => stream; + + @override + void onChange(Change change) { + if (change.nextState is DerivAuthErrorState) { + trackError( + (change.nextState as DerivAuthErrorState).message, + ); + } + + super.onChange(change); + } } diff --git a/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart b/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart index 9470f1ea2..b631f2347 100644 --- a/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart +++ b/packages/deriv_auth/lib/features/auth/services/base_auth_service.dart @@ -5,6 +5,9 @@ import 'package:deriv_auth/core/services/token/models/login_request.dart'; /// Interface to define all authentication-related functionality. abstract class BaseAuthService { + /// Constructor for [BaseAuthService]. + BaseAuthService(); + /// Function before logging user in. Future onLoginRequest({ required GetTokensRequestModel request, diff --git a/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart b/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart index 689ad0367..5e790b903 100644 --- a/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart +++ b/packages/deriv_auth/lib/features/auth/services/deriv_auth_service.dart @@ -1,6 +1,6 @@ import 'package:collection/collection.dart'; - import 'package:deriv_auth/core/connection_info.dart'; + import 'package:deriv_auth/core/constants/constants.dart'; import 'package:deriv_auth/core/exceptions/deriv_auth_exception.dart'; import 'package:deriv_auth/core/extensions/extensions.dart'; diff --git a/packages/deriv_auth/lib/features/get_started/presentation/layouts/deriv_get_started_layout.dart b/packages/deriv_auth/lib/features/get_started/presentation/layouts/deriv_get_started_layout.dart index 07cdabe9e..f9ec33a6d 100644 --- a/packages/deriv_auth/lib/features/get_started/presentation/layouts/deriv_get_started_layout.dart +++ b/packages/deriv_auth/lib/features/get_started/presentation/layouts/deriv_get_started_layout.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'dart:math' as math; +import 'package:deriv_auth/core/analytics/service/auth_tracking_mixin.dart'; import 'package:deriv_auth/core/extensions/context_extension.dart'; import 'package:deriv_auth/core/helpers/semantic_labels.dart'; import 'package:deriv_auth/features/get_started/models/deriv_get_started_slide_model.dart'; @@ -48,7 +49,8 @@ class DerivGetStartedLayout extends StatefulWidget { State createState() => _DerivGetStartedLayoutState(); } -class _DerivGetStartedLayoutState extends State { +class _DerivGetStartedLayoutState extends State + with AuthTrackingMixin { static const Duration _autoScrollInterval = Duration(seconds: 4); static const Duration _scrollAnimationDuration = Duration(seconds: 1); static const Alignment _slideImageAlignment = Alignment.center; @@ -172,7 +174,10 @@ class _DerivGetStartedLayoutState extends State { explicitChildNodes: true, label: SemanticsLabels.starterPageLoginButtonSemantic, child: SecondaryButton( - onPressed: widget.onLoginTapped, + onPressed: () { + trackUserOpenedLoginForm(); + widget.onLoginTapped(); + }, child: Center( child: Text( context.derivAuthLocalization.actionLogin, diff --git a/packages/deriv_auth/lib/features/login/presentation/layouts/deriv_login_layout.dart b/packages/deriv_auth/lib/features/login/presentation/layouts/deriv_login_layout.dart index 9bf31f9a6..4c14f4800 100644 --- a/packages/deriv_auth/lib/features/login/presentation/layouts/deriv_login_layout.dart +++ b/packages/deriv_auth/lib/features/login/presentation/layouts/deriv_login_layout.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:deriv_auth/core/analytics/service/auth_tracking_mixin.dart'; import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_passkeys/deriv_passkeys.dart'; import 'package:deriv_theme/deriv_theme.dart'; @@ -100,7 +101,8 @@ class DerivLoginLayout extends StatefulWidget { State createState() => _DerivLoginLayoutState(); } -class _DerivLoginLayoutState extends State { +class _DerivLoginLayoutState extends State + with AuthTrackingMixin { final GlobalKey _formKey = GlobalKey(); final TextEditingController _emailController = TextEditingController(); @@ -131,7 +133,6 @@ class _DerivLoginLayoutState extends State { centerTitle: false, ), body: BlocConsumer( - bloc: authCubit, listener: _onAuthState, builder: (BuildContext context, DerivAuthState state) => Form( key: _formKey, @@ -162,7 +163,9 @@ class _DerivLoginLayoutState extends State { if (widget.isSocialAuthEnabled) const SizedBox(height: ThemeProvider.margin24), widget.isPasskeysEnabled - ? const ContinueWithPasskeyButton() + ? ContinueWithPasskeyButton( + onTap: trackLoginWithPasskey, + ) : const SizedBox.shrink(), DerivSocialAuthPanel( socialAuthStateHandler: widget.socialAuthStateHandler, diff --git a/packages/deriv_auth/lib/features/social_auth/presentation/widgets/deriv_social_auth_panel.dart b/packages/deriv_auth/lib/features/social_auth/presentation/widgets/deriv_social_auth_panel.dart index ea2241f4e..b8f4f32b6 100644 --- a/packages/deriv_auth/lib/features/social_auth/presentation/widgets/deriv_social_auth_panel.dart +++ b/packages/deriv_auth/lib/features/social_auth/presentation/widgets/deriv_social_auth_panel.dart @@ -1,3 +1,4 @@ +import 'package:deriv_auth/core/analytics/service/auth_tracking_mixin.dart'; import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:flutter/material.dart'; @@ -48,7 +49,8 @@ class DerivSocialAuthPanel extends StatefulWidget { State createState() => _DerivSocialAuthPanelState(); } -class _DerivSocialAuthPanelState extends State { +class _DerivSocialAuthPanelState extends State + with AuthTrackingMixin { late SocialAuthCubit _socialAuthCubit; @override @@ -121,6 +123,19 @@ class _DerivSocialAuthPanelState extends State { ), onTap: widget.isEnabled ? () async { + switch (socialAuthProvider) { + case SocialAuthProvider.google: + trackLoginWithGoogle(); + break; + case SocialAuthProvider.facebook: + trackLoginWithFacebook(); + break; + case SocialAuthProvider.apple: + trackLoginWithApple(); + break; + default: + break; + } final List? socialAuthProviders = await _socialAuthCubit.getSocialAuthProviders(); diff --git a/packages/deriv_auth/pubspec.yaml b/packages/deriv_auth/pubspec.yaml index 1f953e4b9..911956f83 100644 --- a/packages/deriv_auth/pubspec.yaml +++ b/packages/deriv_auth/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_auth description: Provides deriv authentication functionalities for dart/flutter apps. -version: 6.6.7 +version: 6.7.6 environment: sdk: ">=3.0.0 <4.0.0" @@ -12,6 +12,12 @@ dependencies: flutter: sdk: flutter + analytics: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/analytics + ref: analytics-v2.1.0 + deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git @@ -22,7 +28,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.7+6 + ref: deriv_ui-v0.0.7+9 deriv_http_client: git: @@ -39,25 +45,25 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_web_view - ref: deriv_web_view-v0.2.2+1 + ref: deriv_web_view-v0.2.2+3 deriv_localizations: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_localizations - ref: deriv_localizations-v1.4.4 + ref: deriv_localizations-v1.5.1 deriv_passkeys: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_passkeys - ref: deriv_passkeys-v0.0.1+8 + ref: deriv_passkeys-v0.0.2+8 deriv_language_selector: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_language_selector - ref: deriv_language_selector-v0.0.2+4 + ref: deriv_language_selector-v0.0.2+7 flutter_bloc: ^8.1.3 flutter_svg: ^2.0.7 @@ -73,7 +79,7 @@ dependency_overrides: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.7+6 + ref: deriv_ui-v0.0.7+9 dev_dependencies: mocktail: ^1.0.3 diff --git a/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart b/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart index b7a9a0403..cf14654cd 100644 --- a/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart +++ b/packages/deriv_auth/test/features/auth/cubit/deriv_auth_cubit_test.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; import 'package:bloc_test/bloc_test.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_repository.dart'; import 'package:deriv_auth/core/exceptions/deriv_auth_exception.dart'; import 'package:deriv_auth/core/models/account_model.dart'; import 'package:deriv_auth/core/models/auth_error/auth_error.dart'; @@ -17,11 +19,28 @@ import '../mocked_data/mocked_auth_models.dart'; class MockAuthService extends Mock implements BaseAuthService {} +class MockDerivRudderStack extends Mock implements DerivRudderstack {} + void main() { + late final MockDerivRudderStack mockDerivRudderstack; + late final DerivAuthCubit authCubit; late final MockAuthService service; setUpAll(() async { + mockDerivRudderstack = MockDerivRudderStack(); + + AuthTrackingRepository.init( + 'test', + derivRudderstack: mockDerivRudderstack, + ); + + when(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'))).thenAnswer( + (_) => Future.value(true), + ); + service = MockAuthService(); authCubit = DerivAuthCubit(authService: service); }); diff --git a/packages/deriv_auth/test/features/get_started/presentation/layouts/deriv_get_started_layout_test.dart b/packages/deriv_auth/test/features/get_started/presentation/layouts/deriv_get_started_layout_test.dart index 23aa26b08..3e9271705 100644 --- a/packages/deriv_auth/test/features/get_started/presentation/layouts/deriv_get_started_layout_test.dart +++ b/packages/deriv_auth/test/features/get_started/presentation/layouts/deriv_get_started_layout_test.dart @@ -1,6 +1,8 @@ // ignore_for_file: always_specify_types +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; import 'package:bloc_test/bloc_test.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_repository.dart'; import 'package:deriv_auth/features/get_started/models/deriv_get_started_slide_model.dart'; import 'package:deriv_auth/features/get_started/presentation/layouts/deriv_get_started_layout.dart'; import 'package:deriv_language_selector/deriv_language_selector.dart'; @@ -14,6 +16,8 @@ import 'package:patrol_finders/patrol_finders.dart'; import '../../../../pump_app.dart'; +class MockDerivRudderStack extends Mock implements DerivRudderstack {} + class MockDerivGetStartedSlideModel extends Mock implements DerivGetStartedSlideModel {} @@ -22,6 +26,8 @@ class MockLanguageCubit extends MockCubit void main() { group('DerivGetStartedLayout', () { + late final MockDerivRudderStack mockDerivRudderstack; + late MockDerivGetStartedSlideModel mockSlideModel; const String appLogoIconPath = 'assets/icons/ic_logo_extended.svg'; @@ -31,6 +37,23 @@ void main() { setUpAll(() { mockSlideModel = MockDerivGetStartedSlideModel(); + + mockDerivRudderstack = MockDerivRudderStack(); + + AuthTrackingRepository.init( + 'test', + derivRudderstack: mockDerivRudderstack, + ); + + when(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'))).thenAnswer( + (_) => Future.value(true), + ); + + when(() => mockSlideModel.imagePath) + .thenReturn('assets/images/charts.svg'); + when(() => mockSlideModel.supportingText).thenReturn('Supporting text'); when(() => mockSlideModel.imagePath) .thenReturn('assets/images/charts.svg'); when(() => mockSlideModel.supportingText).thenReturn('Supporting text'); diff --git a/packages/deriv_auth/test/features/login/presentation/layouts/deriv_login_layout_test.dart b/packages/deriv_auth/test/features/login/presentation/layouts/deriv_login_layout_test.dart index 9d8ddba69..d1bedacdb 100644 --- a/packages/deriv_auth/test/features/login/presentation/layouts/deriv_login_layout_test.dart +++ b/packages/deriv_auth/test/features/login/presentation/layouts/deriv_login_layout_test.dart @@ -1,4 +1,6 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; import 'package:bloc_test/bloc_test.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_repository.dart'; import 'package:deriv_auth/core/models/landig_comany_model.dart'; import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_passkeys/deriv_passkeys.dart'; @@ -18,8 +20,11 @@ class MockDerivPasskeysBloc extends MockBloc implements DerivPasskeysBloc {} +class MockDerivRudderStackService extends Mock implements DerivRudderstack {} + void main() { group('DerivLoginLayout', () { + late MockDerivRudderStackService mockDerivRudderstack; late MockAuthCubit authCubit; late MockSocialAuthCubit socialAuthCubit; late MockDerivPasskeysBloc derivPasskeysBloc; @@ -27,10 +32,23 @@ void main() { const String welcomeLabel = 'Welcome Back'; setUpAll(() { + mockDerivRudderstack = MockDerivRudderStackService(); authCubit = MockAuthCubit(); socialAuthCubit = MockSocialAuthCubit(); derivPasskeysBloc = MockDerivPasskeysBloc(); + AuthTrackingRepository.init( + 'test', + derivRudderstack: mockDerivRudderstack, + ); + + when(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).thenAnswer( + (_) => Future.value(true), + ); + when(() => derivPasskeysBloc.state).thenReturn( DerivPasskeysInitializedState(), ); diff --git a/packages/deriv_auth/test/features/signup/presentation/layouts/deriv_signup_layout_test.dart b/packages/deriv_auth/test/features/signup/presentation/layouts/deriv_signup_layout_test.dart index e48555b03..bcd5aec8a 100644 --- a/packages/deriv_auth/test/features/signup/presentation/layouts/deriv_signup_layout_test.dart +++ b/packages/deriv_auth/test/features/signup/presentation/layouts/deriv_signup_layout_test.dart @@ -1,3 +1,5 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_auth/core/analytics/data/auth_tracking_repository.dart'; import 'package:deriv_auth/deriv_auth.dart'; import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; @@ -11,8 +13,11 @@ import '../../../../mocks.dart'; import '../../../../pump_app.dart'; import '../../../social_auth/mocks/mock_social_provider_model.dart'; +class MockDerivRudderStack extends Mock implements DerivRudderstack {} + void main() { group('DerivSignupLayout', () { + late final MockDerivRudderStack mockDerivRudderstack; late MockSignupCubit signupCubit; late MockAuthCubit authCubit; late MockSocialAuthCubit socialAuthCubit; @@ -25,6 +30,19 @@ void main() { authCubit = MockAuthCubit(); socialAuthCubit = MockSocialAuthCubit(); + mockDerivRudderstack = MockDerivRudderStack(); + + AuthTrackingRepository.init( + 'test', + derivRudderstack: mockDerivRudderstack, + ); + + when(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'))).thenAnswer( + (_) => Future.value(true), + ); + registerFallbackValue(SocialAuthProvider.google); when(() => signupCubit.state) diff --git a/packages/deriv_language_selector/CHANGELOG.md b/packages/deriv_language_selector/CHANGELOG.md index 4eb2f1faf..042e415f2 100644 --- a/packages/deriv_language_selector/CHANGELOG.md +++ b/packages/deriv_language_selector/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.0.2+7 + + - Update a dependency to the latest release. + +## 0.0.2+6 + + - Update a dependency to the latest release. + +## 0.0.2+5 + + - Update a dependency to the latest release. + ## 0.0.2+4 - Update a dependency to the latest release. diff --git a/packages/deriv_language_selector/pubspec.yaml b/packages/deriv_language_selector/pubspec.yaml index 3b0c952eb..65dbe3b48 100644 --- a/packages/deriv_language_selector/pubspec.yaml +++ b/packages/deriv_language_selector/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_language_selector description: A package to select language for the app. It provides both UI and logic for language selection. -version: 0.0.2+4 +version: 0.0.2+7 publish_to: "none" environment: @@ -14,7 +14,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.7+6 + ref: deriv_ui-v0.0.7+9 shared_preferences: ^2.2.2 flutter_bloc: ^8.1.4 equatable: ^2.0.5 diff --git a/packages/deriv_localizations/CHANGELOG.md b/packages/deriv_localizations/CHANGELOG.md index d162f2858..302a624c8 100644 --- a/packages/deriv_localizations/CHANGELOG.md +++ b/packages/deriv_localizations/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.5.1 + + - **REFACTOR**(deriv_localizations): Crowdin Localization Generated ([#648](https://github.com/regentmarkets/flutter-deriv-packages/issues/648)). ([c925c22a](https://github.com/regentmarkets/flutter-deriv-packages/commit/c925c22a97e6e13568fefa0d86d993985093617c)) + +## 1.5.0 + + - **FEAT**(deriv_localizations): add localization for deriv mobile chart wrapper package ([#627](https://github.com/regentmarkets/flutter-deriv-packages/issues/627)). ([33f5e3a1](https://github.com/regentmarkets/flutter-deriv-packages/commit/33f5e3a1bc0765cb8559b5a39e300b8c088aa705)) + ## 1.4.4 - **REFACTOR**(deriv_localizations): Crowdin Localization Generated ([#617](https://github.com/regentmarkets/flutter-deriv-packages/issues/617)). ([1d5e5f14](https://github.com/regentmarkets/flutter-deriv-packages/commit/1d5e5f141640aa00546dcbb31d2db8eb9a994452)) diff --git a/packages/deriv_localizations/l10n.sh b/packages/deriv_localizations/l10n.sh index b825291b8..bf2e9fe61 100755 --- a/packages/deriv_localizations/l10n.sh +++ b/packages/deriv_localizations/l10n.sh @@ -2,7 +2,7 @@ # Directories containing ARB files -feature_dirs=("deriv_auth" "deriv_passkeys") +feature_dirs=("deriv_auth" "deriv_passkeys" "deriv_mobile_chart_wrapper") # Base localization directory base_l10n_dir="lib/l10n" @@ -29,14 +29,23 @@ done ls $base_l10n_dir/generated -echo "Localization generation complete." - - # Add the generated localization files to Git git add "$base_l10n_dir/generated" +# Create deriv_localizations.dart barrel file under lib folder. +localizations_file="lib/deriv_localizations.dart" +echo "// Generated file for exporting localization classes" > $localizations_file + +for dir in "${feature_dirs[@]}"; do + echo "export 'l10n/generated/$dir/${dir}_localizations.dart';" >> $localizations_file +done + +echo "Created deriv_localizations.dart barrel file with exports." + +# Add and commit the deriv_localizations.dart barrel file to Git +git add $localizations_file + # Commit the changes git commit -m "Update localizations" - echo "Localization generation complete." diff --git a/packages/deriv_localizations/lib/deriv_localizations.dart b/packages/deriv_localizations/lib/deriv_localizations.dart new file mode 100644 index 000000000..72b4f56fb --- /dev/null +++ b/packages/deriv_localizations/lib/deriv_localizations.dart @@ -0,0 +1,4 @@ +// Generated file for exporting localization classes +export 'l10n/generated/deriv_auth/deriv_auth_localizations.dart'; +export 'l10n/generated/deriv_passkeys/deriv_passkeys_localizations.dart'; +export 'l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart'; diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ar.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ar.arb new file mode 100644 index 000000000..1941c81f8 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ar.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "المؤشرات" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_bn.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_bn.arb new file mode 100644 index 000000000..c406ace5d --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_bn.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "সূচক" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_de.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_de.arb new file mode 100644 index 000000000..ca7b35187 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_de.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Indikatoren" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_en.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_en.arb new file mode 100644 index 000000000..d3fba5184 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_en.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Indicators" +} diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_es.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_es.arb new file mode 100644 index 000000000..9503f6aab --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_es.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Indicadores" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_fr.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_fr.arb new file mode 100644 index 000000000..9a78c8fb2 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_fr.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Indicateurs" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_it.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_it.arb new file mode 100644 index 000000000..7dae053d1 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_it.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Indicatori" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ko.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ko.arb new file mode 100644 index 000000000..1df3fd1b7 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ko.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "지표" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_pl.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_pl.arb new file mode 100644 index 000000000..91d1d83c1 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_pl.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Wskaźniki" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_pt.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_pt.arb new file mode 100644 index 000000000..9503f6aab --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_pt.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Indicadores" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ru.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ru.arb new file mode 100644 index 000000000..4164941a3 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_ru.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Индикаторы" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_si.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_si.arb new file mode 100644 index 000000000..2571da265 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_si.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "දර්ශක" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_sw.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_sw.arb new file mode 100644 index 000000000..c047b5c14 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_sw.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Viashiria" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_th.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_th.arb new file mode 100644 index 000000000..dd7623f15 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_th.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "ตัวบ่งชี้" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_tr.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_tr.arb new file mode 100644 index 000000000..0dc2ae764 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_tr.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Göstergeler" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_vi.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_vi.arb new file mode 100644 index 000000000..f6ce868bf --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_vi.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "Các chỉ số" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_zh.arb b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_zh.arb new file mode 100644 index 000000000..4704206cc --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/deriv_mobile_chart_wrapper/app_zh.arb @@ -0,0 +1,3 @@ +{ + "labelIndicators": "指標" +} \ No newline at end of file diff --git a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_pt.arb b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_pt.arb index dc5e97cbb..97df1c81e 100644 --- a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_pt.arb +++ b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_pt.arb @@ -26,9 +26,9 @@ "howToCreatePasskey": "Como criar uma chave de acesso?", "howToCreatePasskeyDescription1": "Aceda às \"Definições de conta\" na Deriv.", "howToCreatePasskeyDescription2": "Pode criar uma passkey por dispositivo.", - "p2pHowToCreatePasskey": "Como criar uma chave de acesso?", - "p2pHowToCreatePasskeyDescription1": "Vá para 'Perfil' em seu aplicativo Deriv P2P.", - "p2pHowToCreatePasskeyDescription2": "Toque em “Chaves de acesso” para criar sua chave de acesso.", + "p2pHowToCreatePasskey": "Como pode criar uma passkey?", + "p2pHowToCreatePasskeyDescription1": "Aceda à secção \"Perfil\" na sua aplicação Deriv P2P.", + "p2pHowToCreatePasskeyDescription2": "Selecione \"Passkeys\" para criar a sua chave de acesso.", "whereArePasskeysSaved": "Onde são guardadas as chaves de acesso?", "whereArePasskeysSavedDescriptionAndroid": "Android: Gestor de palavras-passe do Google.", "whereArePasskeysSavedDescriptionIOS": "iOS: iCloud keychain.", diff --git a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_th.arb b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_th.arb index b4a8d5c12..60425dc8b 100644 --- a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_th.arb +++ b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_th.arb @@ -26,9 +26,9 @@ "howToCreatePasskey": "จะสร้าง Passkey ได้อย่างไร?", "howToCreatePasskeyDescription1": "ไปที่ 'การตั้งค่าบัญชี' บน Deriv", "howToCreatePasskeyDescription2": "คุณสามารถสร้างหนึ่ง Passkey ต่ออุปกรณ์", - "p2pHowToCreatePasskey": "วิธีการสร้างรหัสผ่าน", + "p2pHowToCreatePasskey": "จะสร้าง Passkey ได้อย่างไร?", "p2pHowToCreatePasskeyDescription1": "ไปที่ 'โปรไฟล์' ในแอป Deriv P2P ของคุณ", - "p2pHowToCreatePasskeyDescription2": "แตะ 'รหัสผ่าน' เพื่อสร้างรหัสผ่านของคุณ", + "p2pHowToCreatePasskeyDescription2": "แตะ 'Passkeys' เพื่อสร้าง Passkey ของคุณ", "whereArePasskeysSaved": "Passkey จะถูกบันทึกไว้ที่ไหน?", "whereArePasskeysSavedDescriptionAndroid": "Android: ตัวจัดการรหัสผ่าน Google", "whereArePasskeysSavedDescriptionIOS": "iOS: พวงกุญแจ iCloud", diff --git a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_tr.arb b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_tr.arb index 5fd54dbdc..d6b3ed6b1 100644 --- a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_tr.arb +++ b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_tr.arb @@ -26,9 +26,9 @@ "howToCreatePasskey": "Passkey nasıl oluşturulur?", "howToCreatePasskeyDescription1": "Deriv'de 'Hesap Ayarları'na gidin.", "howToCreatePasskeyDescription2": "Cihaz başına bir passkey oluşturabilirsiniz.", - "p2pHowToCreatePasskey": "Parola nasıl oluşturulur?", + "p2pHowToCreatePasskey": "Passkey nasıl oluşturulur?", "p2pHowToCreatePasskeyDescription1": "Deriv P2P uygulamanızdaki 'Profil' bölümüne gidin.", - "p2pHowToCreatePasskeyDescription2": "Parola anahtarınızı oluşturmak için 'Parola Tuşları'na dokunun.", + "p2pHowToCreatePasskeyDescription2": "Passkey'inizi oluşturmak için \"Passkeys\" ögesine dokunun.", "whereArePasskeysSaved": "Passkeys nereye kaydedilir?", "whereArePasskeysSavedDescriptionAndroid": "Android: Google şifre yöneticisi.", "whereArePasskeysSavedDescriptionIOS": "iOS: iCloud anahtar zinciri.", @@ -55,7 +55,7 @@ "rename": "Yeniden Adlandır", "revoke": "İptal", "continueTradingButtonText": "Alım satıma devam", - "addMorePasskeysButtonText": "Daha fazla passkeys", + "addMorePasskeysButtonText": "Daha fazla passkeys ekle", "unableToSetupPasskey": "Passkey ayarlanamıyor", "unableToSetupPasskeyDescription": "Passkey ayarlarken bir sorunla karşılaştık. İşlem yarıda kesilmiş veya oturum zaman aşımına uğramış olabilir. Lütfen tekrar deneyin.", "passkeysOffErrorTitle": "Passkeys hizmeti kullanılamıyor", diff --git a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_zh.arb b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_zh.arb index a49322481..670eaea44 100644 --- a/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_zh.arb +++ b/packages/deriv_localizations/lib/l10n/deriv_passkeys/app_zh.arb @@ -26,9 +26,9 @@ "howToCreatePasskey": "如何建立金鑰?", "howToCreatePasskeyDescription1": "轉到 Deriv 的‘帳戶設定‘。", "howToCreatePasskeyDescription2": "可以為每個裝置建立一個金鑰。", - "p2pHowToCreatePasskey": "如何建立密碼?", - "p2pHowToCreatePasskeyDescription1": "在您的 Deriv P2P 應用程序中轉到「個人資料」。", - "p2pHowToCreatePasskeyDescription2": "點擊「密碼」以建立您的密碼。", + "p2pHowToCreatePasskey": "如何建立金鑰?", + "p2pHowToCreatePasskeyDescription1": "在 Deriv P2P 應用程式中轉到‘個人資料‘。", + "p2pHowToCreatePasskeyDescription2": "點選‘密鑰‘以建立密鑰。", "whereArePasskeysSaved": "金鑰儲存在哪裡?", "whereArePasskeysSavedDescriptionAndroid": "Android: Google 密碼管理器。", "whereArePasskeysSavedDescriptionIOS": "iOS:iCloud 鑰匙圈。", diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart new file mode 100644 index 000000000..7140049d1 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart @@ -0,0 +1,178 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'deriv_mobile_chart_wrapper_localizations_ar.dart'; +import 'deriv_mobile_chart_wrapper_localizations_bn.dart'; +import 'deriv_mobile_chart_wrapper_localizations_de.dart'; +import 'deriv_mobile_chart_wrapper_localizations_en.dart'; +import 'deriv_mobile_chart_wrapper_localizations_es.dart'; +import 'deriv_mobile_chart_wrapper_localizations_fr.dart'; +import 'deriv_mobile_chart_wrapper_localizations_it.dart'; +import 'deriv_mobile_chart_wrapper_localizations_ko.dart'; +import 'deriv_mobile_chart_wrapper_localizations_pl.dart'; +import 'deriv_mobile_chart_wrapper_localizations_pt.dart'; +import 'deriv_mobile_chart_wrapper_localizations_ru.dart'; +import 'deriv_mobile_chart_wrapper_localizations_si.dart'; +import 'deriv_mobile_chart_wrapper_localizations_sw.dart'; +import 'deriv_mobile_chart_wrapper_localizations_th.dart'; +import 'deriv_mobile_chart_wrapper_localizations_tr.dart'; +import 'deriv_mobile_chart_wrapper_localizations_vi.dart'; +import 'deriv_mobile_chart_wrapper_localizations_zh.dart'; + +/// Callers can lookup localized strings with an instance of DerivMobileChartWrapperLocalizations +/// returned by `DerivMobileChartWrapperLocalizations.of(context)`. +/// +/// Applications need to include `DerivMobileChartWrapperLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: DerivMobileChartWrapperLocalizations.localizationsDelegates, +/// supportedLocales: DerivMobileChartWrapperLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the DerivMobileChartWrapperLocalizations.supportedLocales +/// property. +abstract class DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static DerivMobileChartWrapperLocalizations of(BuildContext context) { + return Localizations.of(context, DerivMobileChartWrapperLocalizations)!; + } + + static const LocalizationsDelegate delegate = _DerivMobileChartWrapperLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('ar'), + Locale('bn'), + Locale('de'), + Locale('en'), + Locale('es'), + Locale('fr'), + Locale('it'), + Locale('ko'), + Locale('pl'), + Locale('pt'), + Locale('ru'), + Locale('si'), + Locale('sw'), + Locale('th'), + Locale('tr'), + Locale('vi'), + Locale('zh') + ]; + + /// No description provided for @labelIndicators. + /// + /// In en, this message translates to: + /// **'Indicators'** + String get labelIndicators; +} + +class _DerivMobileChartWrapperLocalizationsDelegate extends LocalizationsDelegate { + const _DerivMobileChartWrapperLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupDerivMobileChartWrapperLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => ['ar', 'bn', 'de', 'en', 'es', 'fr', 'it', 'ko', 'pl', 'pt', 'ru', 'si', 'sw', 'th', 'tr', 'vi', 'zh'].contains(locale.languageCode); + + @override + bool shouldReload(_DerivMobileChartWrapperLocalizationsDelegate old) => false; +} + +DerivMobileChartWrapperLocalizations lookupDerivMobileChartWrapperLocalizations(Locale locale) { + + + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'ar': return DerivMobileChartWrapperLocalizationsAr(); + case 'bn': return DerivMobileChartWrapperLocalizationsBn(); + case 'de': return DerivMobileChartWrapperLocalizationsDe(); + case 'en': return DerivMobileChartWrapperLocalizationsEn(); + case 'es': return DerivMobileChartWrapperLocalizationsEs(); + case 'fr': return DerivMobileChartWrapperLocalizationsFr(); + case 'it': return DerivMobileChartWrapperLocalizationsIt(); + case 'ko': return DerivMobileChartWrapperLocalizationsKo(); + case 'pl': return DerivMobileChartWrapperLocalizationsPl(); + case 'pt': return DerivMobileChartWrapperLocalizationsPt(); + case 'ru': return DerivMobileChartWrapperLocalizationsRu(); + case 'si': return DerivMobileChartWrapperLocalizationsSi(); + case 'sw': return DerivMobileChartWrapperLocalizationsSw(); + case 'th': return DerivMobileChartWrapperLocalizationsTh(); + case 'tr': return DerivMobileChartWrapperLocalizationsTr(); + case 'vi': return DerivMobileChartWrapperLocalizationsVi(); + case 'zh': return DerivMobileChartWrapperLocalizationsZh(); + } + + throw FlutterError( + 'DerivMobileChartWrapperLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.' + ); +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ar.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ar.dart new file mode 100644 index 000000000..8b345e8b6 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ar.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Arabic (`ar`). +class DerivMobileChartWrapperLocalizationsAr extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsAr([String locale = 'ar']) : super(locale); + + @override + String get labelIndicators => 'المؤشرات'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_bn.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_bn.dart new file mode 100644 index 000000000..648ddc85f --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_bn.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Bengali Bangla (`bn`). +class DerivMobileChartWrapperLocalizationsBn extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsBn([String locale = 'bn']) : super(locale); + + @override + String get labelIndicators => 'সূচক'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_de.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_de.dart new file mode 100644 index 000000000..c3aadd6f9 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_de.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for German (`de`). +class DerivMobileChartWrapperLocalizationsDe extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsDe([String locale = 'de']) : super(locale); + + @override + String get labelIndicators => 'Indikatoren'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_en.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_en.dart new file mode 100644 index 000000000..4cb6f2961 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_en.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for English (`en`). +class DerivMobileChartWrapperLocalizationsEn extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get labelIndicators => 'Indicators'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_es.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_es.dart new file mode 100644 index 000000000..728fbda31 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_es.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Spanish Castilian (`es`). +class DerivMobileChartWrapperLocalizationsEs extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsEs([String locale = 'es']) : super(locale); + + @override + String get labelIndicators => 'Indicadores'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_fr.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_fr.dart new file mode 100644 index 000000000..ca6942d0b --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_fr.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for French (`fr`). +class DerivMobileChartWrapperLocalizationsFr extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsFr([String locale = 'fr']) : super(locale); + + @override + String get labelIndicators => 'Indicateurs'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_it.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_it.dart new file mode 100644 index 000000000..a971c59fe --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_it.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Italian (`it`). +class DerivMobileChartWrapperLocalizationsIt extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsIt([String locale = 'it']) : super(locale); + + @override + String get labelIndicators => 'Indicatori'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ko.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ko.dart new file mode 100644 index 000000000..367cb3365 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ko.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Korean (`ko`). +class DerivMobileChartWrapperLocalizationsKo extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsKo([String locale = 'ko']) : super(locale); + + @override + String get labelIndicators => '지표'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_pl.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_pl.dart new file mode 100644 index 000000000..6c1d04203 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_pl.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Polish (`pl`). +class DerivMobileChartWrapperLocalizationsPl extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsPl([String locale = 'pl']) : super(locale); + + @override + String get labelIndicators => 'Wskaźniki'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_pt.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_pt.dart new file mode 100644 index 000000000..ccdb68863 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_pt.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Portuguese (`pt`). +class DerivMobileChartWrapperLocalizationsPt extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsPt([String locale = 'pt']) : super(locale); + + @override + String get labelIndicators => 'Indicadores'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ru.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ru.dart new file mode 100644 index 000000000..38471e29b --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_ru.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Russian (`ru`). +class DerivMobileChartWrapperLocalizationsRu extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsRu([String locale = 'ru']) : super(locale); + + @override + String get labelIndicators => 'Индикаторы'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_si.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_si.dart new file mode 100644 index 000000000..a536c05cb --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_si.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Sinhala Sinhalese (`si`). +class DerivMobileChartWrapperLocalizationsSi extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsSi([String locale = 'si']) : super(locale); + + @override + String get labelIndicators => 'දර්ශක'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_sw.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_sw.dart new file mode 100644 index 000000000..8f0aebce6 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_sw.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Swahili (`sw`). +class DerivMobileChartWrapperLocalizationsSw extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsSw([String locale = 'sw']) : super(locale); + + @override + String get labelIndicators => 'Viashiria'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_th.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_th.dart new file mode 100644 index 000000000..e7ff1cf14 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_th.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Thai (`th`). +class DerivMobileChartWrapperLocalizationsTh extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsTh([String locale = 'th']) : super(locale); + + @override + String get labelIndicators => 'ตัวบ่งชี้'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_tr.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_tr.dart new file mode 100644 index 000000000..fdb1cf932 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_tr.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Turkish (`tr`). +class DerivMobileChartWrapperLocalizationsTr extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsTr([String locale = 'tr']) : super(locale); + + @override + String get labelIndicators => 'Göstergeler'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_vi.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_vi.dart new file mode 100644 index 000000000..d15440fb2 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_vi.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Vietnamese (`vi`). +class DerivMobileChartWrapperLocalizationsVi extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsVi([String locale = 'vi']) : super(locale); + + @override + String get labelIndicators => 'Các chỉ số'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_zh.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_zh.dart new file mode 100644 index 000000000..ce16903a5 --- /dev/null +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations_zh.dart @@ -0,0 +1,9 @@ +import 'deriv_mobile_chart_wrapper_localizations.dart'; + +/// The translations for Chinese (`zh`). +class DerivMobileChartWrapperLocalizationsZh extends DerivMobileChartWrapperLocalizations { + DerivMobileChartWrapperLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get labelIndicators => '指標'; +} diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_pt.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_pt.dart index 962bf194d..e8d959a8c 100644 --- a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_pt.dart +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_pt.dart @@ -64,13 +64,13 @@ class DerivPasskeysLocalizationsPt extends DerivPasskeysLocalizations { String get howToCreatePasskeyDescription2 => 'Pode criar uma passkey por dispositivo.'; @override - String get p2pHowToCreatePasskey => 'Como criar uma chave de acesso?'; + String get p2pHowToCreatePasskey => 'Como pode criar uma passkey?'; @override - String get p2pHowToCreatePasskeyDescription1 => 'Vá para \'Perfil\' em seu aplicativo Deriv P2P.'; + String get p2pHowToCreatePasskeyDescription1 => 'Aceda à secção \"Perfil\" na sua aplicação Deriv P2P.'; @override - String get p2pHowToCreatePasskeyDescription2 => 'Toque em “Chaves de acesso” para criar sua chave de acesso.'; + String get p2pHowToCreatePasskeyDescription2 => 'Selecione \"Passkeys\" para criar a sua chave de acesso.'; @override String get whereArePasskeysSaved => 'Onde são guardadas as chaves de acesso?'; diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_th.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_th.dart index 283e39200..b48f49d11 100644 --- a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_th.dart +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_th.dart @@ -64,13 +64,13 @@ class DerivPasskeysLocalizationsTh extends DerivPasskeysLocalizations { String get howToCreatePasskeyDescription2 => 'คุณสามารถสร้างหนึ่ง Passkey ต่ออุปกรณ์'; @override - String get p2pHowToCreatePasskey => 'วิธีการสร้างรหัสผ่าน'; + String get p2pHowToCreatePasskey => 'จะสร้าง Passkey ได้อย่างไร?'; @override String get p2pHowToCreatePasskeyDescription1 => 'ไปที่ \'โปรไฟล์\' ในแอป Deriv P2P ของคุณ'; @override - String get p2pHowToCreatePasskeyDescription2 => 'แตะ \'รหัสผ่าน\' เพื่อสร้างรหัสผ่านของคุณ'; + String get p2pHowToCreatePasskeyDescription2 => 'แตะ \'Passkeys\' เพื่อสร้าง Passkey ของคุณ'; @override String get whereArePasskeysSaved => 'Passkey จะถูกบันทึกไว้ที่ไหน?'; diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_tr.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_tr.dart index 00d289dbc..ee9c2d250 100644 --- a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_tr.dart +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_tr.dart @@ -64,13 +64,13 @@ class DerivPasskeysLocalizationsTr extends DerivPasskeysLocalizations { String get howToCreatePasskeyDescription2 => 'Cihaz başına bir passkey oluşturabilirsiniz.'; @override - String get p2pHowToCreatePasskey => 'Parola nasıl oluşturulur?'; + String get p2pHowToCreatePasskey => 'Passkey nasıl oluşturulur?'; @override String get p2pHowToCreatePasskeyDescription1 => 'Deriv P2P uygulamanızdaki \'Profil\' bölümüne gidin.'; @override - String get p2pHowToCreatePasskeyDescription2 => 'Parola anahtarınızı oluşturmak için \'Parola Tuşları\'na dokunun.'; + String get p2pHowToCreatePasskeyDescription2 => 'Passkey\'inizi oluşturmak için \"Passkeys\" ögesine dokunun.'; @override String get whereArePasskeysSaved => 'Passkeys nereye kaydedilir?'; @@ -151,7 +151,7 @@ class DerivPasskeysLocalizationsTr extends DerivPasskeysLocalizations { String get continueTradingButtonText => 'Alım satıma devam'; @override - String get addMorePasskeysButtonText => 'Daha fazla passkeys'; + String get addMorePasskeysButtonText => 'Daha fazla passkeys ekle'; @override String get unableToSetupPasskey => 'Passkey ayarlanamıyor'; diff --git a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_zh.dart b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_zh.dart index 6fd607651..8684b4fc8 100644 --- a/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_zh.dart +++ b/packages/deriv_localizations/lib/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_zh.dart @@ -64,13 +64,13 @@ class DerivPasskeysLocalizationsZh extends DerivPasskeysLocalizations { String get howToCreatePasskeyDescription2 => '可以為每個裝置建立一個金鑰。'; @override - String get p2pHowToCreatePasskey => '如何建立密碼?'; + String get p2pHowToCreatePasskey => '如何建立金鑰?'; @override - String get p2pHowToCreatePasskeyDescription1 => '在您的 Deriv P2P 應用程序中轉到「個人資料」。'; + String get p2pHowToCreatePasskeyDescription1 => '在 Deriv P2P 應用程式中轉到‘個人資料‘。'; @override - String get p2pHowToCreatePasskeyDescription2 => '點擊「密碼」以建立您的密碼。'; + String get p2pHowToCreatePasskeyDescription2 => '點選‘密鑰‘以建立密鑰。'; @override String get whereArePasskeysSaved => '金鑰儲存在哪裡?'; diff --git a/packages/deriv_localizations/pubspec.yaml b/packages/deriv_localizations/pubspec.yaml index aa2b18e4f..f9fc08a0e 100644 --- a/packages/deriv_localizations/pubspec.yaml +++ b/packages/deriv_localizations/pubspec.yaml @@ -3,7 +3,7 @@ description: This packages contains all localizations for the deriv packages publish_to: 'none' -version: 1.4.4 +version: 1.5.1 environment: sdk: '>=3.0.2 <4.0.0' diff --git a/packages/deriv_logger/CHANGELOG.md b/packages/deriv_logger/CHANGELOG.md index 41cc7d819..ac0b885f0 100644 --- a/packages/deriv_logger/CHANGELOG.md +++ b/packages/deriv_logger/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.0.2 + + - **FEAT**(deriv_logger): add ability to print prettified logs in console and UI ([#608](https://github.com/regentmarkets/flutter-deriv-packages/issues/608)). ([5a91c24b](https://github.com/regentmarkets/flutter-deriv-packages/commit/5a91c24bde607ff37940edf18f8dfac67d3fc4fa)) + ## 0.0.1 * TODO: Describe initial release. diff --git a/packages/deriv_logger/pubspec.yaml b/packages/deriv_logger/pubspec.yaml index a339ff738..8b85a3d84 100644 --- a/packages/deriv_logger/pubspec.yaml +++ b/packages/deriv_logger/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_logger description: A debugging tool for deriv applications. -version: 0.0.1 +version: 0.0.2 environment: sdk: ">=3.0.2 <4.0.0" diff --git a/packages/deriv_mobile_chart_wrapper/.gitignore b/packages/deriv_mobile_chart_wrapper/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/.gitignore @@ -0,0 +1,30 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.packages +build/ diff --git a/packages/deriv_mobile_chart_wrapper/.metadata b/packages/deriv_mobile_chart_wrapper/.metadata new file mode 100644 index 000000000..fa347fc6a --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + channel: stable + +project_type: package diff --git a/packages/deriv_mobile_chart_wrapper/CHANGELOG.md b/packages/deriv_mobile_chart_wrapper/CHANGELOG.md new file mode 100644 index 000000000..82aecd4d3 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/CHANGELOG.md @@ -0,0 +1,7 @@ +## 0.0.2 + + - **FEAT**(deriv_mobile_chart_wrapper): [DERG-2498] create deriv_mobile_chart_wrapper package ([#626](https://github.com/regentmarkets/flutter-deriv-packages/issues/626)). ([01aec1ed](https://github.com/regentmarkets/flutter-deriv-packages/commit/01aec1edbd8f02e21951918ec86f00fb28ef2c58)) + +## 0.0.1 + +* Setup the package. diff --git a/packages/deriv_mobile_chart_wrapper/LICENSE b/packages/deriv_mobile_chart_wrapper/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/deriv_mobile_chart_wrapper/README.md b/packages/deriv_mobile_chart_wrapper/README.md new file mode 100644 index 000000000..da82d0a64 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/README.md @@ -0,0 +1,21 @@ +A wrapper package around package _**deriv_chart**_ to implement any functionality specific to mobile and can be wrapped around the main chart package. +Since the main [chart package](https://github.com/regentmarkets/flutter-chart) is used in both mobile app and the web platform, for any feature that is specific to mobile, to keep the size of the main chart package small, we implement it in this package. + +## Features +Menu and interfaces to add/remove indicators and drawing tools. + +## Dependencies +- [deriv_chart](https://github.com/regentmarkets/flutter-chart) +- [deriv_theme](https://github.com/regentmarkets/flutter-deriv-packages/tree/master/packages/deriv_theme) +- [deriv_localizations](https://github.com/regentmarkets/flutter-deriv-packages/tree/master/packages/deriv_localizations) +- [deriv_ui](https://github.com/regentmarkets/flutter-deriv-packages/tree/master/packages/deriv_ui) + +## Get started +``` +dependencies: + deriv_mobile_chart_wrapper: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_mobile_chart_wrapper + ref: [latest_version] +``` diff --git a/packages/deriv_mobile_chart_wrapper/analysis_options.yaml b/packages/deriv_mobile_chart_wrapper/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/deriv_mobile_chart_wrapper/assets/icons/ic_bollinger_bands.svg b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_bollinger_bands.svg new file mode 100644 index 000000000..2abf85f5d --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_bollinger_bands.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/deriv_mobile_chart_wrapper/assets/icons/ic_indicators_menu.svg b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_indicators_menu.svg new file mode 100644 index 000000000..58ad8a51c --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_indicators_menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/deriv_mobile_chart_wrapper/assets/icons/ic_macd.svg b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_macd.svg new file mode 100644 index 000000000..b4da2a46a --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_macd.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/deriv_mobile_chart_wrapper/assets/icons/ic_moving_average.svg b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_moving_average.svg new file mode 100644 index 000000000..f15853309 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_moving_average.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/deriv_mobile_chart_wrapper/assets/icons/ic_rsi.svg b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_rsi.svg new file mode 100644 index 000000000..b602ca188 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/assets/icons/ic_rsi.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/deriv_mobile_chart_wrapper/lib/deriv_mobile_chart_wrapper.dart b/packages/deriv_mobile_chart_wrapper/lib/deriv_mobile_chart_wrapper.dart new file mode 100644 index 000000000..d9d336d92 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/deriv_mobile_chart_wrapper.dart @@ -0,0 +1,6 @@ +library deriv_mobile_chart_wrapper; + +export 'src/mobile_chart_wrapper.dart'; +export 'src/mobile_tools_ui/tools_controller.dart'; +export 'src/mobile_tools_ui/indicator_menu_button.dart'; +export 'package:deriv_chart/deriv_chart.dart'; diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/assets.dart b/packages/deriv_mobile_chart_wrapper/lib/src/assets.dart new file mode 100644 index 000000000..95253e2e1 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/assets.dart @@ -0,0 +1,7 @@ +const String iconAssetsFolder = 'assets/icons/'; + +const String macdIcon = '${iconAssetsFolder}ic_macd.svg'; +const String rsiIcon = '${iconAssetsFolder}ic_rsi.svg'; +const String bollingerBandsIcon = '${iconAssetsFolder}ic_bollinger_bands.svg'; +const String movingAverageIcon = '${iconAssetsFolder}ic_moving_average.svg'; +const String indicatorsMenuIcon = '${iconAssetsFolder}ic_indicators_menu.svg'; diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/extensions.dart b/packages/deriv_mobile_chart_wrapper/lib/src/extensions.dart new file mode 100644 index 000000000..35b539e12 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/extensions.dart @@ -0,0 +1,12 @@ +import 'package:deriv_localizations/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart'; +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:flutter/material.dart'; + +/// Extension for [BuildContext]. +extension ContextExtension on BuildContext { + /// Get DerivMobileChartWrapperLocalizations. + DerivMobileChartWrapperLocalizations get mobileChartWrapperLocalizations => + DerivMobileChartWrapperLocalizations.of(this); + + ThemeProvider get themeProvider => DerivThemeProvider.getTheme(this); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_chart_wrapper.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_chart_wrapper.dart new file mode 100644 index 000000000..bdf01b22b --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_chart_wrapper.dart @@ -0,0 +1,280 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:deriv_ui/components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import 'mobile_tools_ui/chart_bottom_sheet.dart'; +import 'mobile_tools_ui/mobile_tools_bottom_sheet_content.dart'; +import 'mobile_tools_ui/tools_controller.dart'; + +/// The mobile version wrapper around the [Chart] which handles adding/removing +/// indicators and drawing tools to the chart. +class MobileChartWrapper extends StatefulWidget { + /// Initializes [MobileChartWrapper]. + const MobileChartWrapper({ + required this.mainSeries, + required this.granularity, + this.toolsStoreKey = 'default', + this.toolsController, + this.markerSeries, + this.controller, + this.onCrosshairAppeared, + this.onCrosshairDisappeared, + this.onCrosshairHover, + this.onVisibleAreaChanged, + this.onQuoteAreaChanged, + this.theme, + this.isLive = false, + this.dataFitEnabled = false, + this.showCrosshair = true, + this.annotations, + this.opacity = 1.0, + this.pipSize = 4, + this.chartAxisConfig = const ChartAxisConfig(), + this.maxCurrentTickOffset, + this.msPerPx, + this.minIntervalWidth, + this.maxIntervalWidth, + this.dataFitPadding, + this.currentTickAnimationDuration, + this.quoteBoundsAnimationDuration, + this.showCurrentTickBlinkAnimation, + this.verticalPaddingFraction, + this.bottomChartTitleMargin, + this.showDataFitButton, + this.showScrollToLastTickButton, + this.loadingAnimationColor, + Key? key, + }) : super(key: key); + + /// Chart's main data series + final DataSeries mainSeries; + + /// Open position marker series. + final MarkerSeries? markerSeries; + + /// The key which is used to store selected indicators/tools. + /// + /// When you pass the same key that was passed before when user selected some + /// tools, by passing the same key, the tools will be restored. + final String toolsStoreKey; + + /// Chart's controller + final ChartController? controller; + + /// Chart's tools controller. + final ToolsController? toolsController; + + /// Number of digits after decimal point in price. + final int pipSize; + + /// For candles: Duration of one candle in ms. + /// For ticks: Average ms difference between two consecutive ticks. + final int granularity; + + /// Called when crosshair details appear after long press. + final VoidCallback? onCrosshairAppeared; + + /// Called when the crosshair is dismissed. + final VoidCallback? onCrosshairDisappeared; + + /// Called when the crosshair cursor is hovered/moved. + final OnCrosshairHoverCallback? onCrosshairHover; + + /// Called when chart is scrolled or zoomed. + final VisibleAreaChangedCallback? onVisibleAreaChanged; + + /// Callback provided by library user. + final VisibleQuoteAreaChangedCallback? onQuoteAreaChanged; + + /// Chart's theme. + final ChartTheme? theme; + + /// Chart's annotations + final List>? annotations; + + /// Configurations for chart's axes. + final ChartAxisConfig chartAxisConfig; + + /// Whether the chart should be showing live data or not. + /// In case of being true the chart will keep auto-scrolling when its visible + /// area is on the newest ticks/candles. + final bool isLive; + + /// Starts in data fit mode and adds a data-fit button. + final bool dataFitEnabled; + + /// Chart's opacity, Will be applied on the [mainSeries]. + final double opacity; + + /// Whether the crosshair should be shown or not. + final bool showCrosshair; + + /// Max distance between rightBoundEpoch and nowEpoch in pixels. + final double? maxCurrentTickOffset; + + /// Specifies the zoom level of the chart. + final double? msPerPx; + + /// Specifies the minimum interval width + /// that is used for calculating the maximum msPerPx. + final double? minIntervalWidth; + + /// Specifies the maximum interval width + /// that is used for calculating the maximum msPerPx. + final double? maxIntervalWidth; + + /// Padding around data used in data-fit mode. + final EdgeInsets? dataFitPadding; + + /// Duration of the current tick animated transition. + final Duration? currentTickAnimationDuration; + + /// Duration of quote bounds animated transition. + final Duration? quoteBoundsAnimationDuration; + + /// Whether to show current tick blink animation or not. + final bool? showCurrentTickBlinkAnimation; + + /// Fraction of the chart's height taken by top or bottom padding. + /// Quote scaling (drag on quote area) is controlled by this variable. + final double? verticalPaddingFraction; + + /// Specifies the margin to prevent overlap. + final EdgeInsets? bottomChartTitleMargin; + + /// Whether the data fit button is shown or not. + final bool? showDataFitButton; + + /// Whether to show the scroll to last tick button or not. + final bool? showScrollToLastTickButton; + + /// The color of the loading animation. + final Color? loadingAnimationColor; + + @override + MobileChartWrapperState createState() => MobileChartWrapperState(); +} + +/// The state of the [MobileChartWrapper]. +class MobileChartWrapperState extends State { + AddOnsRepository? _indicatorsRepo; + + // TODO(Ramin): Add AddOnsRepository? and DrawingTools + // for drawing tools. + + @override + void initState() { + super.initState(); + + _initRepos(); + _setupController(); + } + + @override + void didUpdateWidget(covariant MobileChartWrapper oldWidget) { + super.didUpdateWidget(oldWidget); + + if (widget.toolsStoreKey != oldWidget.toolsStoreKey) { + loadSavedIndicatorsAndDrawingTools(); + } + } + + void _setupController() { + widget.toolsController?.onShowIndicatorsToolsMenu = () { + if (_indicatorsRepo != null) { + _showIndicatorsSheet(_indicatorsRepo!); + } + }; + } + + void _initRepos() { + if (widget.toolsController?.indicatorsEnabled ?? false) { + _indicatorsRepo = AddOnsRepository( + createAddOn: (Map map) => + IndicatorConfig.fromJson(map), + onEditCallback: (_) => _showIndicatorsSheet(_indicatorsRepo!), + sharedPrefKey: widget.toolsStoreKey, + ); + } + + loadSavedIndicatorsAndDrawingTools(); + } + + Future loadSavedIndicatorsAndDrawingTools() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final List> stateRepos = + >[ + if (_indicatorsRepo != null) _indicatorsRepo!, + // TODO(Ramin): add drawing tools repo here. + ]; + + stateRepos + .asMap() + .forEach((int index, AddOnsRepository element) { + try { + element.loadFromPrefs(prefs, widget.toolsStoreKey); + } on Exception { + // ignore: unawaited_futures + showDialog( + context: context, + builder: (BuildContext context) => AnimatedPopupDialog( + child: Center( + child: element is Repository + // TODO(Ramin): use localization. + ? const Text('Failed loading indicators') + : const Text('Failed loading drawing tools'), + ), + ), + ); + } + }); + } + + void _showIndicatorsSheet(AddOnsRepository indicatorsRepo) { + // Show indicators menu as modal bottom sheet so it's dismissible by tapping + // outside. + showModalBottomSheet( + context: context, + builder: (_) => ChangeNotifierProvider>.value( + value: indicatorsRepo, + child: ChartBottomSheet( + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.5, + child: const MobileToolsBottomSheetContent(), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) => + // TODO(Ramin): Check if we can consider using Chart widget directly. + DerivChart( + indicatorsRepo: _indicatorsRepo ?? + AddOnsRepository( + createAddOn: (Map map) => + IndicatorConfig.fromJson(map), + sharedPrefKey: widget.toolsStoreKey, + ), + drawingToolsRepo: AddOnsRepository( + createAddOn: (Map map) => + DrawingToolConfig.fromJson(map), + sharedPrefKey: widget.toolsStoreKey, + ), + controller: widget.controller, + mainSeries: widget.mainSeries, + markerSeries: widget.markerSeries, + pipSize: widget.pipSize, + granularity: widget.granularity, + onVisibleAreaChanged: widget.onVisibleAreaChanged, + isLive: widget.isLive, + dataFitEnabled: widget.dataFitEnabled, + opacity: widget.opacity, + chartAxisConfig: widget.chartAxisConfig, + annotations: widget.annotations, + activeSymbol: widget.toolsStoreKey, + ); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/chart_bottom_sheet.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/chart_bottom_sheet.dart new file mode 100644 index 000000000..9cdb48942 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/chart_bottom_sheet.dart @@ -0,0 +1,76 @@ +import 'package:deriv_chart/deriv_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'custom_draggable_sheet.dart'; + +/// Bottom sheet container used in chart library. +class ChartBottomSheet extends StatefulWidget { + /// Creates a bottom sheet container for [child]. + const ChartBottomSheet({ + required this.child, + this.theme, + Key? key, + }) : super(key: key); + + /// Body of bottom sheet container. + final Widget child; + + /// The theme of the chart which the bottom sheet is being placed inside. + final ChartTheme? theme; + + @override + _ChartBottomSheetState createState() => _ChartBottomSheetState(); +} + +class _ChartBottomSheetState extends State { + late ChartTheme _theme; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + _theme = widget.theme ?? + (Theme.of(context).brightness == Brightness.dark + ? ChartDefaultDarkTheme() + : ChartDefaultLightTheme()); + } + + @override + Widget build(BuildContext context) => CustomDraggableSheet( + child: Provider.value( + value: _theme, + child: ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(_theme.borderRadius24Chart), + topRight: Radius.circular(_theme.borderRadius24Chart), + ), + child: Material( + elevation: 8, + color: _theme.base07Color, + child: Column( + children: [ + _buildTopHandle(), + Expanded(child: widget.child), + ], + ), + ), + ), + ), + ); + + Widget _buildTopHandle() => Container( + padding: EdgeInsets.symmetric(vertical: _theme.margin08Chart), + width: double.infinity, + child: Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: _theme.base05Color, + borderRadius: BorderRadius.circular(_theme.borderRadius04Chart), + ), + ), + ), + ); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/chart_setting_button_with_background.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/chart_setting_button_with_background.dart new file mode 100644 index 000000000..ae7afce7f --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/chart_setting_button_with_background.dart @@ -0,0 +1,40 @@ +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:flutter/material.dart'; + +/// A button with a background. +class ChartSettingButtonWithBackground extends StatelessWidget { + /// Creates a button with a background. + const ChartSettingButtonWithBackground({ + required this.child, + required this.onTap, + super.key, + }); + + /// The button content. + final Widget child; + + /// The callback function to be called when the button is tapped. + final VoidCallback onTap; + + @override + Widget build(BuildContext context) => Container( + width: ThemeProvider.margin32, + height: ThemeProvider.margin32, + decoration: BoxDecoration( + color: context.theme.colors.secondary, + borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), + ), + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), + child: InkWell( + borderRadius: BorderRadius.circular(ThemeProvider.borderRadius04), + child: Padding( + padding: const EdgeInsets.all(ThemeProvider.margin08), + child: child, + ), + onTap: onTap, + ), + ), + ); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/custom_draggable_sheet.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/custom_draggable_sheet.dart new file mode 100644 index 000000000..3128b3486 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/custom_draggable_sheet.dart @@ -0,0 +1,145 @@ +import 'package:flutter/material.dart'; + +/// A widget to manage the over-scroll to dismiss for a scrollable inside its +/// [child] that being shown by calling [showBottomSheet()]. +/// +/// This widget will listen to [OverscrollNotification] inside its [child] to +/// detect that it has reached its top scroll limit. when user is closing the +/// [child] by over-scrolling, it will call [Navigator.pop()], to fully dismiss +/// the [BottomSheet]. +class CustomDraggableSheet extends StatefulWidget { + /// Initializes a widget to manage the over-scroll to dismiss for a scrollable + /// inside its [child]. + const CustomDraggableSheet({ + required this.child, + Key? key, + this.animationDuration = const Duration(milliseconds: 100), + this.introAnimationDuration = const Duration(milliseconds: 300), + }) : super(key: key); + + /// The sheet that was popped-up inside a [BottomSheet] throw calling + /// [showBottomSheet]. + final Widget child; + + /// The duration of animation whether sheet will fling back to top or dismiss. + final Duration animationDuration; + + /// The duration of the starting animation. + final Duration introAnimationDuration; + + @override + _CustomDraggableSheetState createState() => _CustomDraggableSheetState(); +} + +class _CustomDraggableSheetState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + + final GlobalKey> _sheetKey = GlobalKey(); + + Size? _sheetSize; + + bool _overScrolled = false; + + @override + void initState() { + super.initState(); + + _animationController = AnimationController.unbounded(vsync: this, value: 1) + ..addStatusListener((AnimationStatus status) { + if (status == AnimationStatus.completed && + _animationController.value > 0.9) { + Navigator.of(context).pop(); + } + }); + + WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((_) { + _sheetSize = _initSizes(); + _animationController.animateTo( + 0, + duration: widget.introAnimationDuration, + curve: Curves.easeOut, + ); + }); + } + + Size _initSizes() { + final RenderBox chartBox = + _sheetKey.currentContext!.findRenderObject() as RenderBox; + return chartBox.size; + } + + @override + Widget build(BuildContext context) => AnimatedBuilder( + key: _sheetKey, + animation: _animationController, + builder: (BuildContext context, Widget? child) => FractionalTranslation( + translation: Offset(0, _animationController.value), + child: child, + ), + child: NotificationListener( + onNotification: _handleScrollNotification, + child: widget.child, + ), + ); + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + bool _handleScrollNotification(Notification notification) { + if (_sheetSize != null && notification is OverscrollNotification) { + _overScrolled = true; + _panToBottom(notification); + } else if (notification is ScrollUpdateNotification && _overScrolled) { + _panToTop(notification); + } else if (!_animationController.isAnimating && + notification is ScrollEndNotification && + _overScrolled) { + _overScrolled = false; + _flingToTopOrBottom(); + } + + return false; + } + + void _panToTop(ScrollUpdateNotification notification) { + final double deltaPercent = notification.scrollDelta! / _sheetSize!.height; + + if (deltaPercent > 0) { + _updateSheetHeightBy(deltaPercent); + } + } + + void _panToBottom(OverscrollNotification notification) { + final double deltaPercent = notification.overscroll / _sheetSize!.height; + + if (deltaPercent < 0) { + _updateSheetHeightBy(deltaPercent); + } + } + + void _updateSheetHeightBy(double deltaPercent) { + _animationController.value -= deltaPercent; + final double value = _animationController.value.clamp(0.0, 1.0); + _animationController.value = value; + } + + void _flingToTopOrBottom() { + if (_animationController.value > 0.5) { + _animationController.animateTo( + 1, + duration: widget.animationDuration, + curve: Curves.easeOut, + ); + } else { + _animationController.animateTo( + 0, + duration: widget.animationDuration, + curve: Curves.easeOut, + ); + } + } +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/indicator_list_item.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/indicator_list_item.dart new file mode 100644 index 000000000..8bb00ec7f --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/indicator_list_item.dart @@ -0,0 +1,65 @@ +import 'package:deriv_mobile_chart_wrapper/src/extensions.dart'; +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +/// List item widget to show an indicator. +class IndicatorListItem extends StatelessWidget { + /// Constructor of the widget + const IndicatorListItem({ + required this.iconAssetPath, + required this.title, + required this.onInfoIconTapped, + this.count = 0, + super.key, + }); + + /// The path to the SVG icon asset. + final String iconAssetPath; + + /// The title of the indicator. + final String title; + + /// The callback which will be called when the info icon is tapped. + final VoidCallback onInfoIconTapped; + + /// Number of added indicators of this type. + /// + /// It will show in the item if it's greater than 0. + final int count; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(ThemeProvider.margin16), + child: Row( + children: [ + _buildIndicatorIcon(), + const SizedBox(width: Dimens.margin08), + _buildIndicatorTitle(context), + const Spacer(), + IconButton( + icon: const Icon(Icons.info_outline), + color: context.themeProvider.colors.prominent, + onPressed: onInfoIconTapped, + ), + ], + ), + ); + } + + Widget _buildIndicatorIcon() => SvgPicture.asset( + iconAssetPath, + width: ThemeProvider.margin24, + height: ThemeProvider.margin24, + package: 'deriv_mobile_chart_wrapper', + ); + + Widget _buildIndicatorTitle(BuildContext context) => Text( + title, + style: context.themeProvider.textStyle( + textStyle: TextStyles.body1, + color: context.themeProvider.colors.general, + ), + ); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/indicator_menu_button.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/indicator_menu_button.dart new file mode 100644 index 000000000..42c293168 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/indicator_menu_button.dart @@ -0,0 +1,29 @@ +import 'package:deriv_mobile_chart_wrapper/src/assets.dart'; +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +import 'chart_setting_button_with_background.dart'; + +/// A button that opens the indicator menu. +class IndicatorMenuButton extends StatelessWidget { + /// Initializes the indicator menu button. + const IndicatorMenuButton({ + required this.onTap, + super.key, + }); + + /// The callback function to be called when the button is tapped. + final VoidCallback onTap; + + @override + Widget build(BuildContext context) => ChartSettingButtonWithBackground( + onTap: onTap, + child: SvgPicture.asset( + indicatorsMenuIcon, + width: ThemeProvider.margin18, + height: ThemeProvider.margin18, + package: 'deriv_mobile_chart_wrapper', + ), + ); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/mobile_tools_bottom_sheet_content.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/mobile_tools_bottom_sheet_content.dart new file mode 100644 index 000000000..cbbb7caa0 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/mobile_tools_bottom_sheet_content.dart @@ -0,0 +1,79 @@ +import 'package:deriv_mobile_chart_wrapper/src/assets.dart'; +import 'package:deriv_mobile_chart_wrapper/src/mobile_tools_ui/indicator_list_item.dart'; +import 'package:deriv_mobile_chart_wrapper/src/models/indicator_item_model.dart'; +import 'package:deriv_theme/deriv_theme.dart'; +import 'package:deriv_mobile_chart_wrapper/src/extensions.dart'; +import 'package:flutter/material.dart'; + +/// Bottom sheet content to show the list of support tools (indicators/ drawing +/// tools) for the mobile version. +class MobileToolsBottomSheetContent extends StatelessWidget { + /// Initializes the bottom sheet content. + const MobileToolsBottomSheetContent({super.key}); + + static const List indicators = [ + IndicatorItemModel(title: 'MACD', icon: macdIcon), + IndicatorItemModel(title: 'Relative Strength Index (RSI)', icon: rsiIcon), + IndicatorItemModel(title: 'Bollinger Bands', icon: bollingerBandsIcon), + IndicatorItemModel(title: 'Moving Average', icon: movingAverageIcon), + ]; + + @override + Widget build(BuildContext context) => Column( + children: [ + _buildHeader(context), + Expanded( + child: Ink( + color: context.theme.colors.primary, + child: Column( + children: [ + const SizedBox(height: ThemeProvider.margin16), + _buildChipsList(), + Expanded(child: _buildIndicatorsList()), + ], + ), + ), + ), + ], + ); + + Widget _buildIndicatorsList() { + return ListView.builder( + itemCount: indicators.length, + itemBuilder: (_, index) { + final IndicatorItemModel indicator = indicators[index]; + + return IndicatorListItem( + iconAssetPath: indicator.icon, + title: indicator.title, + onInfoIconTapped: () {}, + ); + }, + ); + } + + Widget _buildChipsList() { + // Overscroll behaviour of horizontal chips list sometimes triggers + // BottomSheet top <-> bottom dragging. That's why we're capturing the + // overscroll here so it doesn't propagate up to the BottomSheet. + return NotificationListener( + onNotification: (OverscrollNotification notification) { + return true; + }, + // TODO(Ramin): add chips list. + child: const SizedBox.shrink(), + ); + } + + Widget _buildHeader(BuildContext context) => Container( + padding: const EdgeInsets.symmetric(vertical: Dimens.margin16), + alignment: Alignment.center, + child: Text( + context.mobileChartWrapperLocalizations.labelIndicators, + style: DerivThemeProvider.getTheme(context).textStyle( + textStyle: TextStyles.subheading, + color: DerivThemeProvider.getTheme(context).colors.prominent, + ), + ), + ); +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/tools_controller.dart b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/tools_controller.dart new file mode 100644 index 000000000..a207eca9c --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/mobile_tools_ui/tools_controller.dart @@ -0,0 +1,21 @@ +import 'dart:ui'; + +/// Controller class to show tools menu. +class ToolsController { + /// Initializes the tools controller. + ToolsController({this.indicatorsEnabled = true}); + + /// Whether indicators are enabled or not. + final bool indicatorsEnabled; + + /// Called to show indicators tools menu. + VoidCallback? onShowIndicatorsToolsMenu; + + /// Shows indicators tools menu. + void showIndicatorsToolsMenu() => onShowIndicatorsToolsMenu?.call(); + + /// Shows drawing tools menu. + void showDrawingToolsMenu() { + // TODO(Ramin): Call the callback for drawing tools. + } +} diff --git a/packages/deriv_mobile_chart_wrapper/lib/src/models/indicator_item_model.dart b/packages/deriv_mobile_chart_wrapper/lib/src/models/indicator_item_model.dart new file mode 100644 index 000000000..fc155c903 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/lib/src/models/indicator_item_model.dart @@ -0,0 +1,14 @@ +/// Model class to keep the information of an indicator item. +class IndicatorItemModel { + /// Initializes an indicator item model. + const IndicatorItemModel({ + required this.title, + required this.icon, + }); + + /// The title. + final String title; + + /// The path to the SVG icon. + final String icon; +} diff --git a/packages/deriv_mobile_chart_wrapper/pubspec.yaml b/packages/deriv_mobile_chart_wrapper/pubspec.yaml new file mode 100644 index 000000000..df9f78a83 --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/pubspec.yaml @@ -0,0 +1,84 @@ +name: deriv_mobile_chart_wrapper +description: A new Flutter package project. +version: 0.0.2 +homepage: + +environment: + sdk: ">=3.0.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + + deriv_chart: + git: + url: git@github.com:regentmarkets/flutter-chart.git + ref: dev + + deriv_theme: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_theme + ref: deriv_theme-v2.5.0 + + deriv_localizations: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_localizations + ref: deriv_localizations-v1.5.1 + + deriv_ui: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/deriv_ui + ref: deriv_ui-v0.0.7+6 + + provider: ^6.0.5 + flutter_svg: ^2.0.9 + shared_preferences: ^2.1.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + mockito: ^5.4.2 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + uses-material-design: true + + assets: + - assets/icons/ic_macd.svg + - assets/icons/ic_rsi.svg + - assets/icons/ic_bollinger_bands.svg + - assets/icons/ic_moving_average.svg + - assets/icons/ic_indicators_menu.svg +# +# For details regarding assets in packages, see +# https://flutter.dev/assets-and-images/#from-packages +# +# An image asset can refer to one or more resolution-specific "variants", see +# https://flutter.dev/assets-and-images/#resolution-aware + +# To add custom fonts to your package, add a fonts section here, +# in this "flutter" section. Each entry in this list should have a +# "family" key with the font family name, and a "fonts" key with a +# list giving the asset and other descriptors for the font. For +# example: +# fonts: +# - family: Schyler +# fonts: +# - asset: fonts/Schyler-Regular.ttf +# - asset: fonts/Schyler-Italic.ttf +# style: italic +# - family: Trajan Pro +# fonts: +# - asset: fonts/TrajanPro.ttf +# - asset: fonts/TrajanPro_Bold.ttf +# weight: 700 +# +# For details regarding fonts in packages, see +# https://flutter.dev/custom-fonts/#from-packages diff --git a/packages/deriv_mobile_chart_wrapper/test/mobile_chart_wrapper_test.dart b/packages/deriv_mobile_chart_wrapper/test/mobile_chart_wrapper_test.dart new file mode 100644 index 000000000..3fc7efb0b --- /dev/null +++ b/packages/deriv_mobile_chart_wrapper/test/mobile_chart_wrapper_test.dart @@ -0,0 +1,77 @@ +import 'package:deriv_localizations/l10n/generated/deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper_localizations.dart'; +import 'package:deriv_mobile_chart_wrapper/src/mobile_tools_ui/mobile_tools_bottom_sheet_content.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter/material.dart'; +import 'package:deriv_mobile_chart_wrapper/deriv_mobile_chart_wrapper.dart'; + +class MockToolsController extends Mock implements ToolsController { + @override + bool get indicatorsEnabled => true; +} + +class MockAddOnsRepository extends Mock + implements AddOnsRepository {} + +void main() { + group('MobileChartWrapper Tests', () { + testWidgets('MobileChartWrapper initializes correctly', + (WidgetTester tester) async { + await tester.pumpWidget(_TestWidget(toolsController: ToolsController())); + + // Verify initial state + expect(find.byType(DerivChart), findsOneWidget); + }); + + testWidgets('ToolsController showIndicatorsToolsMenu callback is set', + (WidgetTester tester) async { + final mockToolsController = MockToolsController(); + await tester.pumpWidget( + _TestWidget(toolsController: mockToolsController), + ); + + // Verify callback is set + verify(mockToolsController.onShowIndicatorsToolsMenu = any).called(1); + }); + + testWidgets('MobileChartWrapper shows indicators sheet', + (WidgetTester tester) async { + final toolsController = ToolsController(); + + await tester.pumpWidget( + _TestWidget( + toolsController: toolsController, + ), + ); + + // Trigger the callback to show the sheet + toolsController.showIndicatorsToolsMenu(); + await tester.pump(); + + // Verify the bottom sheet is displayed + expect(find.byType(MobileToolsBottomSheetContent), findsOneWidget); + }); + }); +} + +class _TestWidget extends StatelessWidget { + const _TestWidget({required this.toolsController}); + + final ToolsController toolsController; + + @override + Widget build(BuildContext context) { + return MaterialApp( + localizationsDelegates: const >[ + DerivMobileChartWrapperLocalizations.delegate, + ], + home: Material( + child: MobileChartWrapper( + mainSeries: LineSeries([]), + granularity: 60, + toolsController: toolsController, + ), + ), + ); + } +} diff --git a/packages/deriv_passkeys/CHANGELOG.md b/packages/deriv_passkeys/CHANGELOG.md index 467720e5b..ce5e2df3e 100644 --- a/packages/deriv_passkeys/CHANGELOG.md +++ b/packages/deriv_passkeys/CHANGELOG.md @@ -1,3 +1,40 @@ +## 0.0.2+8 + + - Update a dependency to the latest release. + +## 0.0.2+7 + + - **FIX**(deriv_passkeys): Fix_logout_issue_by_fetching_refresh_token ([#630](https://github.com/regentmarkets/flutter-deriv-packages/issues/630)). ([282278ae](https://github.com/regentmarkets/flutter-deriv-packages/commit/282278aeb27256eaa37660f58eff704d27e92c93)) + +## 0.0.2+6 + + - Update a dependency to the latest release. + +## 0.0.2+5 + + - Update a dependency to the latest release. + +## 0.0.2+4 + + - **FIX**(deriv_passkeys): fix passkeys success button styles ([#629](https://github.com/regentmarkets/flutter-deriv-packages/issues/629)). ([d1fb8590](https://github.com/regentmarkets/flutter-deriv-packages/commit/d1fb8590eb5eb55f9bd9db3a255f33cf152f364d)) + +## 0.0.2+3 + + - Update a dependency to the latest release. + +## 0.0.2+2 + + - Update a dependency to the latest release. + +## 0.0.2+1 + + - Update a dependency to the latest release. + +## 0.0.2 + + - **REFACTOR**(deriv_passkeys): Removed deprecated linter rules ([#625](https://github.com/regentmarkets/flutter-deriv-packages/issues/625)). ([2fdc28f2](https://github.com/regentmarkets/flutter-deriv-packages/commit/2fdc28f20b8efe2ddc3a9a261c40b533307f25e3)) + - **FEAT**(deriv_passkeys): add user tracking events. ([#607](https://github.com/regentmarkets/flutter-deriv-packages/issues/607)). ([d86b51e2](https://github.com/regentmarkets/flutter-deriv-packages/commit/d86b51e2fe4ca4d18768d0ba17567a388a8d360d)) + ## 0.0.1+8 - Update a dependency to the latest release. diff --git a/packages/deriv_passkeys/analysis_options.yaml b/packages/deriv_passkeys/analysis_options.yaml index fb40ccd5d..aea7c9654 100644 --- a/packages/deriv_passkeys/analysis_options.yaml +++ b/packages/deriv_passkeys/analysis_options.yaml @@ -7,7 +7,6 @@ linter: - always_declare_return_types - always_put_control_body_on_new_line - always_put_required_named_parameters_first - - always_require_non_null_named_parameters - always_specify_types - annotate_overrides - avoid_bool_literals_in_conditional_expressions @@ -24,8 +23,6 @@ linter: - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters - - avoid_returning_null - - avoid_returning_null_for_future - avoid_returning_null_for_void - avoid_setters_without_getters - avoid_shadowing_type_parameters diff --git a/packages/deriv_passkeys/example/lib/main.dart b/packages/deriv_passkeys/example/lib/main.dart index 4baf1c3f8..8047aeed3 100644 --- a/packages/deriv_passkeys/example/lib/main.dart +++ b/packages/deriv_passkeys/example/lib/main.dart @@ -89,7 +89,9 @@ class MyPage extends StatelessWidget { body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const ContinueWithPasskeyButton(), + ContinueWithPasskeyButton( + onTap: () {}, + ), SizedBox( width: double.infinity, child: Padding( diff --git a/packages/deriv_passkeys/lib/src/core/constants/analytics_actions_enums.dart b/packages/deriv_passkeys/lib/src/core/constants/analytics_actions_enums.dart new file mode 100644 index 000000000..4fa6a010b --- /dev/null +++ b/packages/deriv_passkeys/lib/src/core/constants/analytics_actions_enums.dart @@ -0,0 +1,59 @@ +/// The list of actions that can be performed on the Effortless page. +enum EffortlessPageActions { + /// The user opened the effortless login page. + openEffortlessLoginPage, + + /// The user closed the effortless login page. + closeEffortlessLoginPage, + + /// The user pressed the maybe later button. + maybeLater, +} + +/// The list of actions that can be performed on the Learn more page. +enum LearnMorePageActions { + /// The user opened the learn more page. + openLearnMorePage, + + /// The user closed the learn more page. + closeLearnMorePage, +} + +/// The list of actions that can be performed on the Create passkey page. +enum CreatePasskeyFlowActions { + /// The user pressed the create passkey button. + createPasskey, + + /// Create passkey success. + createPasskeySuccess, + + /// The user faces an error. + error, + + /// The user presses the continue trading button. + continueTrading, + + /// The user presses the add more passkeys button. + addMorePasskeys, +} + +/// The list of actions that can be performed on the Rename passkey page. +enum RenamePasskeyFlowActions { + /// The user presses the rename passkey button. + renamePasskey, + + /// The user cancels rename passkey. + cancelRenamePasskey, + + /// Rename passkey success. + renamePasskeySuccess, +} + +/// The list of actions that can be performed on the Manage Passkeys page. +enum ManagePasskeysPageActions { + /// The user opened the manage passkeys page. + openManagePasskeysPage, + + /// The user closed the manage passkeys page. + closeManagePasskeysPage, +} diff --git a/packages/deriv_passkeys/lib/src/core/constants/tracking_event_params_helper.dart b/packages/deriv_passkeys/lib/src/core/constants/tracking_event_params_helper.dart new file mode 100644 index 000000000..5693a7a83 --- /dev/null +++ b/packages/deriv_passkeys/lib/src/core/constants/tracking_event_params_helper.dart @@ -0,0 +1,158 @@ +import 'package:deriv_passkeys/src/core/constants/analytics_actions_enums.dart'; +import 'package:flutter/foundation.dart'; + +/// Platform type string. +String _platform = defaultTargetPlatform == TargetPlatform.android + ? 'Android' + : defaultTargetPlatform == TargetPlatform.iOS + ? 'IOS' + : 'other'; + +/// Function to get all the Effortless page params. +Map getEffortlessLoginTrackingParams(T eventName) => + switch (eventName) { + EffortlessPageActions.openEffortlessLoginPage => { + 'event_name': 'open', + 'params': { + 'form_name': 'ce_passkey_effortless_form_', + 'operating_system': _platform, + }, + }, + EffortlessPageActions.closeEffortlessLoginPage => { + 'event_name': 'close', + 'params': { + 'form_name': 'ce_passkey_effortless_form_', + 'operating_system': _platform, + }, + }, + EffortlessPageActions.maybeLater => { + 'event_name': 'maybe_later', + 'params': { + 'form_name': 'ce_passkey_effortless_form_', + 'operating_system': _platform, + }, + }, + _ => throw Exception('Invalid event name'), + }; + +/// Function to get all the Learn More page params. +Map getLearnMoreTrackingParams( + T eventName, + String mainFormName, +) => + switch (eventName) { + LearnMorePageActions.openLearnMorePage => { + 'event_name': 'info_open', + 'params': { + 'form_name': mainFormName, + 'operating_system': _platform, + }, + }, + LearnMorePageActions.closeLearnMorePage => { + 'event_name': 'info_back', + 'params': { + 'form_name': mainFormName, + 'operating_system': _platform, + }, + }, + _ => throw Exception('Invalid event name'), + }; + +/// Function to get all the Manage passkeys page params. +Map getManagePasskeysTrackingParams(T eventName) => + switch (eventName) { + ManagePasskeysPageActions.openManagePasskeysPage => { + 'event_name': 'open', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': _platform, + }, + }, + ManagePasskeysPageActions.closeManagePasskeysPage => { + 'event_name': 'close', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': _platform, + }, + }, + _ => throw Exception('Invalid event name'), + }; + +/// Function to get all the Create passkey page params. +Map getCreatePasskeyTrackingParams( + T eventName, { + String? mainFormName, + String? subFormName, + String? errorMessage, +}) => + switch (eventName) { + CreatePasskeyFlowActions.createPasskey => { + 'event_name': 'create_passkey_started', + 'params': { + 'form_name': mainFormName, + 'subform_name': subFormName, + 'operating_system': _platform, + }, + }, + CreatePasskeyFlowActions.createPasskeySuccess => { + 'event_name': 'create_passkey_finished', + 'params': { + 'form_name': mainFormName, + 'subform_name': subFormName, + 'operating_system': _platform, + }, + }, + CreatePasskeyFlowActions.error => { + 'event_name': 'error', + 'params': { + 'form_name': mainFormName, + 'subform_name': subFormName, + 'operating_system': _platform, + 'error_message': errorMessage, + }, + }, + CreatePasskeyFlowActions.continueTrading => { + 'event_name': 'create_passkey_continue_trading', + 'params': { + 'form_name': mainFormName, + 'subform_name': subFormName, + 'operating_system': _platform, + }, + }, + CreatePasskeyFlowActions.addMorePasskeys => { + 'event_name': 'create_passkey_add_more_passkeys', + 'params': { + 'form_name': mainFormName, + 'subform_name': subFormName, + 'operating_system': _platform, + }, + }, + _ => throw Exception('Invalid event name'), + }; + +/// Function to get all the Rename passkey page params. +Map getRenamePasskeyTrackingParams(T eventName) => + switch (eventName) { + RenamePasskeyFlowActions.renamePasskey => { + 'event_name': 'passkey_rename_open', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': _platform, + }, + }, + RenamePasskeyFlowActions.renamePasskeySuccess => { + 'event_name': 'passkey_rename_success', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': _platform, + }, + }, + RenamePasskeyFlowActions.cancelRenamePasskey => { + 'event_name': 'passkey_rename_back', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': _platform, + }, + }, + _ => throw Exception('Invalid event name'), + }; diff --git a/packages/deriv_passkeys/lib/src/extensions/context_extensions.dart b/packages/deriv_passkeys/lib/src/core/extensions/context_extensions.dart similarity index 100% rename from packages/deriv_passkeys/lib/src/extensions/context_extensions.dart rename to packages/deriv_passkeys/lib/src/core/extensions/context_extensions.dart diff --git a/packages/deriv_passkeys/lib/src/data/mappers/deriv_passkeys_mapper.dart b/packages/deriv_passkeys/lib/src/data/mappers/deriv_passkeys_mapper.dart index 9404c3987..468a647cf 100644 --- a/packages/deriv_passkeys/lib/src/data/mappers/deriv_passkeys_mapper.dart +++ b/packages/deriv_passkeys/lib/src/data/mappers/deriv_passkeys_mapper.dart @@ -51,6 +51,7 @@ class DerivPasskeysMapper { DerivPasskeysVerifyCredentialsResponseModel model) => DerivPasskeysVerifyCredentialsResponseEntity( token: (model.response['tokens'] as List).first['token'], + refreshToken: model.response['refresh_token'] as String, ); /// Maps [ConnectionInfoEntity] to [PasskeysConnectionInfoModel]. diff --git a/packages/deriv_passkeys/lib/src/data/repositories/passkey_analytics_repository.dart b/packages/deriv_passkeys/lib/src/data/repositories/passkey_analytics_repository.dart new file mode 100644 index 000000000..a72f33172 --- /dev/null +++ b/packages/deriv_passkeys/lib/src/data/repositories/passkey_analytics_repository.dart @@ -0,0 +1,286 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_passkeys/src/core/constants/analytics_actions_enums.dart'; +import 'package:deriv_passkeys/src/core/constants/tracking_event_params_helper.dart'; +import 'package:deriv_passkeys/src/domain/base_repositories/base_passkey_analytics_repository.dart'; + +/// [AnalyticsRepository] is an implementation of [BasePasskeyAnalyticsRepository]. +class AnalyticsRepository extends BasePasskeyAnalyticsRepository { + /// Constructor for [AnalyticsRepository]. + AnalyticsRepository._( + this._appId, { + required this.derivRudderstack, + }); + + static AnalyticsRepository? _instance; + + /// Singleton instance of [AnalyticsRepository]. + static AnalyticsRepository get instance => + _instance ??= throw Exception('AnalyticsRepository is not initialized'); + + /// Instance of [DerivRudderstack]. + final DerivRudderstack derivRudderstack; + + /// Initialize [AnalyticsRepository]. + static void init( + String appId, { + required DerivRudderstack derivRudderstack, + }) => + _instance = AnalyticsRepository._( + appId, + derivRudderstack: derivRudderstack, + ); + + String? _mainFormName; + + String? _subFormName; + + /// Deriv client app ID. + final String _appId; + + @override + void trackOpenEffortlessLoginPage() { + final Map trackingData = getEffortlessLoginTrackingParams( + EffortlessPageActions.openEffortlessLoginPage, + ); + + _mainFormName = trackingData['params']['form_name']; + _subFormName = 'passkey_effortless'; + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackCloseEffortlessLoginPage() { + final Map trackingData = getEffortlessLoginTrackingParams( + EffortlessPageActions.closeEffortlessLoginPage, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackMaybeLater() { + final Map trackingData = getEffortlessLoginTrackingParams( + EffortlessPageActions.maybeLater, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackOpenLearnMorePage() { + final Map trackingData = getLearnMoreTrackingParams( + LearnMorePageActions.openLearnMorePage, + _mainFormName ?? 'testing_main_form', + ); + + _subFormName = 'passkey_info'; + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackCloseLearnMorePage() { + final Map trackingData = getLearnMoreTrackingParams( + LearnMorePageActions.closeLearnMorePage, + _mainFormName ?? 'testing_main_form', + ); + + if (_mainFormName == null) { + _mainFormName = 'testing_main_form'; + } else { + _subFormName = _mainFormName!.contains('account_settings') + ? 'passkey_main' + : 'passkey_effortless'; + } + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackOpenManagePasskeysPage() { + final Map trackingData = getManagePasskeysTrackingParams( + ManagePasskeysPageActions.openManagePasskeysPage, + ); + + _mainFormName = trackingData['params']['form_name']; + _subFormName = 'passkey_main'; + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackCloseManagePasskeysPage() { + final Map trackingData = getManagePasskeysTrackingParams( + ManagePasskeysPageActions.closeManagePasskeysPage, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackCreatePasskey() { + final Map trackingData = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.createPasskey, + mainFormName: _mainFormName, + subFormName: _subFormName, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackCreatePasskeySuccess() { + final Map trackingData = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.createPasskeySuccess, + mainFormName: _mainFormName, + subFormName: _subFormName, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackPasskeyError(String errorMessage) { + final Map trackingData = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.error, + mainFormName: _mainFormName, + subFormName: _subFormName, + errorMessage: errorMessage, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackContinueTrading() { + final Map trackingData = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.continueTrading, + mainFormName: _mainFormName, + subFormName: _subFormName, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackAddMorePasskeys() { + final Map trackingData = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.addMorePasskeys, + mainFormName: _mainFormName, + subFormName: _subFormName, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackRenamePasskey() { + final Map trackingData = getRenamePasskeyTrackingParams( + RenamePasskeyFlowActions.renamePasskey, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackCancelRenamePasskey() { + final Map trackingData = getRenamePasskeyTrackingParams( + RenamePasskeyFlowActions.cancelRenamePasskey, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + @override + void trackRenamePasskeySuccess() { + final Map trackingData = getRenamePasskeyTrackingParams( + RenamePasskeyFlowActions.renamePasskeySuccess, + ); + + _addAppName(trackingData); + + derivRudderstack.track( + eventName: trackingData['event_name'], + properties: trackingData['params'], + ); + } + + void _addAppName(Map data) { + data['params']['form_name'] += _appId == '23789' + ? 'derivgo' + : _appId == '1408' + ? 'p2p' + : 'unsupported'; + } +} diff --git a/packages/deriv_passkeys/lib/src/domain/base_repositories/base_passkey_analytics_repository.dart b/packages/deriv_passkeys/lib/src/domain/base_repositories/base_passkey_analytics_repository.dart new file mode 100644 index 000000000..e1a4addc1 --- /dev/null +++ b/packages/deriv_passkeys/lib/src/domain/base_repositories/base_passkey_analytics_repository.dart @@ -0,0 +1,47 @@ +/// [BasePasskeyAnalyticsRepository] defines the interface for analytics repositories. +abstract class BasePasskeyAnalyticsRepository { + /// Track opening effortless login page. + void trackOpenEffortlessLoginPage(); + + /// Track closing effortless login page. + void trackCloseEffortlessLoginPage(); + + /// Track pressing maybe later button. + void trackMaybeLater(); + + /// Track opening learn more page. + void trackOpenLearnMorePage(); + + /// Track closing learn more page. + void trackCloseLearnMorePage(); + + /// Track opening manage passkeys page. + void trackOpenManagePasskeysPage(); + + /// Track closing manage passkeys page. + void trackCloseManagePasskeysPage(); + + /// Track pressing create passkey button. + void trackCreatePasskey(); + + /// Track create passkey success. + void trackCreatePasskeySuccess(); + + /// Track passkey error. + void trackPasskeyError(String errorMessage); + + /// Track pressing continue trading button. + void trackContinueTrading(); + + /// Track pressing add more passkeys button. + void trackAddMorePasskeys(); + + /// Track pressing rename passkey button. + void trackRenamePasskey(); + + /// Track cancel rename passkey. + void trackCancelRenamePasskey(); + + /// Track rename passkey success. + void trackRenamePasskeySuccess(); +} diff --git a/packages/deriv_passkeys/lib/src/domain/entities/deriv_passkeys_verify_credentials_response_entity.dart b/packages/deriv_passkeys/lib/src/domain/entities/deriv_passkeys_verify_credentials_response_entity.dart index 1e249d40a..aeeca7d38 100644 --- a/packages/deriv_passkeys/lib/src/domain/entities/deriv_passkeys_verify_credentials_response_entity.dart +++ b/packages/deriv_passkeys/lib/src/domain/entities/deriv_passkeys_verify_credentials_response_entity.dart @@ -5,11 +5,15 @@ final class DerivPasskeysVerifyCredentialsResponseEntity extends Equatable { /// Creates a [DerivPasskeysVerifyCredentialsResponseEntity]. const DerivPasskeysVerifyCredentialsResponseEntity({ required this.token, + required this.refreshToken, }); - /// contains the token from the REST API to verify credentials. + /// App token from the REST API to verify credentials. final String token; + /// Refresh token from the REST API to verify credentials. + final String refreshToken; + @override - List get props => [token]; + List get props => [token, refreshToken]; } diff --git a/packages/deriv_passkeys/lib/src/presentation/mixins/passkey_event_tracking_mixin.dart b/packages/deriv_passkeys/lib/src/presentation/mixins/passkey_event_tracking_mixin.dart new file mode 100644 index 000000000..b5a8a4e51 --- /dev/null +++ b/packages/deriv_passkeys/lib/src/presentation/mixins/passkey_event_tracking_mixin.dart @@ -0,0 +1,65 @@ +import 'package:deriv_passkeys/src/data/repositories/passkey_analytics_repository.dart'; +import 'package:deriv_passkeys/src/domain/base_repositories/base_passkey_analytics_repository.dart'; +import 'package:flutter/foundation.dart'; + +/// Mixin that provides analytics tracking functions. +mixin PasskeyEventTrackingMixin implements BasePasskeyAnalyticsRepository { + /// The analytics repository. + @visibleForTesting + BasePasskeyAnalyticsRepository get analyticsRepository => + AnalyticsRepository.instance; + + @override + void trackOpenEffortlessLoginPage() => + analyticsRepository.trackOpenEffortlessLoginPage(); + + @override + void trackCloseEffortlessLoginPage() => + analyticsRepository.trackCloseEffortlessLoginPage(); + + @override + void trackMaybeLater() => analyticsRepository.trackMaybeLater(); + + @override + void trackOpenLearnMorePage() => analyticsRepository.trackOpenLearnMorePage(); + + @override + void trackCloseLearnMorePage() => + analyticsRepository.trackCloseLearnMorePage(); + + @override + void trackOpenManagePasskeysPage() => + analyticsRepository.trackOpenManagePasskeysPage(); + + @override + void trackCloseManagePasskeysPage() => + analyticsRepository.trackCloseManagePasskeysPage(); + + @override + void trackCreatePasskey() => analyticsRepository.trackCreatePasskey(); + + @override + void trackCreatePasskeySuccess() => + analyticsRepository.trackCreatePasskeySuccess(); + + @override + void trackPasskeyError(String errorMessage) => + analyticsRepository.trackPasskeyError(errorMessage); + + @override + void trackContinueTrading() => analyticsRepository.trackContinueTrading(); + + @override + void trackAddMorePasskeys() => analyticsRepository.trackAddMorePasskeys(); + + @override + void trackRenamePasskey() => analyticsRepository.trackRenamePasskey(); + + @override + void trackCancelRenamePasskey() => + analyticsRepository.trackCancelRenamePasskey(); + + @override + void trackRenamePasskeySuccess() => + analyticsRepository.trackRenamePasskeySuccess(); +} diff --git a/packages/deriv_passkeys/lib/src/presentation/pages/effortless_passkeys_login_page.dart b/packages/deriv_passkeys/lib/src/presentation/pages/effortless_passkeys_login_page.dart index 69b03215e..7fd1f0f51 100644 --- a/packages/deriv_passkeys/lib/src/presentation/pages/effortless_passkeys_login_page.dart +++ b/packages/deriv_passkeys/lib/src/presentation/pages/effortless_passkeys_login_page.dart @@ -1,5 +1,6 @@ -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_passkeys/src/presentation/constants/assets.dart'; +import 'package:deriv_passkeys/src/presentation/mixins/passkey_event_tracking_mixin.dart'; import 'package:deriv_passkeys/src/presentation/pages/learn_more_passkeys_page.dart'; import 'package:deriv_passkeys/src/presentation/pages/passkey_created_page.dart'; import 'package:deriv_passkeys/src/presentation/states/bloc/deriv_passkeys_bloc.dart'; @@ -13,14 +14,17 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; /// A stateless widget to build the Effortless Passkeys page. -class EffortlessPasskeysPage extends StatelessWidget { +class EffortlessPasskeysPage extends StatelessWidget + with PasskeyEventTrackingMixin { /// Creates a [EffortlessPasskeysPage]. - const EffortlessPasskeysPage({ + EffortlessPasskeysPage({ required this.onPageClosed, required this.addMorePasskeysNavigationCallback, required this.continueTradingNavigationCallback, super.key, - }); + }) { + trackOpenEffortlessLoginPage(); + } /// The route name for the effortless passkeys page. static const String routeName = 'effortless_passkeys_page'; @@ -34,180 +38,207 @@ class EffortlessPasskeysPage extends StatelessWidget { /// Callback to be called when the flow is complete. final void Function(BuildContext context) onPageClosed; - /// - @override - Widget build(BuildContext context) => - BlocListener( - listener: (BuildContext context, DerivPasskeysState state) { - if (state is DerivPasskeysCreatedSuccessfullyState) { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (BuildContext context) => PasskeyCreatedPage( - onPageClose: onPageClosed, - bottomCallToAction: PasskeysCreatedCallToAction( - addMorePasskeysNavigationCallback: - addMorePasskeysNavigationCallback, - continueTradingNavigationCallback: - continueTradingNavigationCallback, - ), - )), - ); - } else if (state is DerivPasskeysErrorState) { - handlePasskeysError(context, state); - } + Widget build(BuildContext context) => WillPopScope( + onWillPop: () async { + trackCloseEffortlessLoginPage(); + return true; }, - child: Scaffold( - body: SafeArea( - child: LayoutBuilder( - builder: (_, BoxConstraints constraints) => - SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: constraints.maxWidth, - minHeight: constraints.maxHeight), - child: IntrinsicHeight( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - alignment: Alignment.topRight, - child: Padding( - padding: const EdgeInsets.all(16), - child: TextButton( - onPressed: () => onPageClosed(context), - child: Text( - context - .derivPasskeysLocalizations.maybeLater - .toUpperCase(), - style: TextStyle( - color: context.theme.colors.coral, + child: BlocListener( + listener: (BuildContext context, DerivPasskeysState state) { + if (state is DerivPasskeysCreatedSuccessfullyState) { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) => PasskeyCreatedPage( + onPageClose: (BuildContext context) { + onPageClosed(context); + }, + bottomCallToAction: PasskeysCreatedCallToAction( + addMorePasskeysNavigationCallback: + (BuildContext context) { + trackAddMorePasskeys(); + addMorePasskeysNavigationCallback(context); + }, + continueTradingNavigationCallback: + (BuildContext context) { + trackContinueTrading(); + continueTradingNavigationCallback(context); + }, + ), + )), + ); + } else if (state is DerivPasskeysErrorState) { + trackPasskeyError('${state.errorCode}: ${state.message}'); + handlePasskeysError(context, state); + } + }, + child: Scaffold( + body: SafeArea( + child: LayoutBuilder( + builder: (_, BoxConstraints constraints) => + SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: constraints.maxWidth, + minHeight: constraints.maxHeight), + child: IntrinsicHeight( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + alignment: Alignment.topRight, + child: Padding( + padding: const EdgeInsets.all(16), + child: TextButton( + onPressed: () { + trackMaybeLater(); + onPageClosed(context); + }, + child: Text( + context.derivPasskeysLocalizations + .maybeLater + .toUpperCase(), + style: TextStyle( + color: context.theme.colors.coral, + ), ), ), ), ), - ), - Expanded( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 96), - child: Column( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - SvgPicture.asset( - Assets.effortlessPasskeysIcon, - package: 'deriv_passkeys', - ), - Text( - context.derivPasskeysLocalizations - .effortlessLoginWithPasskeys, - style: const TextStyle(fontSize: 20), - ), - const SizedBox( - height: 24, - ), - IconTextRowWidget( - assetName: Assets.fingerPrintIcon, - text: context.derivPasskeysLocalizations - .noNeedToRememberPassword, - ), - Divider( - color: context.theme.colors.hover, - ), - IconTextRowWidget( - assetName: Assets.syncIcon, - text: context.derivPasskeysLocalizations - .syncAcrossDevices, - ), - Divider( - color: context.theme.colors.hover, - ), - IconTextRowWidget( - assetName: Assets.lockIcon, - text: context.derivPasskeysLocalizations - .useYourBiometrics, - ), - Divider( - color: context.theme.colors.hover, - ), - SizedBox( - width: double.infinity, - child: RichText( - text: TextSpan( - children: [ - TextSpan( - text: - '${context.derivPasskeysLocalizations.learnMoreAboutPasskeys} ', - style: TextStyle( - color: context - .theme.colors.general, - )), - WidgetSpan( - alignment: - PlaceholderAlignment.middle, - child: InkWell( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => - LearnMorePasskeysPage( - onPageClosed: - (BuildContext - context) { + Expanded( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 96), + child: Column( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + SvgPicture.asset( + Assets.effortlessPasskeysIcon, + package: 'deriv_passkeys', + ), + Text( + context.derivPasskeysLocalizations + .effortlessLoginWithPasskeys, + style: const TextStyle(fontSize: 20), + ), + const SizedBox( + height: 24, + ), + IconTextRowWidget( + assetName: Assets.fingerPrintIcon, + text: context + .derivPasskeysLocalizations + .noNeedToRememberPassword, + ), + Divider( + color: context.theme.colors.hover, + ), + IconTextRowWidget( + assetName: Assets.syncIcon, + text: context + .derivPasskeysLocalizations + .syncAcrossDevices, + ), + Divider( + color: context.theme.colors.hover, + ), + IconTextRowWidget( + assetName: Assets.lockIcon, + text: context + .derivPasskeysLocalizations + .useYourBiometrics, + ), + Divider( + color: context.theme.colors.hover, + ), + SizedBox( + width: double.infinity, + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: + '${context.derivPasskeysLocalizations.learnMoreAboutPasskeys} ', + style: TextStyle( + color: context + .theme.colors.general, + )), + WidgetSpan( + alignment: + PlaceholderAlignment + .middle, + child: InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute< + Widget>( + builder: (_) => LearnMorePasskeysPage( + onPageClosed: + (BuildContext + context) { Navigator.pop( context); - }, - addMorePasskeysNavigationCallback: - addMorePasskeysNavigationCallback, - continueTradingNavigationCallback: - continueTradingNavigationCallback, + }, addMorePasskeysNavigationCallback: + (BuildContext + context) { + trackAddMorePasskeys(); + addMorePasskeysNavigationCallback( + context); + }, continueTradingNavigationCallback: + (BuildContext + context) { + trackContinueTrading(); + continueTradingNavigationCallback( + context); + }), ), - ), - ); - }, - child: Text( - '${context.derivPasskeysLocalizations.here}.', - style: TextStyle( - color: context.theme - .colors.coral), + ); + }, + child: Text( + '${context.derivPasskeysLocalizations.here}.', + style: TextStyle( + color: context.theme + .colors.coral), + ), ), ), - ), - ], + ], + ), ), - ), - ) - ], + ) + ], + ), ), ), - ), - Container( - width: double.infinity, - child: Padding( - padding: const EdgeInsets.all(16), - child: PrimaryButton( - onPressed: () { - context.read().add( - DerivPasskeysCreateCredentialEvent()); - }, - child: Text( - context.derivPasskeysLocalizations - .createPasskey, - style: TextStyle( - color: context.theme.colors.prominent, + Container( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.all(16), + child: PrimaryButton( + onPressed: () { + trackCreatePasskey(); + context.read().add( + DerivPasskeysCreateCredentialEvent()); + }, + child: Text( + context.derivPasskeysLocalizations + .createPasskey, + style: TextStyle( + color: context.theme.colors.prominent, + ), ), ), ), - ), - ) - ], + ) + ], + ), ), ), - ), - )), + )), + ), ), ), ); diff --git a/packages/deriv_passkeys/lib/src/presentation/pages/learn_more_passkeys_page.dart b/packages/deriv_passkeys/lib/src/presentation/pages/learn_more_passkeys_page.dart index d4cf3f49b..3dc033233 100644 --- a/packages/deriv_passkeys/lib/src/presentation/pages/learn_more_passkeys_page.dart +++ b/packages/deriv_passkeys/lib/src/presentation/pages/learn_more_passkeys_page.dart @@ -1,6 +1,7 @@ import 'package:deriv_passkeys/deriv_passkeys.dart'; -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_passkeys/src/presentation/constants/assets.dart'; +import 'package:deriv_passkeys/src/presentation/mixins/passkey_event_tracking_mixin.dart'; import 'package:deriv_passkeys/src/presentation/pages/passkey_created_page.dart'; import 'package:deriv_passkeys/src/presentation/widgets/passkey_created_call_to_action.dart'; import 'package:deriv_passkeys/src/presentation/widgets/section_title_and_content.dart'; @@ -12,14 +13,17 @@ import 'package:flutter_svg/svg.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; /// A stateless widget to build the Learn More Passkeys page. -class LearnMorePasskeysPage extends StatelessWidget { +class LearnMorePasskeysPage extends StatelessWidget + with PasskeyEventTrackingMixin { /// Creates a [LearnMorePasskeysPage]. - const LearnMorePasskeysPage({ + LearnMorePasskeysPage({ required this.onPageClosed, required this.addMorePasskeysNavigationCallback, required this.continueTradingNavigationCallback, super.key, - }); + }) { + trackOpenLearnMorePage(); + } /// Callback to be called when the flow is complete. final void Function(BuildContext context) onPageClosed; @@ -31,231 +35,238 @@ class LearnMorePasskeysPage extends StatelessWidget { final void Function(BuildContext context) continueTradingNavigationCallback; @override - Widget build(BuildContext context) => - BlocListener( - listener: (BuildContext context, DerivPasskeysState state) { - if (state is DerivPasskeysCreatedSuccessfullyState) { - Navigator.pop(context); - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (BuildContext context) => PasskeyCreatedPage( - onPageClose: onPageClosed, - bottomCallToAction: PasskeysCreatedCallToAction( - addMorePasskeysNavigationCallback: - addMorePasskeysNavigationCallback, - continueTradingNavigationCallback: - continueTradingNavigationCallback, - ), - )), - ); - } + Widget build(BuildContext context) => WillPopScope( + onWillPop: () async { + trackCloseLearnMorePage(); + return true; }, - child: Scaffold( - appBar: AppBar(), - body: Column( - children: [ - Expanded( - child: SingleChildScrollView( - child: SafeArea( - child: Padding( - padding: const EdgeInsets.all(24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 14), - child: SvgPicture.asset( - Assets.effortlessPasskeysIcon, - package: 'deriv_passkeys', - ), + child: BlocListener( + listener: (BuildContext context, DerivPasskeysState state) { + if (state is DerivPasskeysCreatedSuccessfullyState) { + Navigator.pop(context); + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (BuildContext context) => PasskeyCreatedPage( + onPageClose: onPageClosed, + bottomCallToAction: PasskeysCreatedCallToAction( + addMorePasskeysNavigationCallback: + addMorePasskeysNavigationCallback, + continueTradingNavigationCallback: + continueTradingNavigationCallback, ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 24), - child: Text( - context - .derivPasskeysLocalizations.effortlessLogin, - style: const TextStyle(fontSize: 20), + )), + ); + } + }, + child: Scaffold( + appBar: AppBar(), + body: Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 14), + child: SvgPicture.asset( + Assets.effortlessPasskeysIcon, + package: 'deriv_passkeys', + ), ), - ), - SectionTitleAndContent( - title: context - .derivPasskeysLocalizations.whatArePasskeys, - texts: [ - context.derivPasskeysLocalizations - .whatArePasskeysDescriptionPoint1, - context.derivPasskeysLocalizations - .whatArePasskeysDescriptionPoint2 - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Divider( - color: context.theme.colors.hover, + Padding( + padding: const EdgeInsets.symmetric(vertical: 24), + child: Text( + context + .derivPasskeysLocalizations.effortlessLogin, + style: const TextStyle(fontSize: 20), + ), ), - ), - SectionTitleAndContent( - title: - context.derivPasskeysLocalizations.whyPasskeys, - texts: [ - context.derivPasskeysLocalizations - .whyPasskeysDescription1, - context.derivPasskeysLocalizations - .whyPasskeysDescription2 - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Divider( - color: context.theme.colors.hover, + SectionTitleAndContent( + title: context + .derivPasskeysLocalizations.whatArePasskeys, + texts: [ + context.derivPasskeysLocalizations + .whatArePasskeysDescriptionPoint1, + context.derivPasskeysLocalizations + .whatArePasskeysDescriptionPoint2 + ], ), - ), - SectionTitleAndContent( - title: context.read().isDp2p - ? context.derivPasskeysLocalizations - .p2pHowToCreatePasskey - : context.derivPasskeysLocalizations - .howToCreatePasskey, - texts: [ - context.read().isDp2p - ? context.derivPasskeysLocalizations - .p2pHowToCreatePasskeyDescription1 - : context.derivPasskeysLocalizations - .howToCreatePasskeyDescription1, - context.read().isDp2p + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Divider( + color: context.theme.colors.hover, + ), + ), + SectionTitleAndContent( + title: context + .derivPasskeysLocalizations.whyPasskeys, + texts: [ + context.derivPasskeysLocalizations + .whyPasskeysDescription1, + context.derivPasskeysLocalizations + .whyPasskeysDescription2 + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Divider( + color: context.theme.colors.hover, + ), + ), + SectionTitleAndContent( + title: context.read().isDp2p ? context.derivPasskeysLocalizations - .p2pHowToCreatePasskeyDescription2 + .p2pHowToCreatePasskey : context.derivPasskeysLocalizations - .howToCreatePasskeyDescription2 - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Divider( - color: context.theme.colors.hover, + .howToCreatePasskey, + texts: [ + context.read().isDp2p + ? context.derivPasskeysLocalizations + .p2pHowToCreatePasskeyDescription1 + : context.derivPasskeysLocalizations + .howToCreatePasskeyDescription1, + context.read().isDp2p + ? context.derivPasskeysLocalizations + .p2pHowToCreatePasskeyDescription2 + : context.derivPasskeysLocalizations + .howToCreatePasskeyDescription2 + ], ), - ), - SectionTitleAndContent( - title: context.derivPasskeysLocalizations - .whereArePasskeysSaved, - texts: [ - context.derivPasskeysLocalizations - .whereArePasskeysSavedDescriptionAndroid, - context.derivPasskeysLocalizations - .whereArePasskeysSavedDescriptionIOS - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16), - child: Divider( - color: context.theme.colors.hover, + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Divider( + color: context.theme.colors.hover, + ), ), - ), - SectionTitleAndContent( - title: context.derivPasskeysLocalizations - .whatHappensIfEmailChanged, - texts: [ - context.derivPasskeysLocalizations - .whatHappensIfEmailChangedDescription1, - context.derivPasskeysLocalizations - .whatHappensIfEmailChangedDescription2 - ], - ), - Container( - width: double.infinity, - padding: const EdgeInsets.all(16), - margin: const EdgeInsets.only(top: 16), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: context.theme.colors.active, + SectionTitleAndContent( + title: context.derivPasskeysLocalizations + .whereArePasskeysSaved, + texts: [ + context.derivPasskeysLocalizations + .whereArePasskeysSavedDescriptionAndroid, + context.derivPasskeysLocalizations + .whereArePasskeysSavedDescriptionIOS + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Divider( + color: context.theme.colors.hover, ), ), - child: FittedBox( - fit: BoxFit.scaleDown, - alignment: Alignment.centerLeft, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SvgPicture.asset( - Assets.lightBulbIcon, - package: 'deriv_passkeys', - ), - const SizedBox( - width: 8, - ), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '${context.derivPasskeysLocalizations.tips}:', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: context.theme.colors.prominent, + SectionTitleAndContent( + title: context.derivPasskeysLocalizations + .whatHappensIfEmailChanged, + texts: [ + context.derivPasskeysLocalizations + .whatHappensIfEmailChangedDescription1, + context.derivPasskeysLocalizations + .whatHappensIfEmailChangedDescription2 + ], + ), + Container( + width: double.infinity, + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.only(top: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: context.theme.colors.active, + ), + ), + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: Alignment.centerLeft, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset( + Assets.lightBulbIcon, + package: 'deriv_passkeys', + ), + const SizedBox( + width: 8, + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '${context.derivPasskeysLocalizations.tips}:', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: + context.theme.colors.prominent, + ), + ), + Text( + '${context.derivPasskeysLocalizations.beforeUsingPasskeys}:', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: context.theme.colors.general, + ), ), - ), - Text( - '${context.derivPasskeysLocalizations.beforeUsingPasskeys}:', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: context.theme.colors.general, + const SizedBox( + height: 4, ), - ), - const SizedBox( - height: 4, - ), - UnorderedList( - texts: [ - context.derivPasskeysLocalizations - .enableScreenLock, - context.derivPasskeysLocalizations - .signInGoogleOrIcloud, - context.derivPasskeysLocalizations - .enableBluetooth - ], - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: context.theme.colors.general, + UnorderedList( + texts: [ + context.derivPasskeysLocalizations + .enableScreenLock, + context.derivPasskeysLocalizations + .signInGoogleOrIcloud, + context.derivPasskeysLocalizations + .enableBluetooth + ], + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: context.theme.colors.general, + ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), - ), - ], + ], + ), ), ), ), ), - ), - Container( - color: context.theme.colors.secondary, - width: double.infinity, - child: Padding( - padding: const EdgeInsets.only( - left: 24, right: 24, bottom: 24, top: 16), - child: PrimaryButton( - onPressed: () { - context - .read() - .add(DerivPasskeysCreateCredentialEvent()); - }, - child: Text( - context.derivPasskeysLocalizations.createPasskey, - style: TextStyle( - color: context.theme.colors.prominent, + Container( + color: context.theme.colors.secondary, + width: double.infinity, + child: Padding( + padding: const EdgeInsets.only( + left: 24, right: 24, bottom: 24, top: 16), + child: PrimaryButton( + onPressed: () { + context + .read() + .add(DerivPasskeysCreateCredentialEvent()); + }, + child: Text( + context.derivPasskeysLocalizations.createPasskey, + style: TextStyle( + color: context.theme.colors.prominent, + ), ), ), ), ), - ), - ], + ], + ), ), ), ); diff --git a/packages/deriv_passkeys/lib/src/presentation/pages/manage_passkeys_page.dart b/packages/deriv_passkeys/lib/src/presentation/pages/manage_passkeys_page.dart index fa28503bb..86071a04b 100644 --- a/packages/deriv_passkeys/lib/src/presentation/pages/manage_passkeys_page.dart +++ b/packages/deriv_passkeys/lib/src/presentation/pages/manage_passkeys_page.dart @@ -1,5 +1,6 @@ -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_passkeys/src/presentation/constants/assets.dart'; +import 'package:deriv_passkeys/src/presentation/mixins/passkey_event_tracking_mixin.dart'; import 'package:deriv_passkeys/src/presentation/pages/learn_more_passkeys_page.dart'; import 'package:deriv_passkeys/src/presentation/pages/passkey_created_page.dart'; import 'package:deriv_passkeys/src/presentation/states/bloc/deriv_passkeys_bloc.dart'; @@ -34,110 +35,137 @@ class ManagePasskeysPage extends StatefulWidget { State createState() => _ManagePasskeysPageState(); } -class _ManagePasskeysPageState extends State { +class _ManagePasskeysPageState extends State + with PasskeyEventTrackingMixin { @override void initState() { super.initState(); + trackOpenManagePasskeysPage(); context .read() .add(const DerivPasskeysGetPasskeysListEvent()); } @override - Widget build(BuildContext context) => Scaffold( - backgroundColor: context.theme.colors.primary, - appBar: AppBar( - title: const Text('Passkeys', style: TextStyle(fontSize: 20)), - actions: [ - InkWell( - child: Padding( - padding: const EdgeInsets.all(16), - child: SvgPicture.asset( - Assets.learnMorePasskeysIcon, - package: 'deriv_passkeys', + Widget build(BuildContext context) => WillPopScope( + onWillPop: () async { + trackCloseManagePasskeysPage(); + return true; + }, + child: Scaffold( + backgroundColor: context.theme.colors.primary, + appBar: AppBar( + title: const Text('Passkeys', style: TextStyle(fontSize: 20)), + actions: [ + InkWell( + child: Padding( + padding: const EdgeInsets.all(16), + child: SvgPicture.asset( + Assets.learnMorePasskeysIcon, + package: 'deriv_passkeys', + ), ), - ), - onTap: () { - Navigator.push( - context, - MaterialPageRoute( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( builder: (BuildContext context) => LearnMorePasskeysPage( - onPageClosed: (BuildContext context) { - Navigator.pop(context); - }, - addMorePasskeysNavigationCallback: - widget.addMorePasskeysNavigationCallback, - continueTradingNavigationCallback: - widget.continueTradingNavigationCallback, - )), - ); - }, - ), - ], - ), - body: BlocConsumer( - listener: (BuildContext context, DerivPasskeysState state) { - if (state is DerivPasskeysCreatedSuccessfullyState) { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext context) => PasskeyCreatedPage( - onPageClose: (BuildContext context) { + onPageClosed: (BuildContext context) { Navigator.pop(context); }, - bottomCallToAction: PasskeysCreatedCallToAction( - addMorePasskeysNavigationCallback: - widget.addMorePasskeysNavigationCallback, - continueTradingNavigationCallback: - widget.continueTradingNavigationCallback, - ), - )), - ); - } else if (state is DerivPasskeysErrorState) { - handlePasskeysError(context, state); - } - }, builder: (BuildContext context, DerivPasskeysState state) { - if (state is DerivPasskeysLoadedState) { - return SafeArea( - child: SizedBox( - width: double.infinity, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _buildDerivPasskeysLoadedContent( - context, - state, + addMorePasskeysNavigationCallback: + (BuildContext context) { + trackAddMorePasskeys(); + widget.addMorePasskeysNavigationCallback(context); + }, + continueTradingNavigationCallback: + (BuildContext context) { + trackContinueTrading(); + widget.continueTradingNavigationCallback(context); + }, + ), ), - Container( - width: double.infinity, - child: Padding( - padding: const EdgeInsets.all(16), - child: PrimaryButton( - onPressed: () { - context - .read() - .add(DerivPasskeysCreateCredentialEvent()); - }, - child: Text( - context.derivPasskeysLocalizations.createPasskey, - style: TextStyle( - color: context.theme.colors.prominent, + ); + }, + ), + ], + ), + body: BlocConsumer( + listener: (BuildContext context, DerivPasskeysState state) { + if (state is DerivPasskeysCreatedSuccessfullyState) { + trackCreatePasskeySuccess(); + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) => PasskeyCreatedPage( + onPageClose: (BuildContext context) { + Navigator.pop(context); + }, + bottomCallToAction: PasskeysCreatedCallToAction( + addMorePasskeysNavigationCallback: + (BuildContext context) { + trackAddMorePasskeys(); + widget.addMorePasskeysNavigationCallback(context); + }, + continueTradingNavigationCallback: + (BuildContext context) { + trackContinueTrading(); + widget.continueTradingNavigationCallback(context); + }, + ), + ), + ), + ); + } else if (state is DerivPasskeysErrorState) { + trackPasskeyError('${state.errorCode}: ${state.message}'); + handlePasskeysError(context, state); + } + }, builder: (BuildContext context, DerivPasskeysState state) { + if (state is DerivPasskeysLoadedState) { + return SafeArea( + child: SizedBox( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildDerivPasskeysLoadedContent( + context, + state, + ), + Container( + width: double.infinity, + child: Padding( + padding: const EdgeInsets.all(16), + child: PrimaryButton( + onPressed: () { + trackCreatePasskey(); + context + .read() + .add(DerivPasskeysCreateCredentialEvent()); + }, + child: Text( + context.derivPasskeysLocalizations.createPasskey, + style: TextStyle( + color: context.theme.colors.prominent, + ), ), ), ), - ), - ) - ], + ) + ], + ), ), - ), - ); - } - return const SizedBox(); - }), + ); + } + return const SizedBox(); + }), + ), ); Widget _buildDerivPasskeysListContent( - BuildContext context, DerivPasskeysLoadedState state) => + BuildContext context, + DerivPasskeysLoadedState state, + ) => Expanded( child: Padding( padding: const EdgeInsets.all(24), diff --git a/packages/deriv_passkeys/lib/src/presentation/pages/passkey_created_page.dart b/packages/deriv_passkeys/lib/src/presentation/pages/passkey_created_page.dart index b9000812e..db9b0c3fc 100644 --- a/packages/deriv_passkeys/lib/src/presentation/pages/passkey_created_page.dart +++ b/packages/deriv_passkeys/lib/src/presentation/pages/passkey_created_page.dart @@ -1,18 +1,22 @@ -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_passkeys/src/presentation/constants/assets.dart'; +import 'package:deriv_passkeys/src/presentation/mixins/passkey_event_tracking_mixin.dart'; import 'package:deriv_passkeys/src/presentation/utils/platform_utils.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; /// A stateless widget to build the passkey created successfully page. -class PasskeyCreatedPage extends StatelessWidget { +class PasskeyCreatedPage extends StatelessWidget + with PasskeyEventTrackingMixin { /// Creates a [PasskeyCreatedPage]. - const PasskeyCreatedPage({ + PasskeyCreatedPage({ required this.onPageClose, required this.bottomCallToAction, super.key, - }); + }) { + trackCreatePasskeySuccess(); + } /// A callback function that will be called when the user clicks on the 'Continue' button. final void Function(BuildContext context) onPageClose; diff --git a/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_bloc.dart b/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_bloc.dart index cbe09a784..1a8285e7f 100644 --- a/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_bloc.dart +++ b/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_bloc.dart @@ -1,5 +1,7 @@ import 'dart:io'; +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_passkeys/src/data/repositories/passkey_analytics_repository.dart'; import 'package:deriv_passkeys/src/exceptions/platform_exceptions.dart'; import 'package:deriv_passkeys/src/domain/entities/passkeys_connection_info_entity.dart'; import 'package:deriv_passkeys/src/domain/entities/deriv_passkey_entity.dart'; @@ -55,6 +57,8 @@ class DerivPasskeysBloc extends Bloc { emit( DerivPasskeysCredentialVerifiedState( token: derivPasskeysVerifyCredentialsResponseEntity.token, + refreshToken: + derivPasskeysVerifyCredentialsResponseEntity.refreshToken, ), ); }).catchError((Object error) { @@ -145,6 +149,12 @@ class DerivPasskeysBloc extends Bloc { add(const SetDerivPasskeysNotSupportedEvent()); } }); + + /// Initialize the analytics repository. + AnalyticsRepository.init( + connectionInfo.appId, + derivRudderstack: DerivRudderstack(), + ); } /// Passkeys connection info entity. diff --git a/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_state.dart b/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_state.dart index e50ae555c..72f3c115e 100644 --- a/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_state.dart +++ b/packages/deriv_passkeys/lib/src/presentation/states/bloc/deriv_passkeys_state.dart @@ -33,13 +33,19 @@ class DerivPasskeysCreatedSuccessfullyState extends DerivPasskeysState {} /// [DerivPasskeysCredentialVerifiedState] represents the credential verified state within the DerivPasskeys flow. class DerivPasskeysCredentialVerifiedState extends DerivPasskeysState { /// Creates a [DerivPasskeysCredentialVerifiedState]. - const DerivPasskeysCredentialVerifiedState({required this.token}); + const DerivPasskeysCredentialVerifiedState({ + required this.token, + required this.refreshToken, + }); - /// The response. + /// App token from the response. final String token; + /// Refresh token from the response. + final String refreshToken; + @override - List get props => [token]; + List get props => [token, refreshToken]; } /// [DerivPasskeysNotSupportedState] represents the not supported state within the DerivPasskeys flow. diff --git a/packages/deriv_passkeys/lib/src/presentation/utils/handle_errors_utils.dart b/packages/deriv_passkeys/lib/src/presentation/utils/handle_errors_utils.dart index 192db00e2..7358e6d8f 100644 --- a/packages/deriv_passkeys/lib/src/presentation/utils/handle_errors_utils.dart +++ b/packages/deriv_passkeys/lib/src/presentation/utils/handle_errors_utils.dart @@ -1,6 +1,6 @@ //handlePasskeysError import 'package:deriv_passkeys/deriv_passkeys.dart'; -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_ui/widgets/popup_alert_dialog.dart'; import 'package:flutter/material.dart'; diff --git a/packages/deriv_passkeys/lib/src/presentation/utils/platform_utils.dart b/packages/deriv_passkeys/lib/src/presentation/utils/platform_utils.dart index 4dbd2c265..1e217a7e9 100644 --- a/packages/deriv_passkeys/lib/src/presentation/utils/platform_utils.dart +++ b/packages/deriv_passkeys/lib/src/presentation/utils/platform_utils.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:flutter/widgets.dart'; /// Returns the platform name. diff --git a/packages/deriv_passkeys/lib/src/presentation/widgets/continue_with_passkey_button.dart b/packages/deriv_passkeys/lib/src/presentation/widgets/continue_with_passkey_button.dart index ff9a58655..9bbfa6ca0 100644 --- a/packages/deriv_passkeys/lib/src/presentation/widgets/continue_with_passkey_button.dart +++ b/packages/deriv_passkeys/lib/src/presentation/widgets/continue_with_passkey_button.dart @@ -11,9 +11,13 @@ import 'package:flutter_bloc/flutter_bloc.dart'; class ContinueWithPasskeyButton extends StatelessWidget { /// constructs a [ContinueWithPasskeyButton] const ContinueWithPasskeyButton({ + required this.onTap, super.key, }); + /// Function to handle on tap event. + final VoidCallback onTap; + @override Widget build(BuildContext context) => BlocConsumer( @@ -71,6 +75,7 @@ class ContinueWithPasskeyButton extends StatelessWidget { ), ), onTap: () async { + onTap(); context .read() .add(DerivPasskeysVerifyCredentialEvent()); diff --git a/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_created_call_to_action.dart b/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_created_call_to_action.dart index 50591285c..919aaaaa5 100644 --- a/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_created_call_to_action.dart +++ b/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_created_call_to_action.dart @@ -1,4 +1,4 @@ -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_theme/deriv_theme.dart'; import 'package:deriv_ui/deriv_ui.dart'; import 'package:flutter/material.dart'; @@ -21,28 +21,25 @@ class PasskeysCreatedCallToAction extends StatelessWidget { @override Widget build(BuildContext context) => Padding( padding: const EdgeInsets.all(16), - child: Row( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Expanded( - child: SecondaryButton( - onPressed: () => addMorePasskeysNavigationCallback(context), - child: Text( - context.derivPasskeysLocalizations.addMorePasskeysButtonText, - style: TextStyle( - color: context.theme.colors.prominent, - ), + SecondaryButton( + onPressed: () => addMorePasskeysNavigationCallback(context), + child: Text( + context.derivPasskeysLocalizations.addMorePasskeysButtonText, + style: TextStyle( + color: context.theme.colors.prominent, ), ), ), const SizedBox(width: 16), - Expanded( - child: PrimaryButton( - onPressed: () => continueTradingNavigationCallback(context), - child: Text( - context.derivPasskeysLocalizations.continueTradingButtonText, - style: TextStyle( - color: context.theme.colors.prominent, - ), + PrimaryButton( + onPressed: () => continueTradingNavigationCallback(context), + child: Text( + context.derivPasskeysLocalizations.continueTradingButtonText, + style: TextStyle( + color: context.theme.colors.prominent, ), ), ), diff --git a/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_widget.dart b/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_widget.dart index f6d1a7340..5b7f954f5 100644 --- a/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_widget.dart +++ b/packages/deriv_passkeys/lib/src/presentation/widgets/passkey_widget.dart @@ -1,5 +1,5 @@ import 'package:deriv_passkeys/src/domain/entities/deriv_passkey_entity.dart'; -import 'package:deriv_passkeys/src/extensions/context_extensions.dart'; +import 'package:deriv_passkeys/src/core/extensions/context_extensions.dart'; import 'package:deriv_passkeys/src/presentation/constants/assets.dart'; import 'package:deriv_passkeys/src/presentation/utils/date_time_utils.dart'; import 'package:deriv_theme/deriv_theme.dart'; diff --git a/packages/deriv_passkeys/pubspec.yaml b/packages/deriv_passkeys/pubspec.yaml index a5fe81267..821ab4ac7 100644 --- a/packages/deriv_passkeys/pubspec.yaml +++ b/packages/deriv_passkeys/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_passkeys description: Deriv Passkeys Flutter Plugin -version: 0.0.1+8 +version: 0.0.2+8 publish_to: "none" environment: @@ -10,26 +10,36 @@ dependencies: flutter: sdk: flutter + analytics: + git: + url: git@github.com:regentmarkets/flutter-deriv-packages.git + path: packages/analytics + ref: analytics-v2.1.0 + deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_theme ref: deriv_theme-v2.5.0 + deriv_localizations: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_localizations - ref: deriv_localizations-v1.4.4 + ref: deriv_localizations-v1.5.1 + deriv_ui: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.7+6 + ref: deriv_ui-v0.0.7+9 + deriv_http_client: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_http_client ref: deriv_http_client-v2.0.1 + flutter_deriv_api: git: url: git@github.com:deriv-com/flutter-deriv-api.git diff --git a/packages/deriv_passkeys/test/core/constants/tracking_event_params_helper_test.dart b/packages/deriv_passkeys/test/core/constants/tracking_event_params_helper_test.dart new file mode 100644 index 000000000..183505ba9 --- /dev/null +++ b/packages/deriv_passkeys/test/core/constants/tracking_event_params_helper_test.dart @@ -0,0 +1,277 @@ +import 'package:deriv_passkeys/src/core/constants/analytics_actions_enums.dart'; +import 'package:deriv_passkeys/src/core/constants/tracking_event_params_helper.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('getTrackingParams tests:\n\t', () { + group('efforless login flow -> ', () { + test('should return correct params for openEffortlessLoginPage', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getEffortlessLoginTrackingParams( + EffortlessPageActions.openEffortlessLoginPage, + ); + + expect(result, { + 'event_name': 'open', + 'params': { + 'form_name': 'ce_passkey_effortless_form_', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for closeEffortlessLoginPage', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getEffortlessLoginTrackingParams( + EffortlessPageActions.closeEffortlessLoginPage, + ); + + expect(result, { + 'event_name': 'close', + 'params': { + 'form_name': 'ce_passkey_effortless_form_', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for maybeLater', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getEffortlessLoginTrackingParams( + EffortlessPageActions.maybeLater, + ); + + expect(result, { + 'event_name': 'maybe_later', + 'params': { + 'form_name': 'ce_passkey_effortless_form_', + 'operating_system': 'IOS', + }, + }); + }); + }); + + group('tracking learn more page actions ->', () { + test('should return correct params for openLearnMorePage', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getLearnMoreTrackingParams( + LearnMorePageActions.openLearnMorePage, + 'main_form_name', + ); + + expect(result, { + 'event_name': 'info_open', + 'params': { + 'form_name': 'main_form_name', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for closeLearnMorePage', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getLearnMoreTrackingParams( + LearnMorePageActions.closeLearnMorePage, + 'main_form_name', + ); + + expect(result, { + 'event_name': 'info_back', + 'params': { + 'form_name': 'main_form_name', + 'operating_system': 'IOS', + }, + }); + }); + }); + + group('tracking manage passkeys page actions ->', () { + test('should return correct params for openManagePasskeysPage', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getManagePasskeysTrackingParams( + ManagePasskeysPageActions.openManagePasskeysPage, + ); + + expect(result, { + 'event_name': 'open', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for closeManagePasskeysPage', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getManagePasskeysTrackingParams( + ManagePasskeysPageActions.closeManagePasskeysPage, + ); + + expect(result, { + 'event_name': 'close', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': 'IOS', + }, + }); + }); + }); + + group('tracking create passkey actions ->', () { + test('should return correct params for createPasskey', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.createPasskey, + mainFormName: 'main_form_name', + subFormName: 'sub_form_name', + ); + + expect(result, { + 'event_name': 'create_passkey_started', + 'params': { + 'form_name': 'main_form_name', + 'subform_name': 'sub_form_name', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for createPasskeySuccess', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.createPasskeySuccess, + mainFormName: 'main_form_name', + subFormName: 'sub_form_name', + ); + + expect(result, { + 'event_name': 'create_passkey_finished', + 'params': { + 'form_name': 'main_form_name', + 'subform_name': 'sub_form_name', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for error', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.error, + mainFormName: 'main_form_name', + subFormName: 'sub_form_name', + errorMessage: 'error_message', + ); + + expect(result, { + 'event_name': 'error', + 'params': { + 'form_name': 'main_form_name', + 'subform_name': 'sub_form_name', + 'error_message': 'error_message', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for continueTrading', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.continueTrading, + mainFormName: 'main_form_name', + subFormName: 'sub_form_name', + ); + + expect(result, { + 'event_name': 'create_passkey_continue_trading', + 'params': { + 'form_name': 'main_form_name', + 'subform_name': 'sub_form_name', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for addMorePasskeys', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getCreatePasskeyTrackingParams( + CreatePasskeyFlowActions.addMorePasskeys, + mainFormName: 'main_form_name', + subFormName: 'sub_form_name', + ); + + expect(result, { + 'event_name': 'create_passkey_add_more_passkeys', + 'params': { + 'form_name': 'main_form_name', + 'subform_name': 'sub_form_name', + 'operating_system': 'IOS', + }, + }); + }); + }); + + group('tracking rename passkey page actions ->', () { + test('should return correct params for renamePasskey', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getRenamePasskeyTrackingParams( + RenamePasskeyFlowActions.renamePasskey, + ); + + expect(result, { + 'event_name': 'passkey_rename_open', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for cancelRenamePasskey', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getRenamePasskeyTrackingParams( + RenamePasskeyFlowActions.cancelRenamePasskey, + ); + + expect(result, { + 'event_name': 'passkey_rename_back', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': 'IOS', + }, + }); + }); + + test('should return correct params for renamePasskeySuccess', () { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + final Map result = getRenamePasskeyTrackingParams( + RenamePasskeyFlowActions.renamePasskeySuccess, + ); + + expect(result, { + 'event_name': 'passkey_rename_success', + 'params': { + 'form_name': 'ce_passkey_account_settings_form_', + 'operating_system': 'IOS', + }, + }); + }); + }); + }); +} diff --git a/packages/deriv_passkeys/test/data/deriv_passkeys_data_source_mock_setup.dart b/packages/deriv_passkeys/test/data/deriv_passkeys_data_source_mock_setup.dart index 221657dae..4fa40b0d0 100644 --- a/packages/deriv_passkeys/test/data/deriv_passkeys_data_source_mock_setup.dart +++ b/packages/deriv_passkeys/test/data/deriv_passkeys_data_source_mock_setup.dart @@ -99,7 +99,8 @@ const DerivPasskeysVerifyCredentialsResponseModel { 'token': 'token', }, - ] + ], + 'refresh_token': 'refresh_token', }, ); diff --git a/packages/deriv_passkeys/test/data/deriv_passkeys_mapper_test.dart b/packages/deriv_passkeys/test/data/deriv_passkeys_mapper_test.dart index 76968b9ea..8d3684c2b 100644 --- a/packages/deriv_passkeys/test/data/deriv_passkeys_mapper_test.dart +++ b/packages/deriv_passkeys/test/data/deriv_passkeys_mapper_test.dart @@ -106,7 +106,8 @@ void main() { { 'token': '', } - ] + ], + 'refresh_token': 'refresh_token', }, ); diff --git a/packages/deriv_passkeys/test/data/passkey_analytics_repository_test.dart b/packages/deriv_passkeys/test/data/passkey_analytics_repository_test.dart new file mode 100644 index 000000000..3ea8f51fc --- /dev/null +++ b/packages/deriv_passkeys/test/data/passkey_analytics_repository_test.dart @@ -0,0 +1,168 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_passkeys/src/data/repositories/passkey_analytics_repository.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +final class MockDerivRudderstack extends Mock implements DerivRudderstack {} + +void main() { + group('AnalyticsRepository test.', () { + /// [AnalyticsRepository] instance. + late final AnalyticsRepository analyticsRepository; + + /// [MockDerivRudderstack] instance. + late final MockDerivRudderstack mockDerivRudderstack; + + setUpAll(() { + mockDerivRudderstack = MockDerivRudderstack(); + + when(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).thenAnswer( + (_) => Future.value(true), + ); + + AnalyticsRepository.init( + 'test', + derivRudderstack: mockDerivRudderstack, + ); + + analyticsRepository = AnalyticsRepository.instance; + }); + + test('should track opening effortlessLoginPage.', () { + analyticsRepository.trackOpenEffortlessLoginPage(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track closing effortlessLoginPage.', () { + analyticsRepository.trackCloseEffortlessLoginPage(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track maybe later is pressed.', () { + analyticsRepository.trackMaybeLater(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track opening learn more page.', () { + analyticsRepository.trackOpenLearnMorePage(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track closing learn more page.', () { + analyticsRepository.trackCloseLearnMorePage(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track opening manage passkeys page.', () { + analyticsRepository.trackOpenManagePasskeysPage(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track closing manage passkeys page.', () { + analyticsRepository.trackCloseManagePasskeysPage(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track create passkey.', () { + analyticsRepository.trackCreatePasskey(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + test('should track passkey error.', () { + analyticsRepository.trackPasskeyError('error message'); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track continue trading.', () { + analyticsRepository.trackContinueTrading(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track add more passkeys.', () { + analyticsRepository.trackAddMorePasskeys(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track create passkey success.', () { + analyticsRepository.trackCreatePasskeySuccess(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track rename passkey.', () { + analyticsRepository.trackRenamePasskey(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track cancel rename passkey.', () { + analyticsRepository.trackCancelRenamePasskey(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + + test('should track rename passkey success.', () { + analyticsRepository.trackRenamePasskeySuccess(); + + verify(() => mockDerivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).called(1); + }); + }); +} diff --git a/packages/deriv_passkeys/test/presentation/mixins/passkey_event_tracking_mixin_test.dart b/packages/deriv_passkeys/test/presentation/mixins/passkey_event_tracking_mixin_test.dart new file mode 100644 index 000000000..c04e8aa07 --- /dev/null +++ b/packages/deriv_passkeys/test/presentation/mixins/passkey_event_tracking_mixin_test.dart @@ -0,0 +1,146 @@ +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; +import 'package:deriv_passkeys/src/data/repositories/passkey_analytics_repository.dart'; +import 'package:deriv_passkeys/src/presentation/mixins/passkey_event_tracking_mixin.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +final class MockDerivRudderstack extends Mock implements DerivRudderstack {} + +// class AnalyticsMixinObject = Object with AnalyticsMixin; + +class AnalyticsMixinObject with PasskeyEventTrackingMixin {} + +void main() { + group('Analytics mixin tests:\n', () { + late final MockDerivRudderstack mockRudderStack; + late final AnalyticsMixinObject analyticsMixin; + + setUpAll(() { + analyticsMixin = AnalyticsMixinObject(); + mockRudderStack = MockDerivRudderstack(); + + AnalyticsRepository.init( + 'test', + derivRudderstack: mockRudderStack, + ); + when( + () => mockRudderStack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + ), + ).thenAnswer( + (_) => Future.value(true), + ); + }); + + test('Analytics mixin has a AnalyticsRepository parameter.', () { + expect( + analyticsMixin.analyticsRepository, + AnalyticsRepository.instance, + ); + }); + + test('should track opening effortlessLoginPage.', () { + analyticsMixin.trackOpenEffortlessLoginPage(); + + verify(() => AnalyticsRepository.instance.trackOpenEffortlessLoginPage()) + .called(1); + }); + + test('should track closing effortlessLoginPage.', () { + analyticsMixin.trackCloseEffortlessLoginPage(); + + verify(() => AnalyticsRepository.instance.trackCloseEffortlessLoginPage()) + .called(1); + }); + + test('should track maybe later is pressed.', () { + analyticsMixin.trackMaybeLater(); + + verify(() => AnalyticsRepository.instance.trackMaybeLater()).called(1); + }); + + test('should track opening learn more page.', () { + analyticsMixin.trackOpenLearnMorePage(); + + verify(() => AnalyticsRepository.instance.trackOpenLearnMorePage()) + .called(1); + }); + + test('should track closing learn more page.', () { + analyticsMixin.trackCloseLearnMorePage(); + + verify(() => AnalyticsRepository.instance.trackCloseLearnMorePage()) + .called(1); + }); + + test('should track opening manage passkeys page.', () { + analyticsMixin.trackOpenManagePasskeysPage(); + + verify(() => AnalyticsRepository.instance.trackOpenManagePasskeysPage()) + .called(1); + }); + + test('should track closing manage passkeys page.', () { + analyticsMixin.trackCloseManagePasskeysPage(); + + verify(() => AnalyticsRepository.instance.trackCloseManagePasskeysPage()) + .called(1); + }); + + test('should track create passkey.', () { + analyticsMixin.trackCreatePasskey(); + + verify(() => AnalyticsRepository.instance.trackCreatePasskey()).called(1); + }); + + test('should track create passkey success.', () { + analyticsMixin.trackCreatePasskeySuccess(); + + verify(() => AnalyticsRepository.instance.trackCreatePasskeySuccess()) + .called(1); + }); + + test('should track passkey error.', () { + analyticsMixin.trackPasskeyError('error message'); + + verify(() => + AnalyticsRepository.instance.trackPasskeyError('error message')) + .called(1); + }); + + test('should track continue trading.', () { + analyticsMixin.trackContinueTrading(); + + verify(() => AnalyticsRepository.instance.trackContinueTrading()) + .called(1); + }); + + test('should track add more passkeys.', () { + analyticsMixin.trackAddMorePasskeys(); + + verify(() => AnalyticsRepository.instance.trackAddMorePasskeys()) + .called(1); + }); + + test('should track rename passkey.', () { + analyticsMixin.trackRenamePasskey(); + + verify(() => AnalyticsRepository.instance.trackRenamePasskey()).called(1); + }); + + test('should track cancel rename passkey.', () { + analyticsMixin.trackCancelRenamePasskey(); + + verify(() => AnalyticsRepository.instance.trackCancelRenamePasskey()) + .called(1); + }); + + test('should track rename passkey success.', () { + analyticsMixin.trackRenamePasskeySuccess(); + + verify(() => AnalyticsRepository.instance.trackRenamePasskeySuccess()) + .called(1); + }); + }); +} diff --git a/packages/deriv_passkeys/test/presentation/pages/learn_more_passkeys_page_test.dart b/packages/deriv_passkeys/test/presentation/pages/learn_more_passkeys_page_test.dart index adc491d2a..f6abb7bbe 100644 --- a/packages/deriv_passkeys/test/presentation/pages/learn_more_passkeys_page_test.dart +++ b/packages/deriv_passkeys/test/presentation/pages/learn_more_passkeys_page_test.dart @@ -1,7 +1,8 @@ -import 'package:bloc_test/bloc_test.dart'; +import 'package:analytics/sdk/rudderstack/sdk/deriv_rudderstack_sdk.dart'; import 'package:deriv_localizations/l10n/generated/deriv_passkeys/deriv_passkeys_localizations.dart'; import 'package:deriv_localizations/l10n/generated/deriv_passkeys/deriv_passkeys_localizations_en.dart'; import 'package:deriv_passkeys/deriv_passkeys.dart'; +import 'package:deriv_passkeys/src/data/repositories/passkey_analytics_repository.dart'; import 'package:deriv_passkeys/src/presentation/widgets/section_title_and_content.dart'; import 'package:deriv_passkeys/src/presentation/widgets/unordered_list_widget.dart'; import 'package:flutter/material.dart'; @@ -11,9 +12,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:deriv_passkeys/src/presentation/pages/learn_more_passkeys_page.dart'; import 'package:mocktail/mocktail.dart'; -class MockDerivPasskeysBloc - extends MockBloc - implements DerivPasskeysBloc {} +import '../states/bloc/deriv_passkeys_bloc_setup.dart'; + +final class MockDerivRudderstack extends Mock implements DerivRudderstack {} class _TestPage extends StatelessWidget { const _TestPage(); @@ -35,16 +36,23 @@ class _TestPage extends StatelessWidget { } void main() { + late MockDerivRudderstack derivRudderstack; group('LearnMorePasskeysPage', () { - late MockDerivPasskeysBloc derivPasskeysBloc; setUp(() { - derivPasskeysBloc = MockDerivPasskeysBloc(); + setupDerivPasskeysBloc(); + + derivRudderstack = MockDerivRudderstack(); - when(() => derivPasskeysBloc.state).thenReturn( - DerivPasskeysInitializedState(), + when(() => derivRudderstack.track( + eventName: any(named: 'eventName'), + properties: any(named: 'properties'), + )).thenAnswer( + (_) => Future.value(true), ); - when(() => derivPasskeysBloc.isDp2p).thenReturn( - false, + + AnalyticsRepository.init( + 'test', + derivRudderstack: derivRudderstack, ); }); diff --git a/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_bloc_setup.dart b/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_bloc_setup.dart index 6f96991a7..b293053b5 100644 --- a/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_bloc_setup.dart +++ b/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_bloc_setup.dart @@ -3,8 +3,10 @@ import 'package:mocktail/mocktail.dart'; class MockDerivPasskeysService extends Mock implements DerivPasskeysService {} -class MockPasskeysConnectionInfoEntity extends Mock - implements PasskeysConnectionInfoEntity {} +class MockPasskeysConnectionInfoEntity extends PasskeysConnectionInfoEntity { + MockPasskeysConnectionInfoEntity( + {required super.endpoint, required super.appId}); +} late DerivPasskeysBloc derivPasskeysBloc; late MockDerivPasskeysService mockDerivPasskeysService; @@ -12,7 +14,10 @@ late MockPasskeysConnectionInfoEntity mockPasskeysConnectionInfoEntity; void setupDerivPasskeysBloc() { mockDerivPasskeysService = MockDerivPasskeysService(); - mockPasskeysConnectionInfoEntity = MockPasskeysConnectionInfoEntity(); + mockPasskeysConnectionInfoEntity = MockPasskeysConnectionInfoEntity( + appId: 'appId', + endpoint: '', + ); when(() => mockDerivPasskeysService.isSupported()) .thenAnswer((_) async => true); derivPasskeysBloc = DerivPasskeysBloc( @@ -26,7 +31,10 @@ void setupDerivPasskeysBloc() { void setupSuccessDerivPasskeysVerifyCredentialEvent() { const DerivPasskeysVerifyCredentialsResponseEntity mockResponseEntity = - DerivPasskeysVerifyCredentialsResponseEntity(token: 'token'); + DerivPasskeysVerifyCredentialsResponseEntity( + token: 'token', + refreshToken: 'refresh_token', + ); when(() => mockDerivPasskeysService.verifyCredential( jwtToken: any(named: 'jwtToken'), diff --git a/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_state_test.dart b/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_state_test.dart index cba709665..785c6a623 100644 --- a/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_state_test.dart +++ b/packages/deriv_passkeys/test/presentation/states/bloc/deriv_passkeys_state_test.dart @@ -34,8 +34,11 @@ void main() { group('DerivPasskeysCredentialVerifiedState', () { test('props should contain token', () { const String token = 'example_token'; - const DerivPasskeysState state = - DerivPasskeysCredentialVerifiedState(token: token); + const String refreshToken = 'example_refresh_token'; + const DerivPasskeysState state = DerivPasskeysCredentialVerifiedState( + token: token, + refreshToken: refreshToken, + ); expect(state.props, contains(token)); }); }); diff --git a/packages/deriv_passkeys/test/presentation/widgets/continue_with_passkey_button_test.dart b/packages/deriv_passkeys/test/presentation/widgets/continue_with_passkey_button_test.dart index 70c973c68..f3f580ed6 100644 --- a/packages/deriv_passkeys/test/presentation/widgets/continue_with_passkey_button_test.dart +++ b/packages/deriv_passkeys/test/presentation/widgets/continue_with_passkey_button_test.dart @@ -17,13 +17,15 @@ class _TestPage extends StatelessWidget { const _TestPage(); @override - Widget build(BuildContext context) => const MaterialApp( - localizationsDelegates: >[ + Widget build(BuildContext context) => MaterialApp( + localizationsDelegates: const >[ DerivPasskeysLocalizations.delegate, ], - locale: Locale('en'), + locale: const Locale('en'), home: Scaffold( - body: ContinueWithPasskeyButton(), + body: ContinueWithPasskeyButton( + onTap: () {}, + ), ), ); } diff --git a/packages/deriv_ui/CHANGELOG.md b/packages/deriv_ui/CHANGELOG.md index 1441b306e..84203b2c2 100644 --- a/packages/deriv_ui/CHANGELOG.md +++ b/packages/deriv_ui/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.0.7+9 + + - Update a dependency to the latest release. + +## 0.0.7+8 + + - Update a dependency to the latest release. + +## 0.0.7+7 + + - **REFACTOR**(deriv_ui): [DERG-3500] return inputted amount based on the formatter in numpad ([#624](https://github.com/regentmarkets/flutter-deriv-packages/issues/624)). ([fdf4df19](https://github.com/regentmarkets/flutter-deriv-packages/commit/fdf4df1979ddb8710dba8925ef779cca25d60615)) + ## 0.0.7+6 - **REFACTOR**: update dependencies. ([af89e148](https://github.com/regentmarkets/flutter-deriv-packages/commit/af89e148dfecb6dbc8552b2b883cbad4102a56f8)) diff --git a/packages/deriv_ui/lib/components/numpad/core/exchange_notifier.dart b/packages/deriv_ui/lib/components/numpad/core/exchange_notifier.dart index e823e458b..29decb525 100644 --- a/packages/deriv_ui/lib/components/numpad/core/exchange_notifier.dart +++ b/packages/deriv_ui/lib/components/numpad/core/exchange_notifier.dart @@ -118,9 +118,9 @@ class ExchangeController extends ChangeNotifier { /// what user send by default in primaryCurrency when coming to numpad.) double finalAmount() { if (_exchangeRate.baseCurrency == primaryCurrency.currencyType) { - return primaryCurrency.amount; + return primaryCurrency.formattedAmount; } else { - return secondaryCurrency.amount; + return secondaryCurrency.formattedAmount; } } } diff --git a/packages/deriv_ui/lib/components/numpad/models/currency_detail.dart b/packages/deriv_ui/lib/components/numpad/models/currency_detail.dart index 1cb784034..15f388a0d 100644 --- a/packages/deriv_ui/lib/components/numpad/models/currency_detail.dart +++ b/packages/deriv_ui/lib/components/numpad/models/currency_detail.dart @@ -20,7 +20,7 @@ class CurrencyDetail { ]; /// Available stable coin currency that Deriv supports. - static List stableCurrencies = [ + static List stableCryptoCurrencies = [ 'USDC', 'USDT', 'TUSDT', @@ -37,8 +37,11 @@ class CurrencyDetail { bool get isFiat => fiatCurrencies.contains(currencyType); /// This will check if the currency is stable coin currency(USDC,tUSDT,eUSDT) or not. - bool get isStableCurrency => - stableCurrencies.contains(currencyType.toUpperCase()); + bool get isStableCryptoCurrency => + stableCryptoCurrencies.contains(currencyType.toUpperCase()); + + /// This will give the formatted amount to do processing based on type of currency it is like fiat or crypto currency. + double get formattedAmount => double.parse(formatter.format(amount)); /// Amount that is used to display for user. String get displayAmount { @@ -46,17 +49,17 @@ class CurrencyDetail { return ''; } - return isFiat || isStableCurrency + return isFiat || isStableCryptoCurrency ? amount.toStringAsFixed(2) : amount.toStringAsFixed(8); } /// This will give a specific currency formatter based on what type of currency it is like fiat or crypto currency. NumberFormat get formatter { - if (isFiat || isStableCurrency) { - return NumberFormat.decimalPattern()..maximumFractionDigits = 2; + if (isFiat || isStableCryptoCurrency) { + return NumberFormat('#0.##', 'en_US'); } else { - return NumberFormat.decimalPattern()..maximumFractionDigits = 8; + return NumberFormat('#0.########', 'en_US'); } } diff --git a/packages/deriv_ui/lib/components/numpad/widgets/number_pad.dart b/packages/deriv_ui/lib/components/numpad/widgets/number_pad.dart index 5bbd15020..24472b652 100644 --- a/packages/deriv_ui/lib/components/numpad/widgets/number_pad.dart +++ b/packages/deriv_ui/lib/components/numpad/widgets/number_pad.dart @@ -661,7 +661,7 @@ class _NumpadWithExchange extends NumberPad { NumberPadCloseCallback? onClose, }) : super( numberPadType: NumberPadWidgetType.singleInput, - formatter: NumberFormat.decimalPattern()..maximumFractionDigits = 8, + formatter: primaryCurrency.formatter, label: label, firstInputTitle: title, onClose: onClose, diff --git a/packages/deriv_ui/pubspec.yaml b/packages/deriv_ui/pubspec.yaml index f32c6ff6e..08fa0368b 100644 --- a/packages/deriv_ui/pubspec.yaml +++ b/packages/deriv_ui/pubspec.yaml @@ -1,6 +1,6 @@ name: deriv_ui description: A new Flutter package project. -version: 0.0.7+6 +version: 0.0.7+9 publish_to: none environment: @@ -23,7 +23,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_web_view - ref: deriv_web_view-v0.2.2+1 + ref: deriv_web_view-v0.2.2+3 flutter_svg: ^2.0.7 intl: ^0.18.0 diff --git a/packages/deriv_web_view/CHANGELOG.md b/packages/deriv_web_view/CHANGELOG.md index c90de8c2a..caf42b7ba 100644 --- a/packages/deriv_web_view/CHANGELOG.md +++ b/packages/deriv_web_view/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.2.2+3 + + - **FIX**(deriv_web_view): update deriv web view readme file ([#637](https://github.com/regentmarkets/flutter-deriv-packages/issues/637)). ([b0b66dad](https://github.com/regentmarkets/flutter-deriv-packages/commit/b0b66dadb6cb1b30dc3ad759f2eee031673b6bbf)) + +## 0.2.2+2 + + - **FIX**(deriv_web_view): update deriv web view readme file ([#637](https://github.com/regentmarkets/flutter-deriv-packages/issues/637)). ([b0b66dad](https://github.com/regentmarkets/flutter-deriv-packages/commit/b0b66dadb6cb1b30dc3ad759f2eee031673b6bbf)) + - **FIX**(deriv_webview): add language parameter to needed classes ([#600](https://github.com/regentmarkets/flutter-deriv-packages/issues/600)). ([9b857999](https://github.com/regentmarkets/flutter-deriv-packages/commit/9b857999f479486830671f8b8e8b7fc26dcd769b)) + ## 0.2.2+1 - **FIX**(deriv_web_view): update PTA model ([#557](https://github.com/regentmarkets/flutter-deriv-packages/issues/557)). ([49583f82](https://github.com/regentmarkets/flutter-deriv-packages/commit/49583f823fdac9c3ec3a85529fad9ab9f6784c67)) diff --git a/packages/deriv_web_view/lib/deriv_web_view.dart b/packages/deriv_web_view/lib/deriv_web_view.dart index 8580ac4e7..e8777a60a 100644 --- a/packages/deriv_web_view/lib/deriv_web_view.dart +++ b/packages/deriv_web_view/lib/deriv_web_view.dart @@ -154,6 +154,7 @@ Future openLoggedInWebPage({ required String appToken, required String userAgent, required String platform, + String? languageCode, HttpClientPredicate? getHttpClient, String destinationAppId = '16929', String? action, @@ -179,6 +180,7 @@ Future openLoggedInWebPage({ action: action, code: code, platform: platform, + languageCode: languageCode ); if (oneTimeToken == null) { @@ -219,6 +221,7 @@ Future openLoggedInWebPage({ action: action, code: code, platform: platform, + languageCode: languageCode, ); } @@ -252,6 +255,7 @@ Future _fetchOneTimeToken({ HttpClientPredicate? getHttpClient, String? action, String? code, + String? languageCode, }) async { loadingDialog(context); @@ -267,6 +271,7 @@ Future _fetchOneTimeToken({ code: code, getHttpClient: getHttpClient, platform: platform, + languageCode: languageCode, ); Navigator.of(context, rootNavigator: rootNavigator).pop(); @@ -287,6 +292,7 @@ Future _getOneTimeToken({ HttpClientPredicate? getHttpClient, String? action, String? code, + String? languageCode, }) async { try { final String? token = await performPassThroughAuthentication( @@ -301,6 +307,7 @@ Future _getOneTimeToken({ code: code, getHttpClient: getHttpClient, platform: platform, + languageCode: languageCode, ); return token; @@ -327,6 +334,7 @@ Future _validateCredentials({ HttpClientPredicate? getHttpClient, String? action, String? code, + String? languageCode, }) async { final String? oneTimeToken = await _fetchOneTimeToken( context: context, @@ -343,6 +351,7 @@ Future _validateCredentials({ action: action, code: code, platform: platform, + languageCode: languageCode, ); if (oneTimeToken == null) { diff --git a/packages/deriv_web_view/lib/helper.dart b/packages/deriv_web_view/lib/helper.dart index 37a4ed291..1860b7b61 100644 --- a/packages/deriv_web_view/lib/helper.dart +++ b/packages/deriv_web_view/lib/helper.dart @@ -26,6 +26,7 @@ Future performPassThroughAuthentication({ HttpClientPredicate? getHttpClient, String? action, String? code, + String? languageCode, }) async { final url = getPtaLoginUrl(host: endpoint); @@ -47,6 +48,7 @@ Future performPassThroughAuthentication({ platform: platform, action: action, code: code, + languageCode: languageCode, ), ).toJson(); diff --git a/packages/deriv_web_view/lib/models/pta_login_request_model.dart b/packages/deriv_web_view/lib/models/pta_login_request_model.dart index 4dfee0910..3edce537f 100644 --- a/packages/deriv_web_view/lib/models/pta_login_request_model.dart +++ b/packages/deriv_web_view/lib/models/pta_login_request_model.dart @@ -33,6 +33,7 @@ class UrlParamsModel { required this.platform, this.action, this.code, + this.languageCode, }); /// URL that user should be redirected to for example `/cashier`. @@ -50,6 +51,9 @@ class UrlParamsModel { /// Code. final String? code; + /// language code + final String? languageCode; + /// Converts a instance of this class to json. Map toJson() => { 'redirect_url': redirectUrl, @@ -57,5 +61,6 @@ class UrlParamsModel { if (action != null) 'action': action, if (code != null) 'code': code, 'platform': platform, + if (languageCode != null) 'lang': languageCode, }; } diff --git a/packages/deriv_web_view/pubspec.yaml b/packages/deriv_web_view/pubspec.yaml index b2198fe66..919b4f04a 100644 --- a/packages/deriv_web_view/pubspec.yaml +++ b/packages/deriv_web_view/pubspec.yaml @@ -1,7 +1,7 @@ name: deriv_web_view description: Deriv Web View package project. -version: 0.2.2+1 +version: 0.2.2+3 homepage: https://deriv.com/ publish_to: "none" diff --git a/packages/deriv_widgetbook/CHANGELOG.md b/packages/deriv_widgetbook/CHANGELOG.md index edd0b2854..5163095e0 100644 --- a/packages/deriv_widgetbook/CHANGELOG.md +++ b/packages/deriv_widgetbook/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.0.2+9 + + - Update a dependency to the latest release. + +## 0.0.2+8 + + - Update a dependency to the latest release. + +## 0.0.2+7 + + - Update a dependency to the latest release. + ## 0.0.2+6 - **REFACTOR**: update dependencies. ([af89e148](https://github.com/regentmarkets/flutter-deriv-packages/commit/af89e148dfecb6dbc8552b2b883cbad4102a56f8)) diff --git a/packages/deriv_widgetbook/pubspec.yaml b/packages/deriv_widgetbook/pubspec.yaml index 9ced126dc..79424c1a1 100644 --- a/packages/deriv_widgetbook/pubspec.yaml +++ b/packages/deriv_widgetbook/pubspec.yaml @@ -1,7 +1,7 @@ name: deriv_widgetbook description: Storybook for Deriv UI widgets and components. publish_to: "none" -version: 0.0.2+6 +version: 0.0.2+9 environment: sdk: ">=3.0.0 <4.0.0" @@ -16,7 +16,7 @@ dependencies: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git path: packages/deriv_ui - ref: deriv_ui-v0.0.7+6 + ref: deriv_ui-v0.0.7+9 deriv_theme: git: url: git@github.com:regentmarkets/flutter-deriv-packages.git diff --git a/packages/update_checker/CHANGELOG.md b/packages/update_checker/CHANGELOG.md index b20c8edde..caf0d3f95 100644 --- a/packages/update_checker/CHANGELOG.md +++ b/packages/update_checker/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.4.0 + + - **FEAT**(update_checker): add the ability to change the key from the app side ([#628](https://github.com/regentmarkets/flutter-deriv-packages/issues/628)). ([b18609a0](https://github.com/regentmarkets/flutter-deriv-packages/commit/b18609a00533aaab6d6962eb89f323e2f560df8b)) + +## 1.3.0 + + - **FEAT**(deriv_logger): add ability to print prettified logs in console and UI ([#608](https://github.com/regentmarkets/flutter-deriv-packages/issues/608)). ([5a91c24b](https://github.com/regentmarkets/flutter-deriv-packages/commit/5a91c24bde607ff37940edf18f8dfac67d3fc4fa)) + ## 1.2.2 - **FIX**(update_checker): fix optional update issue in remote config [#475](https://github.com/regentmarkets/flutter-deriv-packages/issues/475). ([ac0dc26b](https://github.com/regentmarkets/flutter-deriv-packages/commit/ac0dc26b46b478248ea81d1dba6b7c6844b88995)) diff --git a/packages/update_checker/lib/src/presentation/update_checker.dart b/packages/update_checker/lib/src/presentation/update_checker.dart index 88eeaa134..9f9514d6c 100644 --- a/packages/update_checker/lib/src/presentation/update_checker.dart +++ b/packages/update_checker/lib/src/presentation/update_checker.dart @@ -1,3 +1,4 @@ +import 'package:firebase_remote_config/firebase_remote_config.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:update_checker/src/repositories/base_firebase.dart'; @@ -43,6 +44,19 @@ class _UpdateCheckerState extends State { @override void initState() { super.initState(); + // Configure fetch settings + FirebaseRemoteConfig.instance.setConfigSettings(RemoteConfigSettings( + fetchTimeout: const Duration(seconds: 10), + // Default fetch timeout + + // As google set a limit of 5 call per hour for each device, + // we set the minimumFetchInterval to 15 minutes + // to avoid Throttling error, + // check: https://firebase.google.com/docs/remote-config/get-started?platform=ios#throttling + minimumFetchInterval: + const Duration(minutes: 15), // Minimum fetch interval set to 0 + )); + _updateBloc = UpdateBloc(firebaseRepository: widget.firebaseRepository); _updateBloc.add(UpdateFetchEvent()); } diff --git a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart index 8fdd42e2d..58bbd165a 100644 --- a/packages/update_checker/lib/src/repositories/firebase_remote_config.dart +++ b/packages/update_checker/lib/src/repositories/firebase_remote_config.dart @@ -5,9 +5,11 @@ import 'package:update_checker/src/repositories/base_firebase.dart'; /// the firebase database. class FirebaseRemoteConfigRepository implements BaseFirebase { /// Initializes the Firebase Database repository - const FirebaseRemoteConfigRepository(); + FirebaseRemoteConfigRepository( + {String versionControlKey = 'app_version_control'}) + : _versionControlKey = versionControlKey; - static const String _versionControlKey = 'app_version_control'; + late final String _versionControlKey; /// Fetches the update information from the database. @override diff --git a/packages/update_checker/pubspec.yaml b/packages/update_checker/pubspec.yaml index 9f0fde39d..5d50c0fc9 100644 --- a/packages/update_checker/pubspec.yaml +++ b/packages/update_checker/pubspec.yaml @@ -1,6 +1,6 @@ name: update_checker description: Check and retrieve update information from the server for the given package. -version: 1.2.2 +version: 1.4.0 homepage: https://deriv.com/ publish_to: "none"