master e6f390555038 cached
239 files
1.3 MB
435.2k tokens
232 symbols
1 requests
Download .txt
Showing preview only (1,410K chars total). Download the full file or copy to clipboard to get everything.
Repository: mysteriumnetwork/mysterium-vpn-desktop
Branch: master
Commit: e6f390555038
Files: 239
Total size: 1.3 MB

Directory structure:
gitextract_p7zwqvqt/

├── .eslintrc
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── stale.yml
│   └── workflows/
│       ├── lint.yml
│       └── release.yml
├── .gitignore
├── .prettierrc
├── .yarnclean
├── LICENSE
├── README.md
├── assets/
│   ├── README.md
│   └── backgrounds/
│       ├── identity-bg.xcf
│       └── terms-bg.xcf
├── build/
│   ├── background.tiff
│   ├── entitlements.mac.plist
│   └── nsis/
│       └── customize.nsi
├── ci/
│   ├── afterPack.js
│   └── notarize.js
├── docs/
│   ├── DEV_GUIDE.md
│   ├── DOCS_CONTRIBUTING
│   ├── myst-supervisor.mmd
│   └── node-tequilapi.mmd
├── monkey-patch-crypto.js
├── package.json
├── sentry-symbols.js
├── sentry.properties
├── src/
│   ├── app/
│   │   ├── .eslintrc
│   │   ├── analytics/
│   │   │   ├── analytics.ts
│   │   │   └── event.ts
│   │   ├── config/
│   │   │   ├── filters.ts
│   │   │   └── store.ts
│   │   ├── connection/
│   │   │   ├── components/
│   │   │   │   ├── ConnectButton/
│   │   │   │   │   └── ConnectButton.tsx
│   │   │   │   └── DisconnectButton/
│   │   │   │       └── DisconnectButton.tsx
│   │   │   ├── status.ts
│   │   │   └── store.ts
│   │   ├── daemon/
│   │   │   ├── components/
│   │   │   │   ├── AppVersion.tsx
│   │   │   │   └── StartupLoadingView/
│   │   │   │       └── StartupLoadingView.tsx
│   │   │   └── store.ts
│   │   ├── feedback/
│   │   │   └── store.ts
│   │   ├── identity/
│   │   │   ├── components/
│   │   │   │   ├── IdentityRegistrationView/
│   │   │   │   │   └── IdentityRegistrationView.tsx
│   │   │   │   └── IdentityUpgradeView/
│   │   │   │       └── IdentityUpgradeView.tsx
│   │   │   ├── identity.ts
│   │   │   └── store.ts
│   │   ├── index.tsx
│   │   ├── location/
│   │   │   ├── components/
│   │   │   │   ├── CurrentIP/
│   │   │   │   │   └── CurrentIP.tsx
│   │   │   │   ├── Flag/
│   │   │   │   │   └── Flag.tsx
│   │   │   │   └── ProtectionStatus/
│   │   │   │       └── ProtectionStatus.tsx
│   │   │   ├── countries.ts
│   │   │   └── states.ts
│   │   ├── navigation/
│   │   │   ├── components/
│   │   │   │   ├── Routes/
│   │   │   │   │   └── Routes.tsx
│   │   │   │   ├── TitleBar/
│   │   │   │   │   ├── NakedTitleBar.tsx
│   │   │   │   │   ├── TitleBar.tsx
│   │   │   │   │   ├── WindowButtonsLinux.tsx
│   │   │   │   │   └── WindowButtonsWindows.tsx
│   │   │   │   ├── ViewContainer/
│   │   │   │   │   └── ViewContainer.tsx
│   │   │   │   ├── ViewContent/
│   │   │   │   │   └── ViewContent.tsx
│   │   │   │   ├── ViewNavBar/
│   │   │   │   │   └── ViewNavBar.tsx
│   │   │   │   ├── ViewSidebar/
│   │   │   │   │   └── ViewSidebar.tsx
│   │   │   │   └── ViewSplit/
│   │   │   │       └── ViewSplit.tsx
│   │   │   ├── locations.ts
│   │   │   └── store.ts
│   │   ├── onboarding/
│   │   │   ├── components/
│   │   │   │   ├── IdentityBackup/
│   │   │   │   │   ├── IdentityBackup.tsx
│   │   │   │   │   └── animation_identity_keys.json
│   │   │   │   ├── IdentitySetup/
│   │   │   │   │   ├── IdentitySetup.tsx
│   │   │   │   │   └── animation_identity.json
│   │   │   │   ├── InitialTopup/
│   │   │   │   │   ├── InitialTopup.tsx
│   │   │   │   │   ├── UseReferralCodePrompt.tsx
│   │   │   │   │   └── animation_onboarding_topup.json
│   │   │   │   ├── IntroductionSteps/
│   │   │   │   │   ├── IntroductionSteps.tsx
│   │   │   │   │   ├── Step1.tsx
│   │   │   │   │   ├── Step2.tsx
│   │   │   │   │   ├── Step3.tsx
│   │   │   │   │   ├── Step4.tsx
│   │   │   │   │   ├── animation_crypto.json
│   │   │   │   │   ├── animation_network.json
│   │   │   │   │   ├── animation_payasyougo.json
│   │   │   │   │   └── animation_privacy.json
│   │   │   │   └── Welcome/
│   │   │   │       └── Welcome.tsx
│   │   │   └── store.ts
│   │   ├── payment/
│   │   │   ├── components/
│   │   │   │   ├── SelectTaxCountry/
│   │   │   │   │   └── SelectTaxCountry.tsx
│   │   │   │   └── SelectTaxState/
│   │   │   │       └── SelectTaxState.tsx
│   │   │   ├── currency.ts
│   │   │   ├── display.ts
│   │   │   ├── methods.ts
│   │   │   ├── rate.ts
│   │   │   └── store.ts
│   │   ├── proposals/
│   │   │   ├── components/
│   │   │   │   ├── CountryFilter/
│   │   │   │   │   └── CountryFilter.tsx
│   │   │   │   ├── Preset/
│   │   │   │   │   └── Preset.tsx
│   │   │   │   ├── ProposalQuality/
│   │   │   │   │   └── ProposalQuality.tsx
│   │   │   │   ├── ProposalTable/
│   │   │   │   │   ├── ProposalTable.tsx
│   │   │   │   │   └── RowRenderer.tsx
│   │   │   │   ├── QualityFilter/
│   │   │   │   │   └── QualityFilter.tsx
│   │   │   │   └── SelectedProposal/
│   │   │   │       └── SelectedProposal.tsx
│   │   │   ├── store.ts
│   │   │   └── uiProposal.ts
│   │   ├── referral/
│   │   │   └── store.ts
│   │   ├── storage/
│   │   │   └── localStorage.ts
│   │   ├── store.ts
│   │   ├── tequilapi/
│   │   │   └── index.ts
│   │   ├── ui-kit/
│   │   │   ├── colors.ts
│   │   │   ├── components/
│   │   │   │   ├── Anchor.tsx
│   │   │   │   ├── Button/
│   │   │   │   │   ├── BrandButton.tsx
│   │   │   │   │   ├── CancelButton.tsx
│   │   │   │   │   ├── GhostButton.tsx
│   │   │   │   │   ├── LightButton.tsx
│   │   │   │   │   ├── OutlineButton.tsx
│   │   │   │   │   ├── RippleButton.tsx
│   │   │   │   │   ├── SecondaryButton.tsx
│   │   │   │   │   └── SidebarButtons.tsx
│   │   │   │   ├── Clipboard/
│   │   │   │   │   └── Clipboard.tsx
│   │   │   │   ├── CryptoAnimation/
│   │   │   │   │   ├── CryptoAnimation.tsx
│   │   │   │   │   ├── animation_btc.json
│   │   │   │   │   ├── animation_dai.json
│   │   │   │   │   ├── animation_doge.json
│   │   │   │   │   ├── animation_eth.json
│   │   │   │   │   ├── animation_ltc.json
│   │   │   │   │   ├── animation_myst.json
│   │   │   │   │   └── animation_usdt.json
│   │   │   │   ├── LogoTitle/
│   │   │   │   │   └── LogoTitle.tsx
│   │   │   │   ├── MysteriumVPN2Toast/
│   │   │   │   │   └── MysteriumVPN2Toast.tsx
│   │   │   │   ├── Prompt/
│   │   │   │   │   └── Prompt.tsx
│   │   │   │   ├── QR/
│   │   │   │   │   └── QR.tsx
│   │   │   │   ├── SectionTitle/
│   │   │   │   │   └── SectionTitle.tsx
│   │   │   │   ├── Spinner/
│   │   │   │   │   ├── Spinner.tsx
│   │   │   │   │   └── animation_spinner.json
│   │   │   │   ├── StepProgressBar/
│   │   │   │   │   └── StepProgressBar.tsx
│   │   │   │   ├── Toggle/
│   │   │   │   │   └── Toggle.tsx
│   │   │   │   └── dismissibleToast.tsx
│   │   │   ├── form-components/
│   │   │   │   ├── Checkbox/
│   │   │   │   │   └── Checkbox.tsx
│   │   │   │   ├── Search.tsx
│   │   │   │   ├── Select.tsx
│   │   │   │   ├── TextArea.tsx
│   │   │   │   └── TextInput.tsx
│   │   │   ├── icons/
│   │   │   │   ├── IconBrowsing.tsx
│   │   │   │   ├── IconCloudDownload.tsx
│   │   │   │   ├── IconCopy.tsx
│   │   │   │   ├── IconDocument.tsx
│   │   │   │   ├── IconDownload.tsx
│   │   │   │   ├── IconDuration.tsx
│   │   │   │   ├── IconGlobe.tsx
│   │   │   │   ├── IconIdentity.tsx
│   │   │   │   ├── IconMedia.tsx
│   │   │   │   ├── IconMusic.tsx
│   │   │   │   ├── IconMystToken.tsx
│   │   │   │   ├── IconNoPreset.tsx
│   │   │   │   ├── IconPaid.tsx
│   │   │   │   ├── IconPerson.tsx
│   │   │   │   ├── IconPlay.tsx
│   │   │   │   ├── IconPriceTier.tsx
│   │   │   │   ├── IconReceived.tsx
│   │   │   │   ├── IconSent.tsx
│   │   │   │   ├── IconSettings.tsx
│   │   │   │   ├── IconWallet.tsx
│   │   │   │   └── Props.tsx
│   │   │   └── typography.ts
│   │   └── views/
│   │       ├── common/
│   │       │   ├── AcceptTerms/
│   │       │   │   └── AcceptTermsView.tsx
│   │       │   ├── Help/
│   │       │   │   ├── HelpContentReportIssue.tsx
│   │       │   │   ├── HelpContentTermsAndConditions.tsx
│   │       │   │   └── HelpView.tsx
│   │       │   ├── Loading/
│   │       │   │   ├── LoadingView.tsx
│   │       │   │   ├── animation_loading_loop.json
│   │       │   │   └── animation_loading_start.json
│   │       │   └── Settings/
│   │       │       ├── ExportIdentityPrompt.tsx
│   │       │       ├── ImportIdentityPrompt.tsx
│   │       │       ├── SettingsConnection.tsx
│   │       │       ├── SettingsFilters.tsx
│   │       │       ├── SettingsMysteriumId.tsx
│   │       │       └── SettingsView.tsx
│   │       └── consumer/
│   │           ├── Connected/
│   │           │   ├── ConnectedView.tsx
│   │           │   ├── ConnectionProposal.tsx
│   │           │   ├── ConnectionStatistics.tsx
│   │           │   ├── animation_connected_loop.json
│   │           │   ├── animation_connecting_loop.json
│   │           │   └── animation_connecting_start.json
│   │           ├── Proposals/
│   │           │   ├── ManualConnectView.tsx
│   │           │   ├── ProposalSearch.tsx
│   │           │   ├── QuickConnectView.tsx
│   │           │   ├── SwitchConnectView.tsx
│   │           │   └── animation_quick_connect.json
│   │           ├── Referral/
│   │           │   └── ReferralView.tsx
│   │           ├── Topup/
│   │           │   ├── TopupChooseMethod.tsx
│   │           │   ├── TopupFailed.tsx
│   │           │   ├── TopupRoutes.tsx
│   │           │   ├── TopupSuccess.tsx
│   │           │   ├── coingate/
│   │           │   │   ├── CoingateOrderSummary.tsx
│   │           │   │   ├── CoingatePaymentOptions.tsx
│   │           │   │   ├── CoingateSelectAmount.tsx
│   │           │   │   ├── CoingateWaitingForPayment.tsx
│   │           │   │   └── LogoCoingate.tsx
│   │           │   ├── common/
│   │           │   │   ├── OptionLabel.tsx
│   │           │   │   ├── OptionValue.tsx
│   │           │   │   └── OrderBreakdown.tsx
│   │           │   ├── myst/
│   │           │   │   ├── MystChooseChain.tsx
│   │           │   │   ├── MystPolygonWaitingForPayment.tsx
│   │           │   │   └── MystSelectAmount.tsx
│   │           │   ├── paypal/
│   │           │   │   ├── LogoPaypal.tsx
│   │           │   │   ├── PaypalOrderSummary.tsx
│   │           │   │   ├── PaypalPaymentOptions.tsx
│   │           │   │   ├── PaypalSelectAmount.tsx
│   │           │   │   └── PaypalWaitingForPayment.tsx
│   │           │   └── stripe/
│   │           │       ├── LogoStripe.tsx
│   │           │       ├── StripeOrderSummary.tsx
│   │           │       ├── StripePaymentOptions.tsx
│   │           │       ├── StripeSelectAmount.tsx
│   │           │       └── StripeWaitingForPayment.tsx
│   │           └── Wallet/
│   │               └── WalletView.tsx
│   ├── config.ts
│   ├── main/
│   │   ├── .eslintrc
│   │   ├── cliFlags.tsx
│   │   ├── index.tsx
│   │   ├── menu.ts
│   │   ├── node/
│   │   │   ├── mysteriumNode.ts
│   │   │   ├── supervisor.ts
│   │   │   └── tequila.ts
│   │   └── tray.ts
│   ├── shared/
│   │   ├── errors/
│   │   │   ├── parseError.ts
│   │   │   └── sentry.ts
│   │   ├── ipc.ts
│   │   ├── log/
│   │   │   └── log.ts
│   │   ├── node/
│   │   │   ├── mysteriumNodeIPC.ts
│   │   │   └── supervisorIPC.ts
│   │   └── push/
│   │       └── topics.ts
│   ├── typings/
│   │   ├── assets.d.ts
│   │   ├── libraries.d.ts
│   │   └── react-table-config.d.ts
│   └── utils/
│       ├── env.ts
│       ├── handleProcessExit.ts
│       ├── paths.ts
│       ├── spawn.ts
│       ├── sudo.ts
│       └── user.ts
├── static/
│   ├── logo.icns
│   ├── sudo-askpass.osascript.js
│   └── support.html
├── tsconfig.json
├── webpack.main.additions.js
└── webpack.renderer.additions.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintrc
================================================
{
  "parser": "@typescript-eslint/parser",
  "extends": [
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier",
    "plugin:prettier/recommended"
  ],
  "parserOptions": {
    "ecmaVersion": 2018,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "plugins": [
    "header",
    "import"
  ],
  "ignorePatterns": ["static/*.js"],
  "rules": {
    "react/prop-types": "off",
    "header/header": [2, "block", [
      "*",
      {"pattern": " * Copyright \\(c\\) \\d{4} BlockDev AG", "template": " * Copyright (c) 2022 BlockDev AG"},
      " *",
      " * This source code is licensed under the MIT license found in the",
      " * LICENSE file in the root directory of this source tree.",
      " "
    ]],
    "import/order": ["error", {
      "newlines-between": "always"
    }]
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}


================================================
FILE: .gitattributes
================================================
* text=auto eol=lf

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Submit a bug report to help us improve
title: "[bug]"
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Logs**
Attach logs (app, node, supervisor) to the issue. Where to find the logs: https://github.com/mysteriumnetwork/mysterium-vpn-desktop#logs

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. Windows 10]
 - App Version [e.g. 4.1.0]

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: "[feature request]"
labels: enhancement
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 90
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
  - enhancement
  - security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  recent activity. It will be closed if no further activity occurs. Is this issue still relevant?
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false



================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint

on: [pull_request, push]

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 18
      - run: yarn install --frozen-lockfile
      - run: yarn lint


================================================
FILE: .github/workflows/release.yml
================================================
name: Release

on:
  push:
    # If the commit is tagged with a version (e.g. "1.0.0") release the app after building
    tags: ["*"]

jobs:
  release:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [macos-latest, windows-latest, ubuntu-latest]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 18
      - run: yarn install --frozen-lockfile
      - uses: battila7/get-version-action@v2
        id: get_version
      - name: Replace version
        run: yarn version --no-git-tag-version --new-version ${{ steps.get_version.outputs.version }}
      - name: Install rpmbuild
        run: sudo apt install -y rpm
        # Only install rpmbuild on Ubuntu
        if: startsWith(matrix.os, 'ubuntu')
      - name: Bundle & release
        uses: samuelmeuli/action-electron-builder@v1
        with:
          build_script_name: "build"
          release: true
          github_token: ${{ secrets.github_token }}
          # macOS code signing certificate
          mac_certs: ${{ secrets.mac_certs }}
          mac_certs_password: ${{ secrets.mac_certs_password }}
          # Windows code signing certificate
#          windows_certs: ${{ secrets.windows_certs }}
#          windows_certs_password: ${{ secrets.windows_certs_password }}
        env:
          APPLEID: ${{ secrets.APPLEID }}
          APPLEIDPASS: ${{ secrets.APPLEIDPASS }}
          APPLETEAMID: ${{ secrets.APPLETEAMID }}


================================================
FILE: .gitignore
================================================
node_modules
dist/
static/bin/
*.log
.idea
.electron-symbols


================================================
FILE: .prettierrc
================================================
{
  "semi": false,
  "trailingComma": "all",
  "singleQuote": false,
  "printWidth": 120,
  "tabWidth": 4
}


================================================
FILE: .yarnclean
================================================
@types/react-native



================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 BlockDev AG

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# mysterium-vpn-desktop

[![GitHub release (latest by date)](https://img.shields.io/github/v/release/mysteriumnetwork/mysterium-vpn-desktop)](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases/latest)
[![Downloads](https://img.shields.io/github/downloads/mysteriumnetwork/mysterium-vpn-desktop/total.svg)](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases)
[![Lint](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/workflows/Lint/badge.svg?event=push)](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/actions?query=workflow%3ALint)

## ⚠️ MysteriumVPN 2.0 for Desktop is available. https://www.mysteriumvpn.com

Mysterium VPN is a Desktop VPN client for Windows, macOS and Linux.

It is the first Mysterium Network use case in action. Our dVPN is our flagship product and showcases the potential of our residential IP network. [Learn more](https://docs.mysterium.network/)

## Usage

Download and install the [latest version](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases/latest) for your platform. After installation, run MysteriumVPN to get started.

### Linux

#### Ubuntu/Debian

- Download the `.deb` package from [releases](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases/latest)
- Install app with dependencies: 

```sh
sudo apt install ./package-name.deb
```

#### CentOS/Fedora/RHEL 

- Download the `.rpm` package from [releases](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases/latest)
- Install app with dependencies: 

```sh
sudo dnf install package-name.rpm
```

### macOS

#### Manual Install

- Download the `.dmg` package from [releases](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases/latest)
- Open the package and drag `MysteriumVPN.app` onto the `Applications` shortcut

#### Homebrew

- Mysterium VPN can also be installed with [Homebrew](https://brew.sh/):

```sh
brew install --cask mysteriumvpn
```

- Update

```sh
brew upgrade --cask mysteriumvpn
```

### Windows

#### Manual Install

- Download the `.exe` file from [releases](https://github.com/mysteriumnetwork/mysterium-vpn-desktop/releases/latest)
- Run the executable to install

#### Chocolatey

- Mysterium VPN can also be installed with [chocolatey](https://chocolatey.org/):

```pwsh
choco install -y mysteriumvpn
```

- Update

```pwsh
choco update -y mysteriumvpn
```

## Logs

Logs help to debug issues when something goes wrong. Make sure to attach all of them when submitting a bug report.

### Windows

- `%USERPROFILE%\AppData\Roaming\MysteriumVPN\logs` (app)
- `%USERPROFILE%\.mysterium\logs\mysterium-node.log` (node)
- `%PROGRAMDATA%\MystSupervisor\myst_supervisor.log` (supervisor)

### macOS

- `~/Library/Logs/MysteriumVPN` (app)
- `~/.mysterium/logs/mysterium-node.log` (node)
- `/var/log/myst_supervisor.log` (supervisor)

### Linux

- `~/.config/MysteriumVPN/logs` (app)
- `~/.mysterium/logs/mysterium-node.log` (node)
- `/var/log/myst_supervisor.log` (supervisor)

** Note: In development mode, application logs are printed to the console

## Development

Pre-requisites:
- Node >=16 LTS
- yarn

1. Install and build the project
    ```
    yarn && yarn build
    ```
2. Start (webpack dev server with hot reload):

    ```
    yarn dev
    ```

## Packaging for distribution

Required env variables (macOS):
- APPLEID
- APPLEIDPASS (generate an app-specific password for this)
- APPLETEAMID

```
yarn bundle
```

## Development guide

[./docs/DEV_GUIDE.md](./docs/DEV_GUIDE.md)

### Upgrading electron version

When upgrading, upload debug symbols to sentry:
```
node sentry-symbols.js
```
https://docs.sentry.io/platforms/javascript/electron/#uploading-debug-information


================================================
FILE: assets/README.md
================================================
A directory for original assets used throughout the application.
Not to be used directly!


================================================
FILE: build/entitlements.mac.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>com.apple.security.cs.allow-jit</key>
		<true/>
		<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
		<true/>
		<key>com.apple.security.cs.allow-dyld-environment-variables</key>
		<true/>
	</dict>
</plist>


================================================
FILE: build/nsis/customize.nsi
================================================
RequestExecutionLevel admin

!macro customInstall
  File "/oname=$INSTDIR\resources\app.asar.unpacked\node_modules\@mysteriumnetwork\node\bin\win\x64\wintun.dll" "${BUILD_RESOURCES_DIR}\nsis\wintun.dll"

  DetailPrint "Installing Supervisor service..."
  nsExec::ExecToStack '"$INSTDIR\resources\app.asar.unpacked\node_modules\@mysteriumnetwork\node\bin\win\x64\myst_supervisor.exe" --install --uid "0"'
  Pop $0
  Pop $1
  ${ifNot} $0 == 0
    MessageBox MB_OK `Supervisor service install failed (error $0).$\r$\n$\r$\n$1`
  ${endif}
!macroend

!macro customRemoveFiles

  DetailPrint "Uninstalling supervisor service..."
  nsExec::Exec '"$INSTDIR\resources\app.asar.unpacked\node_modules\@mysteriumnetwork\node\bin\win\x64\myst_supervisor.exe" --uninstall'
  RMDir /r $INSTDIR
!macroend


================================================
FILE: ci/afterPack.js
================================================
/**
 * Copyright (c) 2022 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require("path")

const chmodr = require("chmodr")
const { Arch } = require("electron-builder")

exports.default = async function afterPack(context) {
    const { electronPlatformName, appOutDir, packager, arch } = context
    if (electronPlatformName === "darwin" && arch === Arch.universal) {
        // Some files lose their attributes after app.asar merge (for macOS universal binary),
        // thus we need to set +x for the binaries
        const nodeBinDir = path.join(
            packager.getResourcesDir(appOutDir),
            "app.asar.unpacked",
            "node_modules",
            "@mysteriumnetwork",
            "node",
            "bin",
        )
        chmodr.sync(nodeBinDir, 0o755)
    }
}


================================================
FILE: ci/notarize.js
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { notarize } = require("@electron/notarize")

// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require("../package.json")

exports.default = async function notarizing(context) {
    const { electronPlatformName, appOutDir } = context
    if (electronPlatformName !== "darwin") {
        return
    }
    if (process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
        console.log("CSC_IDENTITY_AUTO_DISCOVERY is set to false, skipping notarization")
        return
    }

    const appName = context.packager.appInfo.productFilename

    return await notarize({
        appBundleId: packageJson.build.appId,
        appPath: `${appOutDir}/${appName}.app`,
        appleId: process.env.APPLEID,
        appleIdPassword: process.env.APPLEIDPASS,
        teamId: process.env.APPLETEAMID,
    })
}


================================================
FILE: docs/DEV_GUIDE.md
================================================
# Developer's guide to Mysterium VPN desktop app

This desktop app is built with:
- [Typescript](https://www.typescriptlang.org/) - Typed superset of JavaScript that compiles to plain JavaScript
- [Electron](https://www.electronjs.org/) - Build cross-platform desktop apps with JavaScript, HTML, and CSS
- [node](https://github.com/mysteriumnetwork/node) - Mysterium Node, hereinafter referred to as Node
- [node/supervisor](https://github.com/mysteriumnetwork/node/tree/master/supervisor) - Supervisor: Background service for installing/running Node
- [mysterium-vpn-js](https://github.com/mysteriumnetwork/mysterium-vpn-js) - JS SDK for communicating with Node
- [mobx-react-lite](https://github.com/mobxjs/mobx-react-lite) - Simple, scalable state management

## Supervisor

Supervisor's job is to start Node when necessary and kill it when its job is done. 

Below is a typical communication sequence on a user system where neither Supervisor nor Node is initially running (clean install).

![Myst supervisor](./myst-supervisor.png)

## Node (Tequilapi)

Desktop app communicates to Node's REST API, _aka_ Tequilapi.  
The easiest way to do that is to use [JS SDK](https://github.com/mysteriumnetwork/mysterium-vpn-js).  
Another possible way is to construct HTTP calls by yourself, consulting [documentation](https://tequilapi.mysterium.network/).  

Below is a typical communication sequence between the App and the Node when establishing a VPN connection.

![Node Tequilapi](./node-tequilapi.png)

## Further steps

Fork the repository and start hacking!


================================================
FILE: docs/DOCS_CONTRIBUTING
================================================
PNGs generated by MermaidJS Live Editor:
https://mermaid-js.github.io/mermaid-live-editor

================================================
FILE: docs/myst-supervisor.mmd
================================================
sequenceDiagram
Note right of Desktop app: Application starts
Desktop app->>Node: Healthcheck
Node-->>Desktop app: Failed
Desktop app->>Supervisor: Run node
Supervisor-->>Desktop app: Failed
Desktop app->>Desktop app: Ask for permissions
Desktop app->>Supervisor: Install supervisor and node
Supervisor-->>Desktop app: Success
Desktop app->>Supervisor: Run node
Supervisor-->>Desktop app: Success
Desktop app->>Node: Healthcheck
Node-->>Desktop app: Success
Note right of Desktop app: Application logic
Note right of Desktop app: Application closes
Desktop app->>Supervisor: Kill node
Supervisor-->>Desktop app: Success
Note right of Desktop app: Application exits


================================================
FILE: docs/node-tequilapi.mmd
================================================
sequenceDiagram
Note right of Desktop app: Application starts
Desktop app->>Node: healthcheck()
Node-->>Desktop app: ok
Desktop app->>Node: identityCurrent()
Node->>Node: Identity registration if necessary
Node-->>Desktop app: ok { id: 0x.... }

Desktop app->>Node: findProposals()
Node-->>Desktop app: ok { proposals: [...] }

Note right of Desktop app: User selects proposal

Desktop app->>Node: connectionCreate()
Node-->>Desktop app: ok

Desktop app->>Node: connectionStatus()
Node-->>Desktop app: ok

Note right of Desktop app: VPN session is active

Desktop app->>Node: connectionCancel()
Node-->>Desktop app: ok


================================================
FILE: monkey-patch-crypto.js
================================================
/**
 * Copyright (c) 2022 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
// eslint-disable-next-line @typescript-eslint/no-var-requires
const crypto = require("crypto")

/**
 * @see {@link https://stackoverflow.com/a/72219174}
 */
let cryptoPatched = false
const monkeyPatchCrypto = () => {
    if (cryptoPatched) return
    cryptoPatched = true

    /**
     * The MD4 algorithm is not available anymore in Node.js 17+ (because of library SSL 3).
     * In that case, silently replace MD4 by the MD5 algorithm.
     */
    try {
        crypto.createHash("md4")
    } catch (e) {
        console.warn('Crypto "MD4" is not supported anymore by this Node.js version')
        const origCreateHash = crypto.createHash
        crypto.createHash = (alg, opts) => {
            return origCreateHash(alg === "md4" ? "md5" : alg, opts)
        }
    }
}

monkeyPatchCrypto()


================================================
FILE: package.json
================================================
{
  "name": "mysterium-vpn-desktop",
  "productName": "MysteriumDark",
  "description": "Desktop VPN client (legacy) for Mysterium Network",
  "version": "0.0.0-snapshot",
  "main": "index.js",
  "author": {
    "name": "Mysterium Network",
    "email": "mysterium-dev@mysterium.network",
    "url": "https://mysterium.network/"
  },
  "license": "MIT",
  "scripts": {
    "dev": "electron-webpack dev",
    "lint": "eslint src",
    "clean": "shx rm -rf dist",
    "build": "electron-webpack",
    "bundle": "yarn build && electron-builder",
    "bundle-dev": "yarn build && CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder"
  },
  "engines": {
    "node": ">=16"
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^1.2.35",
    "@fortawesome/free-brands-svg-icons": "^5.15.3",
    "@fortawesome/free-solid-svg-icons": "^5.15.3",
    "@fortawesome/react-fontawesome": "^0.1.14",
    "@mysteriumnetwork/node": "1.29.2",
    "@mysteriumnetwork/terms": "0.0.50",
    "@sentry/electron": "^4.0.0",
    "@use-it/interval": "^1.0.0",
    "async-retry": "^1.3.3",
    "bignumber.js": "^9.0.2",
    "byte-size": "^8.0.0",
    "electron-log": "^4.4.8",
    "electron-updater": "^5.2.1",
    "history": "^5.3.0",
    "lodash": "^4.17.21",
    "mkdirp": "^1.0.4",
    "mobx": "^6.6.0",
    "mobx-logger": "^0.7.1",
    "mobx-react-lite": "^3.3.0",
    "mysterium-vpn-js": "^28.0.0",
    "node-machine-id": "^1.1.12",
    "open": "^7.0.0",
    "qrcode.react": "^2.0.0",
    "react": "^18.2.0",
    "react-autosuggest": "^10.1.0",
    "react-circle-flags": "^0.0.17",
    "react-countdown": "^2.3.2",
    "react-dom": "^18.2.0",
    "react-hook-form": "^7.31.1",
    "react-hot-toast": "^2.2.0",
    "react-is": "^18.2.0",
    "react-lottie-player": "^1.4.3",
    "react-markdown": "^6.0.2",
    "react-router-dom": "^6.3.0",
    "react-table": "^7.7.0",
    "react-tooltip": "^4.2.21",
    "react-virtualized-auto-sizer": "^1.0.6",
    "react-window": "^1.8.7",
    "semver": "^7.5.2",
    "source-map-support": "^0.5.21",
    "styled-components": "5.3.0",
    "sudo-prompt": "^9.2.1"
  },
  "devDependencies": {
    "@electron/notarize": "^2.2.0",
    "@sentry/cli": "^2.5.2",
    "@types/async-retry": "^1.4.3",
    "@types/electron-devtools-installer": "^2.2.2",
    "@types/lodash": "^4.14.179",
    "@types/node": "^16.0.0",
    "@types/qrcode.react": "^1.0.2",
    "@types/react": "^18.0.14",
    "@types/react-autosuggest": "^10.1.5",
    "@types/react-dom": "^18.0.5",
    "@types/react-table": "^7.7.12",
    "@types/react-virtualized-auto-sizer": "^1.0.1",
    "@types/react-window": "^1.8.5",
    "@types/semver": "^7.3.9",
    "@types/styled-components": "^5.1.24",
    "@types/webpack-env": "^1.16.3",
    "@typescript-eslint/eslint-plugin": "^5.23.0",
    "@typescript-eslint/parser": "^5.23.0",
    "chmodr": "^1.2.0",
    "cross-env": "^7.0.3",
    "electron": "^20.1.2",
    "electron-builder": "^23.3.3",
    "electron-devtools-installer": "^3.2.0",
    "electron-download": "^4.1.1",
    "electron-webpack": "^2.8.2",
    "electron-webpack-ts": "^4.0.1",
    "eslint": "^8.23.1",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-header": "^3.1.1",
    "eslint-plugin-import": "^2.26.0",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-react": "^7.31.8",
    "glob": "^7.2.0",
    "prettier": "^2.7.1",
    "shx": "^0.3.3",
    "typescript": "^4.6.4",
    "url-loader": "^4.1.0",
    "webpack": "^4.46.0"
  },
  "resolutions": {
    "@types/react": "^18.0.14"
  },
  "analyticsUrl": "https://analytics.mysterium.network",
  "intercomAppId": "sjkeehf4",
  "sentryDsn": "https://5c3208e8d6124f2db303a2d12c7f48b8@o136129.ingest.sentry.io/5222592",
  "electronWebpack": {
    "renderer": {
      "sourceDirectory": "src/app",
      "template": "src/app/index.html",
      "webpackConfig": "webpack.renderer.additions.js"
    },
    "main": {
      "webpackConfig": "webpack.main.additions.js"
    }
  },
  "build": {
    "appId": "network.mysterium.mysterium-vpn-desktop",
    "directories": {
      "buildResources": "build",
      "output": "dist"
    },
    "files": [
      "!**/node_modules/@mysteriumnetwork/node/bin/**",
      "**/node_modules/@mysteriumnetwork/node/bin/${os}/${arch}/**"
    ],
    "mac": {
      "target": {
        "target": "default",
        "arch": "universal"
      },
      "singleArchFiles": "**/node_modules/@mysteriumnetwork/node/bin/**",
      "icon": "static/logo.icns",
      "hardenedRuntime": true,
      "entitlements": "build/entitlements.mac.plist",
      "entitlementsInherit": "build/entitlements.mac.plist",
      "category": "public.app-category.productivity"
    },
    "dmg": {
      "background": "build/background.tiff",
      "iconTextSize": 14
    },
    "win": {
      "target": [
        "nsis"
      ],
      "icon": "static/logo.icns"
    },
    "linux": {
      "target": [
        "deb",
        "rpm"
      ],
      "icon": "static/logo.icns",
      "category": "Network"
    },
    "deb": {
      "depends": [
        "resolvconf",
        "libgtk-3-0",
        "libnotify4",
        "libnss3",
        "libxss1",
        "libxtst6",
        "xdg-utils",
        "libatspi2.0-0",
        "libuuid1",
        "libappindicator3-1",
        "libsecret-1-0"
      ]
    },
    "rpm": {
      "depends": [
        "resolvconf",
        "at-spi2-core",
        "gtk3",
        "libXScrnSaver",
        "libXtst",
        "libnotify",
        "libuuid",
        "nss",
        "xdg-utils"
      ]
    },
    "nsis": {
      "oneClick": true,
      "perMachine": true,
      "allowElevation": true,
      "runAfterFinish": true,
      "include": "build/nsis/customize.nsi"
    },
    "afterPack": "ci/afterPack.js",
    "afterSign": "ci/notarize.js",
    "publish": {
      "provider": "github",
      "releaseType": "prerelease",
      "vPrefixedTagName": false
    }
  }
}


================================================
FILE: sentry-symbols.js
================================================
#!/usr/bin/env node
/* eslint-disable */

let SentryCli;
let download;

try {
  SentryCli = require('@sentry/cli');
  download = require('electron-download');
} catch (e) {
  console.error('ERROR: Missing required packages, please run:');
  console.error('npm install --save-dev @sentry/cli electron-download');
  process.exit(1);
}

const VERSION = /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[\da-z-]+(?:\.[\da-z-]+)*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?\b/i;
const SYMBOL_CACHE_FOLDER = '.electron-symbols';
const package = require('./package.json');
const sentryCli = new SentryCli('./sentry.properties');

async function main() {
  let version = getElectronVersion();
  if (!version) {
    console.error('Cannot detect electron version, check package.json');
    return;
  }

  console.log('We are starting to download all possible electron symbols');
  console.log('We need it in order to symbolicate native crashes');
  console.log(
    'This step is only needed once whenever you update your electron version',
  );
  console.log('Just call this script again it should do everything for you.');

  let zipPath = await downloadSymbols({
    version,
    platform: 'darwin',
    arch: 'x64',
    dsym: true,
  });
  await sentryCli.execute(['upload-dif', '-t', 'dsym', zipPath], true);

  zipPath = await downloadSymbols({
    version,
    platform: 'win32',
    arch: 'ia32',
    symbols: true,
  });
  await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);

  zipPath = await downloadSymbols({
    version,
    platform: 'win32',
    arch: 'x64',
    symbols: true,
  });
  await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);

  zipPath = await downloadSymbols({
    version,
    platform: 'linux',
    arch: 'x64',
    symbols: true,
  });
  await sentryCli.execute(['upload-dif', '-t', 'breakpad', zipPath], true);

  console.log('Finished downloading and uploading to Sentry');
  console.log(`Feel free to delete the ${SYMBOL_CACHE_FOLDER}`);
}

function getElectronVersion() {
  if (!package) {
    return false;
  }

  let electronVersion =
    (package.dependencies && package.dependencies.electron) ||
    (package.devDependencies && package.devDependencies.electron);

  if (!electronVersion) {
    return false;
  }

  const matches = VERSION.exec(electronVersion);
  return matches ? matches[0] : false;
}

async function downloadSymbols(options) {
  return new Promise((resolve, reject) => {
    download(
      {
        ...options,
        cache: SYMBOL_CACHE_FOLDER,
      },
      (err, zipPath) => {
        if (err) {
          reject(err);
        } else {
          resolve(zipPath);
        }
      },
    );
  });
}

main().catch(e => console.error(e));


================================================
FILE: sentry.properties
================================================
defaults.url=https://sentry.io/
defaults.org=mysterium-network
defaults.project=mysterium-vpn-desktop
cli.executable=../../.config/yarn/global/node_modules/@sentry/cli/bin/sentry-cli


================================================
FILE: src/app/.eslintrc
================================================
{
  "extends": [
    "../../.eslintrc"
  ],
  "rules": {
    "no-restricted-imports": ["error", {
      "patterns": [
        "main"
      ]
    }]
  }
}


================================================
FILE: src/app/analytics/analytics.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { autorun } from "mobx"
import { ipcRenderer } from "electron"
import retry from "async-retry"

import * as packageJson from "../../../package.json"
import { rootStore } from "../store"
import { MainIpcListenChannels } from "../../shared/ipc"
import { log } from "../../shared/log/log"
import { isDevelopment } from "../../utils/env"

import { Client, Event } from "./event"

interface Request extends Event {
    client?: Client
}

export class Analytics {
    baseUrl: string
    disabled: boolean
    client: Client = {}

    constructor({ baseUrl, disabled }: { baseUrl: string; disabled: boolean }) {
        this.baseUrl = baseUrl
        this.disabled = disabled
    }

    initialize(): void {
        this.client.app_version = packageJson.version
        autorun(() => {
            this.client.os = rootStore.os
        })
        autorun(() => {
            this.client.country = rootStore.connection.originalLocation?.country
        })
        autorun(() => {
            this.client.consumer_id = rootStore.identity.identity?.id
        })
        ipcRenderer.invoke(MainIpcListenChannels.GetMachineId).then((machineId) => {
            this.client.machine_id = machineId
        })
        ipcRenderer.invoke(MainIpcListenChannels.GetOS).then((os) => {
            this.client.os = os
        })
        ipcRenderer.invoke(MainIpcListenChannels.GetOSVersion).then((os_version) => {
            this.client.os_version = os_version
        })
    }

    event = (name: Event["name"], fields?: Omit<Event, "name">): void => {
        log.debug("UserEvent:", name, fields, "Client:", this.client)
        if (this.disabled) {
            return
        }
        const MAX_RETRIES = 10
        const req: Request = {
            name,
            ...fields,
            client: this.client,
        }
        retry(
            async () => {
                return await fetch(`${this.baseUrl}/events`, {
                    mode: "no-cors",
                    method: "POST",
                    body: JSON.stringify(req),
                })
            },
            {
                onRetry: (e, attempt) => log.warn(`Failed to report event (${attempt}/${MAX_RETRIES}): ${e.message}`),
            },
        )
    }
}

export const analytics = new Analytics({
    baseUrl: "https://consumetrics.mysterium.network/api/v1",
    disabled: isDevelopment(),
})


================================================
FILE: src/app/analytics/event.ts
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

export enum EventName {
    startup = "startup",
    connect_attempt = "connect_attempt",
    connect_success = "connect_success",
    connect_cancel = "connect_cancel",
    connect_failure = "connect_failure",
    manual_connect = "manual_connect",
    quick_connect = "quick_connect",
    disconnect_attempt = "disconnect_attempt",
    disconnect_success = "disconnect_success",
    disconnect_failure = "disconnect_failure",
    page_view = "page_view",
    balance_update = "balance_update",
}

export interface Event {
    name: EventName
    duration?: number
    balance?: number
    country?: string
    provider_id?: string
    page_title?: string
}

export interface Client {
    machine_id?: string
    app_version?: string
    os?: string
    os_version?: string
    country?: string
    consumer_id?: string
}


================================================
FILE: src/app/config/filters.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { action, computed, makeObservable, reaction } from "mobx"
import { QualityLevel } from "mysterium-vpn-js"

import { RootStore } from "../store"
import { log } from "../../shared/log/log"

import { ProposalFilters } from "./store"

export class Filters {
    root: RootStore

    constructor(root: RootStore) {
        makeObservable(this, {
            config: computed,
            country: computed,
            presetID: computed,
            setPartial: action,
            initialized: computed,
            defaults: computed,
            reset: action,
        })
        this.root = root
    }

    setupReactions(): void {
        reaction(() => this.root.config.config, this.onConfigChanged)
    }

    onConfigChanged = (): void => {
        const initialized = this.config.quality != null
        if (!initialized) {
            log.info("Config loaded. Filter configuration does not exist, initializing to defaults.")
            this.reset()
        }
    }

    get config(): ProposalFilters {
        return this.root.config.config.desktop?.filters || {}
    }

    setPartial = (filters: ProposalFilters): Promise<void> => {
        return this.root.config.updateDesktopConfigPartial({ filters })
    }

    get country(): string | undefined {
        return this.config.other?.country ?? undefined
    }

    get presetID(): number | undefined | null {
        return this.config.preset?.id
    }

    get initialized(): boolean {
        return this.config.quality?.level != null
    }

    get defaults(): ProposalFilters {
        return {
            quality: {
                level: QualityLevel.MEDIUM,
                "include-failed": false,
            },
        }
    }

    reset = (): Promise<void> => {
        return this.setPartial(this.defaults)
    }
}


================================================
FILE: src/app/config/store.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { action, computed, makeObservable, observable, runInAction, toJS } from "mobx"
import { DNSOption, QualityLevel } from "mysterium-vpn-js"
import * as termsPackageJson from "@mysteriumnetwork/terms/package.json"
import * as _ from "lodash"
import { ipcRenderer } from "electron"

import { RootStore } from "../store"
import { log } from "../../shared/log/log"
import { tequilapi } from "../tequilapi"
import { MainIpcListenChannels } from "../../shared/ipc"

export interface Config {
    desktop: DesktopConfig
    payments?: {
        consumer?: {
            "price-gib-max"?: number
            "price-hour-max"?: number
        }
    }
    "keep-connected-on-fail"?: boolean
}

export interface DesktopConfig {
    "terms-agreed"?: {
        at?: string
        version?: string
    }
    onboarded?: boolean
    dns?: DNSOption
    "nat-compatibility"?: "auto" | "off"
    "quick-connect"?: boolean
    filters?: ProposalFilters
    "vpn2-offered"?: boolean
}

export interface ProposalFilters {
    preset?: {
        id?: number | null
    }
    quality?: {
        "include-failed"?: boolean
        level?: QualityLevel
    }
    other?: {
        country?: string | null
    }
}

export interface PriceCeiling {
    perGibMax: number
}

export class ConfigStore {
    config: Config = { desktop: {} }
    loaded = false

    root: RootStore

    constructor(root: RootStore) {
        this.root = root
        makeObservable(this, {
            config: observable,
            loaded: observable,
            loadConfig: action,
            updateDesktopConfigPartial: action,
            updateNodeConfigPartial: action,
            persistConfig: action,

            currentTermsAgreed: computed,
            agreeToTerms: action,
            dnsOption: computed,
            setDnsOption: action,
            autoNATCompatibility: computed,
            setAutoNATCompatibility: action,
            onboarded: computed,
            setOnboarded: action,
            quickConnect: computed,
            setQuickConnect: action,
            killSwitch: computed,
            setKillSwitch: action,
        })
    }

    loadConfig = async (): Promise<void> => {
        const config = await tequilapi.userConfig()
        runInAction(() => {
            this.config = {
                desktop: {},
                ...config.data,
            }
            log.info("Using config:", JSON.stringify(this.config))
        })
        runInAction(() => {
            this.loaded = true
        })
    }

    updateDesktopConfigPartial = async (desktopConfig: DesktopConfig): Promise<void> => {
        this.config.desktop = _.merge({}, this.config.desktop, desktopConfig)
        return this.persistConfig()
    }

    updateNodeConfigPartial = async (config: Partial<Config>): Promise<void> => {
        this.config = _.merge({}, this.config, config)
        return this.persistConfig()
    }

    // Offload to main
    persistConfig = async (): Promise<void> => {
        ipcRenderer.send(MainIpcListenChannels.SaveUserConfig, toJS(this.config))
    }

    get currentTermsAgreed(): boolean {
        const version = this.config.desktop?.["terms-agreed"]?.version
        const at = this.config.desktop?.["terms-agreed"]?.at
        return !!version && !!at && version == termsPackageJson.version
    }

    agreeToTerms = async (): Promise<void> => {
        await this.updateDesktopConfigPartial({
            "terms-agreed": {
                version: termsPackageJson.version,
                at: new Date().toISOString(),
            },
        })
    }

    get onboarded(): boolean {
        return this.config.desktop?.onboarded ?? false
    }

    setOnboarded = async (): Promise<void> => {
        return this.updateDesktopConfigPartial({ onboarded: true })
    }

    get dnsOption(): DNSOption {
        return this.config.desktop?.dns ?? "provider"
    }

    setDnsOption = async (dns: string): Promise<void> => {
        return this.updateDesktopConfigPartial({ dns })
    }

    get autoNATCompatibility(): boolean {
        return this.config.desktop?.["nat-compatibility"] !== "off"
    }

    setAutoNATCompatibility = async (enabled: boolean): Promise<void> => {
        return this.updateDesktopConfigPartial({ "nat-compatibility": enabled ? "auto" : "off" })
    }

    get quickConnect(): boolean {
        return this.config.desktop?.["quick-connect"] !== false
    }

    setQuickConnect = async (enabled: boolean): Promise<void> => {
        return this.updateDesktopConfigPartial({ "quick-connect": enabled })
    }

    get killSwitch(): boolean {
        return this.config["keep-connected-on-fail"] === true
    }

    setKillSwitch = async (enabled: boolean): Promise<void> => {
        return this.updateNodeConfigPartial({
            "keep-connected-on-fail": enabled,
        })
    }

    get vpn2Offered(): boolean {
        return this.config.desktop?.["vpn2-offered"] === true
    }

    setVpn2Offered = async (): Promise<void> => {
        return this.updateDesktopConfigPartial({ "vpn2-offered": true })
    }
}


================================================
FILE: src/app/connection/components/ConnectButton/ConnectButton.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { ConnectionStatus } from "mysterium-vpn-js"
import React from "react"
import { observer } from "mobx-react-lite"
import toast from "react-hot-toast"

import { useStores } from "../../../store"
import { BrandButton, BrandButtonProps } from "../../../ui-kit/components/Button/BrandButton"
import { CancelButton } from "../../../ui-kit/components/Button/CancelButton"
import { dismissibleToast } from "../../../ui-kit/components/dismissibleToast"

export type ConnectButtonProps = {
    width?: number
    height?: number
}

export const ConnectButton: React.FC<ConnectButtonProps> = observer(function ConnectButton() {
    const { connection } = useStores()
    const text = ((): string => {
        switch (connection.status) {
            case ConnectionStatus.NOT_CONNECTED:
                return "Connect"
            case ConnectionStatus.CONNECTING:
                return "Cancel"
            case ConnectionStatus.CONNECTED:
                return "Disconnect"
            case ConnectionStatus.DISCONNECTING:
                return "Disconnecting"
        }
        return ""
    })()
    const onClick = async (): Promise<void> => {
        if (connection.status === ConnectionStatus.NOT_CONNECTED) {
            try {
                return await connection.connect()
            } catch (reason) {
                toast.error(
                    dismissibleToast(
                        <span>
                            <>
                                <b>Oops! Could not connect 😶</b>
                                <br />
                                {reason}
                            </>
                        </span>,
                    ),
                )
                return
            }
        }
        return await connection.disconnect()
    }
    const isCancel = connection.status !== ConnectionStatus.NOT_CONNECTED
    const buttonProps: BrandButtonProps = {
        disabled: connection.gracePeriod,
        onClick,
    }
    return isCancel ? (
        <CancelButton {...buttonProps}>{text}</CancelButton>
    ) : (
        <BrandButton {...buttonProps}>{text}</BrandButton>
    )
})


================================================
FILE: src/app/connection/components/DisconnectButton/DisconnectButton.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { ConnectionStatus } from "mysterium-vpn-js"
import React, { ButtonHTMLAttributes } from "react"
import { observer } from "mobx-react-lite"

import { useStores } from "../../../store"
import { SecondaryButton } from "../../../ui-kit/components/Button/SecondaryButton"

export const DisconnectButton = observer(function DisconnectButton() {
    const { connection } = useStores()
    const text = ((): string => {
        switch (connection.status) {
            case ConnectionStatus.NOT_CONNECTED:
                return "Connect"
            case ConnectionStatus.CONNECTING:
                return "Cancel"
            case ConnectionStatus.CONNECTED:
            case ConnectionStatus.ON_HOLD:
                return "Disconnect"
            case ConnectionStatus.DISCONNECTING:
                return "Disconnecting"
        }
        return ""
    })()
    const onClick = async (): Promise<void> => {
        return await connection.disconnect()
    }
    const buttonProps: ButtonHTMLAttributes<HTMLButtonElement> = {
        disabled: connection.gracePeriod,
        onClick,
    }
    return <SecondaryButton {...buttonProps}>{text}</SecondaryButton>
})


================================================
FILE: src/app/connection/status.ts
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { ConnectionStatus } from "mysterium-vpn-js"

export const connectionInProgress = (status: ConnectionStatus): boolean => {
    return [
        ConnectionStatus.CONNECTING,
        ConnectionStatus.CONNECTED,
        ConnectionStatus.ON_HOLD,
        ConnectionStatus.DISCONNECTING,
    ].includes(status)
}

export const connectionActive = (status: ConnectionStatus): boolean => {
    return [ConnectionStatus.CONNECTED, ConnectionStatus.ON_HOLD].includes(status)
}


================================================
FILE: src/app/connection/store.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { action, computed, makeObservable, observable, reaction, runInAction } from "mobx"
import { AppState, ConnectionStatistics, ConnectionStatus, Location, SSEEventType } from "mysterium-vpn-js"
import { ipcRenderer } from "electron"
import retry from "async-retry"
import _ from "lodash"

import { RootStore } from "../store"
import { DaemonStatusType } from "../daemon/store"
import { newUIProposal, UIProposal } from "../proposals/uiProposal"
import { MainIpcListenChannels } from "../../shared/ipc"
import { log, logErrorMessage } from "../../shared/log/log"
import { eventBus, tequilapi } from "../tequilapi"
import { parseError } from "../../shared/errors/parseError"
import { analytics } from "../analytics/analytics"
import { EventName } from "../analytics/event"

export class ConnectionStore {
    connectInProgress = false
    gracePeriod = false
    status = ConnectionStatus.NOT_CONNECTED
    userCancelled = false
    statistics?: ConnectionStatistics
    proposal?: UIProposal
    location?: Location
    originalLocation?: Location
    natType?: string

    root: RootStore

    constructor(root: RootStore) {
        makeObservable(this, {
            connectInProgress: observable,
            gracePeriod: observable,
            status: observable,
            userCancelled: observable,
            statistics: observable,
            proposal: observable,
            location: observable,
            originalLocation: observable,
            natType: observable,
            connect: action,
            statusCheck: action,
            disconnect: action,
            resolveOriginalLocation: action,
            resetLocation: action,
            resolveLocation: action,
            currentIp: computed,
            setConnectInProgress: action,
            markUserCancelled: action,
            setGracePeriod: action,
            setStatus: action,
            setProposal: action,
            setLocation: action,
            setOriginalLocation: action,
            setStatistics: action,
        })
        this.root = root
    }

    setupReactions(): void {
        eventBus.on(SSEEventType.AppStateChange, (state: AppState) => {
            if (state.consumer?.connection) {
                this.setStatus(state.consumer.connection.status)
                this.setStatistics(state.consumer.connection.statistics)
            }
            if (state.consumer?.connection?.proposal) {
                this.setProposal(newUIProposal(state.consumer.connection.proposal))
            } else {
                this.setProposal(undefined)
            }
        })
        reaction(
            () => this.root.daemon.status,
            async (status) => {
                if (status == DaemonStatusType.Up) {
                    await this.resolveOriginalLocation()
                }
            },
        )
        reaction(
            () => this.status,
            async (status) => {
                if ([ConnectionStatus.CONNECTING, ConnectionStatus.DISCONNECTING].includes(status)) {
                    this.resetLocation()
                }
                if ([ConnectionStatus.NOT_CONNECTED, ConnectionStatus.CONNECTED].includes(status)) {
                    await this.resolveLocation()
                }
            },
        )
        reaction(
            () => this.status,
            (status) => {
                ipcRenderer.send(MainIpcListenChannels.ConnectionStatus, status)
            },
            { name: "Notify tray with new connection status" },
        )
        reaction(
            () => this.root.connection.status,
            (status) => this.root.navigation.navigateOnConnectionStatus(status),
        )
        reaction(
            () => this.root.daemon.status,
            async (status) => {
                if (status === DaemonStatusType.Up) {
                    await this.resolveNATType()
                }
            },
        )
        reaction(
            () => this.root.config.autoNATCompatibility,
            async () => {
                await this.resolveNATType()
            },
        )
        window.addEventListener("online", async () => {
            log.info("Network connection restored")
            await this.resolveNATType()
        })
        window.addEventListener("offline", async () => {
            log.info("Network connection lost")
            await this.resolveNATType()
        })
    }

    async connect(): Promise<void> {
        analytics.event(EventName.manual_connect, { country: this.root.proposals.active?.country })
        return this._doConnect()
    }

    async quickConnect(): Promise<void> {
        const proposal = _.sample(this.root.proposals.filteredProposals)
        this.root.proposals.setActiveProposal(proposal)
        analytics.event(EventName.quick_connect, { country: proposal?.country })
        return this._doConnect()
    }

    private async _doConnect(): Promise<void> {
        analytics.event(EventName.balance_update, {
            balance: Number(this.root.identity.identity?.balanceTokens?.human),
        })
        const proposal = this.root.proposals.active
        if (!this.root.identity.identity || !proposal) {
            return
        }
        this.setConnectInProgress(true)
        this.setGracePeriod()
        const before = new Date()
        try {
            analytics.event(EventName.connect_attempt, { country: proposal.country, provider_id: proposal.providerId })
            await tequilapi.connectionCreate(
                {
                    consumerId: this.root.identity.identity.id,
                    providerId: proposal.providerId,
                    serviceType: proposal.serviceType,
                    connectOptions: {
                        dns: this.root.config.dnsOption,
                    },
                },
                60_000,
            )
            analytics.event(EventName.connect_success, {
                country: proposal.country,
                provider_id: proposal.providerId,
                duration: new Date().getTime() - before.getTime(),
            })
        } catch (err) {
            if (this.userCancelled) {
                analytics.event(EventName.connect_cancel, {
                    country: proposal.country,
                    provider_id: proposal.providerId,
                    duration: new Date().getTime() - before.getTime(),
                })
                return Promise.resolve()
            }
            analytics.event(EventName.connect_failure, {
                country: proposal.country,
                provider_id: proposal.providerId,
                duration: new Date().getTime() - before.getTime(),
            })
            const msg = parseError(err)
            logErrorMessage("Could not connect", msg)
            return Promise.reject(msg.humanReadable)
        } finally {
            this.setConnectInProgress(false)
        }
    }

    async statusCheck(): Promise<void> {
        try {
            if (this.connectInProgress) {
                return
            }
            const conn = await tequilapi.connectionStatus()
            if (this.connectInProgress) {
                return
            }
            this.setStatus(conn.status)
        } catch (err) {
            const msg = parseError(err)
            logErrorMessage("Could not check connection status", msg)
            this.setStatus(ConnectionStatus.NOT_CONNECTED)
        }
    }

    async disconnect(): Promise<void> {
        this.markUserCancelled()
        analytics.event(EventName.balance_update, {
            balance: Number(this.root.identity.identity?.balanceTokens?.human),
        })
        const from = this.root.connection.location?.country
        this.setGracePeriod()
        const before = new Date()
        try {
            analytics.event(EventName.disconnect_attempt, { country: from })
            await tequilapi.connectionCancel()
            const duration = new Date().getTime() - before.getTime()
            analytics.event(EventName.disconnect_success, { country: from, duration })
        } catch (err) {
            const duration = new Date().getTime() - before.getTime()
            analytics.event(EventName.disconnect_failure, { country: from, duration })
            const msg = parseError(err)
            logErrorMessage("Failed to disconnect", msg)
        }
    }

    async resolveOriginalLocation(): Promise<void> {
        try {
            const location = await tequilapi.location()
            this.setOriginalLocation(location)
        } catch (err) {
            const msg = parseError(err)
            logErrorMessage("Failed to lookup original location", msg)
        }
    }

    resetLocation(): void {
        this.setLocation({
            country: "unknown",
            ip: "Updating...",
            asn: 0,
            city: "",
            continent: "",
            isp: "",
            ipType: "",
        })
    }

    async resolveLocation(): Promise<void> {
        let location: Location = {
            country: "unknown",
            ip: "Updating...",
            asn: 0,
            city: "",
            continent: "",
            isp: "",
            ipType: "",
        }
        const MAX_RETRIES = 5
        await retry(
            async () => {
                location = await tequilapi.connectionLocation()
            },
            {
                retries: MAX_RETRIES,
                onRetry: (e, attempt) =>
                    log.warn(`Failed to update location (${attempt}/${MAX_RETRIES}): ${e.message}`),
            },
        )
        this.setLocation(location)
    }

    async resolveNATType(): Promise<void> {
        if (this.root.config.autoNATCompatibility && this.status !== ConnectionStatus.CONNECTED) {
            log.info("Resolving NAT type...")
            try {
                const natType = await tequilapi.natType()
                runInAction(() => {
                    this.natType = natType.type
                })
                log.info("Resolved NAT type:", natType.type || natType)
            } catch (err) {
                log.error("Could not resolve NAT type:", err)
            }
        }
    }

    get currentIp(): string {
        if (this.status === ConnectionStatus.CONNECTED) {
            return this.location?.ip ?? ""
        }
        return this.originalLocation?.ip ?? ""
    }

    setConnectInProgress = (b: boolean): void => {
        this.connectInProgress = b
    }

    setGracePeriod = (): void => {
        this.gracePeriod = true
        setTimeout(() => {
            runInAction(() => {
                this.gracePeriod = false
            })
        }, 5000)
    }

    markUserCancelled = (): void => {
        runInAction(() => {
            this.userCancelled = true
        })
        setTimeout(() => {
            runInAction(() => {
                this.userCancelled = false
            })
        }, 5000)
    }

    setStatus = (s: ConnectionStatus): void => {
        this.status = s
    }

    setProposal = (p?: UIProposal): void => {
        this.proposal = p
    }

    setLocation = (l: Location): void => {
        this.location = l
    }

    setOriginalLocation = (l: Location): void => {
        this.originalLocation = l
    }

    setStatistics = (s?: ConnectionStatistics): void => {
        this.statistics = s
    }
}


================================================
FILE: src/app/daemon/components/AppVersion.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React from "react"
import styled from "styled-components"
import { nodeVersion } from "@mysteriumnetwork/node"

import * as packageJson from "../../../../package.json"
import { Small } from "../../ui-kit/typography"

const Container = styled(Small)`
    opacity: 0.5;
    text-align: center;
`

export const AppVersion: React.FC<{ className?: string }> = ({ className }) => {
    return (
        <Container className={className}>
            Version: <b>{packageJson.version}</b>
            <br />
            Mysterium Node: <b>{nodeVersion()}</b>
        </Container>
    )
}


================================================
FILE: src/app/daemon/components/StartupLoadingView/StartupLoadingView.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import React from "react"

import { LoadingView } from "../../../views/common/Loading/LoadingView"
import { useStores } from "../../../store"

export const StartupLoadingView: React.FC = observer(function StartupLoadingView() {
    const { daemon } = useStores()
    return <LoadingView status={daemon.startupStatus} />
})


================================================
FILE: src/app/daemon/store.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { action, makeObservable, observable, reaction, when } from "mobx"
import { ipcRenderer } from "electron"

import { RootStore } from "../store"
import { log, logErrorMessage } from "../../shared/log/log"
import { sseConnect, tequilapi } from "../tequilapi"
import { MainIpcListenChannels, WebIpcListenChannels } from "../../shared/ipc"
import { mysteriumNodeIPC } from "../../shared/node/mysteriumNodeIPC"
import { supervisorIPC } from "../../shared/node/supervisorIPC"
import { parseError } from "../../shared/errors/parseError"

export enum DaemonStatusType {
    Up = "UP",
    Down = "DOWN",
}

export enum StartupStatus {
    CheckingForUpdates = "Checking for updates",
    UpdateAvailable = "Update available",
    UpdateNotAvailable = "No update available",
    Downloading = "Downloading update",
    DownloadingComplete = "Download complete. Restart the app to upgrade!",
    KillingGhosts = "Killing ghosts",
    StartingDaemon = "Starting daemon",
}

export class DaemonStore {
    statusLoading = false
    status = DaemonStatusType.Down
    startupStatus = StartupStatus.CheckingForUpdates
    starting = false

    eventSource?: EventSource

    root: RootStore

    constructor(root: RootStore) {
        makeObservable(this, {
            statusLoading: observable,
            status: observable,
            startupStatus: observable,
            starting: observable,
            setStartupStatus: action,
            healthcheck: action,
            update: action,
            start: action,
            supervisorInstall: action,
            setStatus: action,
            setStarting: action,
            setStatusLoading: action,
        })
        this.root = root
        setInterval(async () => {
            await this.healthcheck()
        }, 5_000)
    }

    setupReactions(): void {
        when(
            () => this.startupStatus == StartupStatus.UpdateNotAvailable,
            async () => {
                await this.start()
            },
        )
        reaction(
            () => this.status,
            async (status) => {
                if (status == DaemonStatusType.Up) {
                    this.eventSource = sseConnect()
                }
            },
        )
        this.root.navigation.showLoading()
        this.update()
    }

    setStartupStatus(status: StartupStatus): void {
        this.startupStatus = status
    }

    async healthcheck(): Promise<void> {
        if (this.starting) {
            log.info("Daemon is starting, skipping healthcheck")
            return
        }
        if (this.statusLoading) {
            log.info("Another healthcheck is in progress, skipping")
            return
        }
        this.setStatusLoading(true)
        try {
            await tequilapi.healthCheck(10_000)
            this.setStatus(DaemonStatusType.Up)
        } catch (err) {
            const msg = parseError(err)
            logErrorMessage("Healthcheck failed", msg)
            this.setStatus(DaemonStatusType.Down)
        }
        this.setStatusLoading(false)
    }

    async update(): Promise<void> {
        ipcRenderer.send(MainIpcListenChannels.Update)
        ipcRenderer.on(WebIpcListenChannels.UpdateAvailable, () => {
            log.info("Update available", this.startupStatus)
            if (this.startupStatus == StartupStatus.CheckingForUpdates) {
                this.setStartupStatus(StartupStatus.UpdateAvailable)
            }
        })
        ipcRenderer.on(WebIpcListenChannels.UpdateNotAvailable, () => {
            log.info("Update not available", this.startupStatus)
            if (this.startupStatus == StartupStatus.CheckingForUpdates) {
                this.setStartupStatus(StartupStatus.UpdateNotAvailable)
            }
        })
        ipcRenderer.on(WebIpcListenChannels.UpdateDownloading, () => {
            this.setStartupStatus(StartupStatus.Downloading)
        })
        ipcRenderer.on(WebIpcListenChannels.UpdateDownloadComplete, () => {
            this.setStartupStatus(StartupStatus.DownloadingComplete)
        })
        setTimeout(() => {
            if (this.startupStatus == StartupStatus.CheckingForUpdates) {
                log.info("Update timeout", this.startupStatus)
                this.setStartupStatus(StartupStatus.UpdateNotAvailable)
            }
        }, 5_000)
    }

    async start(): Promise<void> {
        if (this.status == DaemonStatusType.Up) {
            log.info("Mysterium Node is already running")
            return
        }
        log.info("Starting Mysterium Node")
        if (this.starting) {
            log.info("Already starting")
            return
        }
        this.setStarting(true)
        try {
            await supervisorIPC.connect()
        } catch (err) {
            const msg = parseError(err)
            logErrorMessage("Failed to connect to the supervisor, installing", msg)
            await this.supervisorInstall()
        }

        await supervisorIPC.upgrade()
        this.setStartupStatus(StartupStatus.KillingGhosts)
        await mysteriumNodeIPC.killGhosts()
        this.setStartupStatus(StartupStatus.StartingDaemon)
        await mysteriumNodeIPC.start()
        this.setStarting(false)
    }

    async supervisorInstall(): Promise<void> {
        try {
            return await supervisorIPC.install()
        } catch (err) {
            log.error("Failed to install supervisor", err)
        }
    }

    setStatus = (s: DaemonStatusType): void => {
        this.status = s
    }

    setStarting = (s: boolean): void => {
        this.starting = s
    }

    setStatusLoading = (s: boolean): void => {
        this.statusLoading = s
    }
}


================================================
FILE: src/app/feedback/store.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { action, makeObservable, observable } from "mobx"
import { Issue } from "mysterium-vpn-js"

import { RootStore } from "../store"
import { tequilapi } from "../tequilapi"
import { logErrorMessage } from "../../shared/log/log"
import { parseError } from "../../shared/errors/parseError"

export class FeedbackStore {
    root: RootStore

    loading = false

    constructor(root: RootStore) {
        makeObservable(this, {
            loading: observable,
            setLoading: action,
            reportIssue: action,
        })
        this.root = root
    }

    async reportIssue(issue: Issue): Promise<string> {
        this.setLoading(true)
        try {
            const issueId = await tequilapi.reportIssueGithub(issue)
            return issueId.issueId
        } catch (err) {
            const msg = parseError(err)
            logErrorMessage("Could not submit the report", msg)
            return Promise.reject(msg.humanReadable)
        } finally {
            this.setLoading(false)
        }
    }

    setLoading = (b: boolean): void => {
        this.loading = b
    }
}


================================================
FILE: src/app/identity/components/IdentityRegistrationView/IdentityRegistrationView.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import { IdentityRegistrationStatus } from "mysterium-vpn-js"
import React from "react"
import { comparer, reaction, when } from "mobx"

import { LoadingView } from "../../../views/common/Loading/LoadingView"
import { Step, useStores } from "../../../store"
import { log } from "../../../../shared/log/log"

const displayRegistrationStatus = (s?: IdentityRegistrationStatus): string => {
    switch (s) {
        case IdentityRegistrationStatus.Unknown:
            return "Unable to check"
        case IdentityRegistrationStatus.Unregistered:
            return "Waiting for tokens to arrive"
        case IdentityRegistrationStatus.InProgress:
            return "Registering MysteriumID"
        case IdentityRegistrationStatus.Registered:
            return "Registered"
        case IdentityRegistrationStatus.RegistrationError:
            return "Registration Failed"
    }
    return ""
}

export const IdentityRegistrationView: React.FC = observer(function IdentityRegistrationView() {
    const root = useStores()
    const { identity } = root
    const statusDisplay = displayRegistrationStatus(identity.identity?.registrationStatus)
    reaction(
        () => identity.identity?.balanceTokens,
        async (balance, prev) => {
            log.debug(`[event] Balance changed: ${prev?.ether} -> ${balance?.ether}`)
            switch (identity.identity?.registrationStatus) {
                case IdentityRegistrationStatus.Unregistered:
                case IdentityRegistrationStatus.RegistrationError:
                    if (await identity.balanceSufficientToRegister()) {
                        root.startupSequence(Step.IDENTITY_REGISTER)
                    }
            }
        },
        { equals: comparer.structural },
    )
    when(
        () => identity.identity?.registrationStatus === IdentityRegistrationStatus.Registered,
        () => {
            root.startupSequence(Step.IDENTITY_REGISTER_DONE)
        },
    )
    return <LoadingView status={statusDisplay} />
})


================================================
FILE: src/app/identity/components/IdentityUpgradeView/IdentityUpgradeView.tsx
================================================
/**
 * Copyright (c) 2022 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import React, { useEffect } from "react"
import { toast } from "react-hot-toast"

import { LoadingView } from "../../../views/common/Loading/LoadingView"
import { Step, useStores } from "../../../store"

export const IdentityUpgradeView: React.FC = observer(function IdentityUpgradeView() {
    const root = useStores()
    const { identity } = root
    useEffect(() => {
        identity
            .upgrade()
            .then(() => {
                toast.success("ID upgraded! Balance will refresh within 1-3 minutes.")
                return root.startupSequence(Step.IDENTITY_UPGRADE_DONE)
            })
            .catch(() => {
                toast.error("Failed to upgrade ID (restart and try again)")
            })
    }, [])
    return <LoadingView status="Upgrading identity compatibility (<1 minute)..." />
})


================================================
FILE: src/app/identity/identity.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { Identity, IdentityRegistrationStatus } from "mysterium-vpn-js"

export const registered = (id: Identity): boolean =>
    [IdentityRegistrationStatus.Registered].includes(id.registrationStatus)


================================================
FILE: src/app/identity/store.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { AppState, Identity, SSEEventType } from "mysterium-vpn-js"
import { action, computed, makeObservable, observable, reaction, runInAction, toJS } from "mobx"
import retry from "async-retry"
import _ from "lodash"
import BigNumber from "bignumber.js"

import { RootStore, Step } from "../store"
import { eventBus, tequilapi } from "../tequilapi"
import { log } from "../../shared/log/log"
import { ExportIdentityOpts, ImportIdentityOpts, mysteriumNodeIPC } from "../../shared/node/mysteriumNodeIPC"
import { analytics } from "../analytics/analytics"
import { EventName } from "../analytics/event"

export class IdentityStore {
    loading = false
    identity?: Identity
    unlocked = false
    identities: Identity[] = []

    root: RootStore

    constructor(root: RootStore) {
        makeObservable(this, {
            loading: observable,
            identity: observable,
            unlocked: observable,
            identities: observable,
            refreshIdentity: action,
            create: action,
            loadIdentity: action,
            identityExists: computed,
            fetchIdentity: action,
            hasIdentities: action,
            unlock: action,
            balanceSufficientToRegister: action,
            register: action,
            registerWithReferralToken: action,
            upgradeRequired: action,
            upgrade: action,
            setLoading: action,
            setIdentity: action,
            setIdentities: action,
            exportIdentity: action,
            importIdentityChooseFile: action,
            importIdentity: action,
        })
        this.root = root
    }

    setupReactions(): void {
        eventBus.on(SSEEventType.AppStateChange, (state: AppState) => {
            this.setIdentities(state.identities ?? [])
        })
        reaction(
            () => this.identities,
            async (identities) => {
                this.refreshIdentity(identities)
            },
            { name: "Refresh identity from node state" },
        )
        const reportBalanceUpdate = _.debounce((amount: number) => {
            analytics.event(EventName.balance_update, {
                balance: amount,
            })
        }, 60_000)
        reaction(
            () => Number(this.identity?.balanceTokens.human ?? 0),
            (balance) => {
                reportBalanceUpdate(balance)
            },
        )
    }

    async hasIdentities(): Promise<boolean> {
        const ids = await tequilapi.identityList()
        return ids.length > 0
    }

    async loadIdentity(): Promise<void> {
        log.info("Loading identity")
        const identity = await this.fetchIdentity()
        this.setIdentity(identity)
        await this.unlock()
        log.info("Identity loaded:", identity)
    }

    get identityExists(): boolean {
        return this.identity?.id != null
    }

    async fetchIdentity(): Promise<Identity | undefined> {
        const ids = await tequilapi.identityList()
        if (ids.length < 1) {
            return undefined
        }
        const current = await tequilapi.identityCurrent({ passphrase: "" }).catch((reason) => {
            throw Error("Could not get current identity ref: " + reason)
        })
        const MAX_RETRIES = 10
        return await retry(
            async () => {
                return tequilapi.identity(current.id)
            },
            {
                retries: MAX_RETRIES,
                onRetry: (e, attempt) => log.warn(`Failed to get identity (${attempt}/${MAX_RETRIES}): ${e.message}`),
            },
        ).catch((reason) => {
            throw Error("Could not get identity: " + reason)
        })
    }

    refreshIdentity = (identities: Identity[]): void => {
        if (!this.identity) {
            return
        }
        const matchingId = identities.find((id) => id.id == this.identity?.id)

        this.setIdentity(matchingId)
    }

    async create(): Promise<void> {
        try {
            await tequilapi.identityCreate("")
        } catch (err) {
            log.error("Failed to create ID", err)
        }
    }

    async unlock(): Promise<void> {
        if (this.unlocked) {
            return
        }
        if (!this.identity) {
            return
        }
        const i = this.identity.id
        try {
            await tequilapi.identityUnlock(i, "", 10_000)
            runInAction(() => {
                this.unlocked = true
            })
        } catch (err) {
            log.error("Failed to unlock identity", err)
        }
    }

    async balanceSufficientToRegister(): Promise<boolean> {
        const { id, balanceTokens: balance } = this.requireId()

        // Check whether the user is eligible for free registration
        const eligibleForFreeRegistration = await tequilapi.freeRegistrationEligibility(id).then((res) => res.eligible)
        if (eligibleForFreeRegistration) {
            log.info("User is eligible for free registration")
            return true
        }

        // Fetch registration fees and compare with user's balance
        await this.root.payment.fetchTransactorFees()
        const registrationFee = this.root.payment.fees?.registration
        if (new BigNumber(balance.wei).isGreaterThan(new BigNumber(registrationFee?.wei ?? 0))) {
            log.info(`Balance is sufficient to register: ${balance.ether} >= ${registrationFee?.ether} (reg. fee)`)
            return true
        }

        log.info(`Balance is NOT sufficient to register: ${balance.ether} < ${registrationFee?.ether} (reg. fee)`)
        return false
    }

    async register(id: Identity, referralToken?: string): Promise<void> {
        log.info("Registering MysteriumID: ", toJS(id), referralToken ? `token: ${referralToken}` : "")
        await this.root.payment.fetchTransactorFees()
        try {
            await tequilapi.identityRegister(id.id, { stake: 0, referralToken })
        } catch (err) {
            log.error("Failed to register identity", err)
        }
    }

    requireId(): Identity {
        const id = this.identity
        if (!id) {
            throw Error("No Identity")
        }
        return id
    }

    async upgradeRequired(): Promise<boolean> {
        const id = this.requireId().id
        const res = await tequilapi.http.get(`identities/${id}/migrate-hermes/status`)
        const status = res?.status
        if (!status) {
            log.error("Cannot check for migration status")
            return false
        }
        if (status !== "required") {
            log.info("Migration is not required")
            return false
        }
        return true
    }

    async upgrade(): Promise<void> {
        const id = this.requireId().id
        await retry(
            async () => {
                const res = await tequilapi.http.post(`identities/${id}/migrate-hermes`, {}, 60_000)
                log.info("Migrate ID response:", JSON.stringify(res))
            },
            {
                retries: 10,
                factor: 1,
                onRetry: (e, attempt) => log.warn(`Retrying ID upgrade (${attempt}): ${e.message}`),
            },
        )
    }

    async registerWithReferralToken(token: string): Promise<void> {
        if (!this.identity) {
            return
        }
        this.setLoading(true)
        try {
            // eslint-disable-next-line @typescript-eslint/ban-ts-comment
            // @ts-ignore
            await tequilapi.identityRegister(this.identity?.id, { token })
        } finally {
            this.setLoading(false)
        }
    }

    async refreshBalance(): Promise<void> {
        if (!this.identity) {
            return
        }
        await tequilapi.identityBalanceRefresh(this.identity.id)
    }

    setLoading = (b: boolean): void => {
        this.loading = b
    }

    setIdentity = (identity?: Identity): void => {
        this.identity = identity
    }

    setIdentities = (identities: Identity[]): void => {
        this.identities = identities
    }

    async exportIdentity(opts: ExportIdentityOpts): Promise<string> {
        const res = await mysteriumNodeIPC.exportIdentity(opts)
        if (res.error) {
            return Promise.reject(res.error)
        }
        return String(res.result)
    }

    importIdentityChooseFile(): Promise<string> {
        return mysteriumNodeIPC.importIdentityChooseFile()
    }

    async importIdentity(opts: ImportIdentityOpts): Promise<string> {
        const res = await mysteriumNodeIPC.importIdentity(opts)
        if (res.error) {
            return Promise.reject(res.error)
        }
        await this.loadIdentity()
        this.root.startupSequence(Step.IDENTITY_CREATE_DONE)
        return String(res.result)
    }
}


================================================
FILE: src/app/index.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { platform } from "os"

import React from "react"
import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom"
import { createGlobalStyle, keyframes } from "styled-components"
import { Toaster } from "react-hot-toast"
import { createHashHistory } from "history"
import { observe } from "mobx"
import { createRoot } from "react-dom/client"

import { initialize as initializeSentry } from "../shared/errors/sentry"

import { Routes } from "./navigation/components/Routes/Routes"
import { createRootStore, StoreContext } from "./store"
import { brand, brandLight, greyBlue1 } from "./ui-kit/colors"
import { analytics } from "./analytics/analytics"

initializeSentry()

let globalFontStyle
switch (platform()) {
    case "win32":
        globalFontStyle = `
            font-family: "Segoe UI", sans-serif;
            font-weight: normal;
        `
        break
    case "darwin":
        globalFontStyle = `
            font-family: -apple-system, BlinkMacSystemFont, sans-serif;
            font-weight: 400;
        `
        break
    default:
        globalFontStyle = `
            font-family: Ubuntu, sans-serif;
            font-weight: normal;
        `
}

const fadeIn = keyframes`
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
`

const GlobalStyle = createGlobalStyle`
    html, body, #app {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        font-size: 12px;
        ${globalFontStyle}
    }
    input, button {
        ${globalFontStyle}
    }
    #app {
        display: flex;
        flex-direction: column;
        animation: ${fadeIn} .5s;

        user-select: none;
        -webkit-app-region: no-drag;
    }

    ::-webkit-scrollbar {
        width: 8px;
    }
    ::-webkit-scrollbar-track {
        background: rgba(0, 0, 0, 0.05); 
    }
    ::-webkit-scrollbar-thumb {
        background: ${greyBlue1};
        &:active {
            background: ${brandLight};
        }
    }
    input[type=range] {
        -webkit-appearance: none;
    }
    input[type=range]::-webkit-slider-thumb {
        -webkit-appearance: none;
        height: 20px;
        width: 20px;
        border-radius: 50px;
        background: ${brandLight};
        top: -6px;
        position: relative;
        &:active {
            background: ${brand};
        }
    }
    input[type=range]::-webkit-slider-runnable-track {
        -webkit-appearance: none;
        height: 10px;
        background: #5a2058;
    }
    :focus-visible {
        outline: 2px solid ${brandLight};
        outline-offset: 2px;
    }
`

const history = createHashHistory()
const rootStore = createRootStore(history)

analytics.initialize()

const App: React.FC = () => (
    <React.Fragment>
        <GlobalStyle />
        <HistoryRouter history={history}>
            <StoreContext.Provider value={rootStore}>
                <Routes />
            </StoreContext.Provider>
        </HistoryRouter>
        <Toaster
            position="top-right"
            gutter={40}
            containerStyle={{
                marginTop: 35,
                overflowWrap: "anywhere",
                wordBreak: "break-word",
                fontSize: 14,
            }}
            toastOptions={{
                duration: 8_000,
            }}
        />
    </React.Fragment>
)

// Render components
const container = document.getElementById("app")
if (!container) {
    throw new Error("Could not find DOM container '#app'")
}
const root = createRoot(container)
root.render(<App />)

observe(rootStore, (change) => {
    if (change.name === "os" && container) {
        container.className = change.object[change.name]
    }
})


================================================
FILE: src/app/location/components/CurrentIP/CurrentIP.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import React from "react"
import styled from "styled-components"

import { useStores } from "../../../store"

const IP = styled.div`
    width: 100px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    user-select: text;
`

export const CurrentIP: React.FC<{ className?: string }> = observer(function CurrentIP({ className }) {
    const { connection } = useStores()
    return <IP className={className}>{connection.currentIp}</IP>
})


================================================
FILE: src/app/location/components/Flag/Flag.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import React from "react"
import { CircleFlag } from "react-circle-flags"

export interface FlagProps {
    className?: string
    countryCode?: string
    size?: number
}

export const Flag: React.FC<FlagProps> = ({ className, countryCode = "unknown", size = 15 }) => {
    return (
        <CircleFlag
            className={className}
            countryCode={countryCode?.toLowerCase() ?? "unknown"}
            width={size}
            height={size}
        />
    )
}


================================================
FILE: src/app/location/components/ProtectionStatus/ProtectionStatus.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import React from "react"
import styled from "styled-components"

import { useStores } from "../../../store"
import { brand } from "../../../ui-kit/colors"
import { connectionActive } from "../../../connection/status"

const Status = styled.div<{ isProtected: boolean }>`
    box-sizing: border-box;
    width: 80px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    border-radius: 18px;
    padding: 0 7px;
    color: ${(props) => (props.isProtected ? "#fff" : brand)};
    background: ${(props) => (props.isProtected ? "#58c800" : "inherit")};
`

export const ProtectionStatus = observer(function ProtectionStatus() {
    const { connection } = useStores()
    const isProtected = connectionActive(connection.status)
    return <Status isProtected={isProtected}>{isProtected ? "Protected" : "Unprotected"}</Status>
})


================================================
FILE: src/app/location/countries.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
export const countryNames: { [key: string]: string } = {
    af: "Afghanistan",
    al: "Albania",
    dz: "Algeria",
    as: "American Samoa",
    ad: "Andorra",
    ao: "Angola",
    ai: "Anguilla",
    aq: "Antarctica",
    ag: "Antigua and Barbuda",
    ar: "Argentina",
    am: "Armenia",
    aw: "Aruba",
    au: "Australia",
    at: "Austria",
    az: "Azerbaijan",
    bs: "Bahamas",
    bh: "Bahrain",
    bd: "Bangladesh",
    bb: "Barbados",
    by: "Belarus",
    be: "Belgium",
    bz: "Belize",
    bj: "Benin",
    bm: "Bermuda",
    bt: "Bhutan",
    bo: "Bolivia",
    ba: "Bosnia and Herzegovina",
    bw: "Botswana",
    br: "Brazil",
    vg: "British Virgin Islands",
    bn: "Brunei",
    bg: "Bulgaria",
    bf: "Burkina Faso",
    bi: "Burundi",
    kh: "Cambodia",
    cm: "Cameroon",
    ca: "Canada",
    cv: "Cape Verde",
    ky: "Cayman Islands",
    cf: "Central African Republic",
    td: "Chad",
    cl: "Chile",
    cn: "China",
    cx: "Christmas Island",
    cc: "Cocos (Keeling) Islands",
    co: "Colombia",
    km: "Comoros",
    ck: "Cook Islands",
    cr: "Costa Rica",
    hr: "Croatia",
    cu: "Cuba",
    cw: "Curaçao",
    cy: "Cyprus",
    cz: "Czech Republic",
    cd: "DR Congo",
    dk: "Denmark",
    dj: "Djibouti",
    dm: "Dominica",
    do: "Dominican Republic",
    ec: "Ecuador",
    eg: "Egypt",
    sv: "El Salvador",
    gq: "Equatorial Guinea",
    er: "Eritrea",
    ee: "Estonia",
    et: "Ethiopia",
    fk: "Falkland Islands",
    fo: "Faroe Islands",
    fj: "Fiji",
    fi: "Finland",
    fr: "France",
    pf: "French Polynesia",
    tf: "French Southern and Antarctic Lands",
    ga: "Gabon",
    gm: "Gambia",
    ge: "Georgia",
    de: "Germany",
    gh: "Ghana",
    gi: "Gibraltar",
    gr: "Greece",
    gl: "Greenland",
    gd: "Grenada",
    gu: "Guam",
    gt: "Guatemala",
    gg: "Guernsey",
    gn: "Guinea",
    gw: "Guinea-Bissau",
    gy: "Guyana",
    ht: "Haiti",
    hn: "Honduras",
    hk: "Hong Kong",
    hu: "Hungary",
    is: "Iceland",
    in: "India",
    id: "Indonesia",
    ir: "Iran",
    iq: "Iraq",
    ie: "Ireland",
    im: "Isle of Man",
    il: "Israel",
    it: "Italy",
    jm: "Jamaica",
    jp: "Japan",
    je: "Jersey",
    jo: "Jordan",
    kz: "Kazakhstan",
    ke: "Kenya",
    ki: "Kiribati",
    xk: "Kosovo",
    kw: "Kuwait",
    kg: "Kyrgyzstan",
    la: "Laos",
    lv: "Latvia",
    lb: "Lebanon",
    ls: "Lesotho",
    lr: "Liberia",
    ly: "Libya",
    li: "Liechtenstein",
    lt: "Lithuania",
    lu: "Luxembourg",
    mo: "Macau",
    mk: "Macedonia",
    mg: "Madagascar",
    mw: "Malawi",
    my: "Malaysia",
    mv: "Maldives",
    ml: "Mali",
    mt: "Malta",
    mh: "Marshall Islands",
    mq: "Martinique",
    mr: "Mauritania",
    mu: "Mauritius",
    yt: "Mayotte",
    mx: "Mexico",
    fm: "Micronesia",
    md: "Moldova",
    mc: "Monaco",
    mn: "Mongolia",
    me: "Montenegro",
    ms: "Montserrat",
    ma: "Morocco",
    mz: "Mozambique",
    mm: "Myanmar",
    na: "Namibia",
    nr: "Nauru",
    np: "Nepal",
    nl: "Netherlands",
    nc: "New Caledonia",
    nz: "New Zealand",
    ni: "Nicaragua",
    ne: "Niger",
    ng: "Nigeria",
    nu: "Niue",
    nf: "Norfolk Island",
    kp: "North Korea",
    mp: "Northern Mariana Islands",
    no: "Norway",
    om: "Oman",
    pk: "Pakistan",
    pw: "Palau",
    ps: "Palestine",
    pa: "Panama",
    pg: "Papua New Guinea",
    py: "Paraguay",
    pe: "Peru",
    ph: "Philippines",
    pn: "Pitcairn Islands",
    pl: "Poland",
    pt: "Portugal",
    pr: "Puerto Rico",
    qa: "Qatar",
    cg: "Republic of the Congo",
    ro: "Romania",
    ru: "Russia",
    rw: "Rwanda",
    re: "Réunion",
    bl: "Saint Barthélemy",
    kn: "Saint Kitts and Nevis",
    lc: "Saint Lucia",
    mf: "Saint Martin",
    vc: "Saint Vincent and the Grenadines",
    ws: "Samoa",
    sm: "San Marino",
    sa: "Saudi Arabia",
    sn: "Senegal",
    rs: "Serbia",
    sc: "Seychelles",
    sl: "Sierra Leone",
    sg: "Singapore",
    sx: "Sint Maarten",
    sk: "Slovakia",
    si: "Slovenia",
    sb: "Solomon Islands",
    so: "Somalia",
    za: "South Africa",
    gs: "South Georgia",
    kr: "South Korea",
    ss: "South Sudan",
    es: "Spain",
    lk: "Sri Lanka",
    sd: "Sudan",
    sr: "Suriname",
    sz: "Swaziland",
    se: "Sweden",
    ch: "Switzerland",
    sy: "Syria",
    tw: "Taiwan",
    tj: "Tajikistan",
    tz: "Tanzania",
    th: "Thailand",
    tg: "Togo",
    tk: "Tokelau",
    to: "Tonga",
    tt: "Trinidad and Tobago",
    tn: "Tunisia",
    tr: "Turkey",
    tm: "Turkmenistan",
    tc: "Turks and Caicos Islands",
    tv: "Tuvalu",
    ug: "Uganda",
    ua: "Ukraine",
    ae: "United Arab Emirates",
    gb: "United Kingdom",
    us: "United States",
    vi: "United States Virgin Islands",
    uy: "Uruguay",
    uz: "Uzbekistan",
    vu: "Vanuatu",
    va: "Vatican City",
    ve: "Venezuela",
    vn: "Vietnam",
    wf: "Wallis and Futuna",
    eh: "Western Sahara",
    ye: "Yemen",
    zm: "Zambia",
    zw: "Zimbabwe",
    ax: "Åland Islands",
} as const

export const countryName = (countryCode?: string): string => {
    countryCode = countryCode?.toLowerCase() ?? "unknown"
    const match = countryNames[countryCode]
    return match ?? "Unknown"
}

export const isUnknownCountry = (countryCode?: string): boolean => countryName(countryCode) === "Unknown"


================================================
FILE: src/app/location/states.ts
================================================
/**
 * Copyright (c) 2022 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

type CountryCode = string
type StateCode = string
type StateName = string

export const STATES: { [countryCode: CountryCode]: { [stateCode: StateCode]: StateName } } = {
    US: {
        AL: "Alabama",
        AK: "Alaska",
        AZ: "Arizona",
        AR: "Arkansas",
        CA: "California",
        CO: "Colorado",
        CT: "Connecticut",
        DE: "Delaware",
        FL: "Florida",
        GA: "Georgia",
        HI: "Hawaii",
        ID: "Idaho",
        IL: "Illinois",
        IN: "Indiana",
        IA: "Iowa",
        KS: "Kansas",
        KY: "Kentucky",
        LA: "Louisiana",
        ME: "Maine",
        MD: "Maryland",
        MA: "Massachusetts",
        MI: "Michigan",
        MN: "Minnesota",
        MS: "Mississippi",
        MO: "Missouri",
        MT: "Montana",
        NE: "Nebraska",
        NV: "Nevada",
        NH: "New Hampshire",
        NJ: "New Jersey",
        NM: "New Mexico",
        NY: "New York",
        NC: "North Carolina",
        ND: "North Dakota",
        OH: "Ohio",
        OK: "Oklahoma",
        OR: "Oregon",
        PA: "Pennsylvania",
        RI: "Rhode Island",
        SC: "South Carolina",
        SD: "South Dakota",
        TN: "Tennessee",
        TX: "Texas",
        UT: "Utah",
        VT: "Vermont",
        VA: "Virginia",
        WA: "Washington",
        WV: "West Virginia",
        WI: "Wisconsin",
        WY: "Wyoming",
    },
} as const


================================================
FILE: src/app/navigation/components/Routes/Routes.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import React from "react"
import { Route, Routes as ReactRoutes, Navigate, useLocation } from "react-router-dom"
import { ConnectionStatus } from "mysterium-vpn-js"
import { observer } from "mobx-react-lite"
import styled from "styled-components"

import { AcceptTermsView } from "../../../views/common/AcceptTerms/AcceptTermsView"
import { ConnectedView } from "../../../views/consumer/Connected/ConnectedView"
import { WalletView } from "../../../views/consumer/Wallet/WalletView"
import { useStores } from "../../../store"
import { TitleBar } from "../TitleBar/TitleBar"
import { locations } from "../../locations"
import { winSize } from "../../../../config"
import { NakedTitleBar } from "../TitleBar/NakedTitleBar"
import { HelpView } from "../../../views/common/Help/HelpView"
import { SettingsView } from "../../../views/common/Settings/SettingsView"
import { TopupRoutes } from "../../../views/consumer/Topup/TopupRoutes"
import { StartupLoadingView } from "../../../daemon/components/StartupLoadingView/StartupLoadingView"
import { IdentityRegistrationView } from "../../../identity/components/IdentityRegistrationView/IdentityRegistrationView"
import { QuickConnectView } from "../../../views/consumer/Proposals/QuickConnectView"
import { ManualConnectView } from "../../../views/consumer/Proposals/ManualConnectView"
import { Welcome } from "../../../onboarding/components/Welcome/Welcome"
import { IdentitySetup } from "../../../onboarding/components/IdentitySetup/IdentitySetup"
import { IdentityBackup } from "../../../onboarding/components/IdentityBackup/IdentityBackup"
import { InitialTopup } from "../../../onboarding/components/InitialTopup/InitialTopup"
import { HelpContentReportIssue } from "../../../views/common/Help/HelpContentReportIssue"
import { HelpContentTermsAndConditions } from "../../../views/common/Help/HelpContentTermsAndConditions"
import { SettingsFilters } from "../../../views/common/Settings/SettingsFilters"
import { SettingsConnection } from "../../../views/common/Settings/SettingsConnection"
import { SettingsMysteriumId } from "../../../views/common/Settings/SettingsMysteriumId"
import { Step1 } from "../../../onboarding/components/IntroductionSteps/Step1"
import { Step2 } from "../../../onboarding/components/IntroductionSteps/Step2"
import { Step3 } from "../../../onboarding/components/IntroductionSteps/Step3"
import { Step4 } from "../../../onboarding/components/IntroductionSteps/Step4"
import { IdentityUpgradeView } from "../../../identity/components/IdentityUpgradeView/IdentityUpgradeView"

const WinContents = styled.div`
    min-height: 0;
    flex: 1;
    display: flex;
    flex-direction: row;
`

const Main = styled.div`
    width: ${winSize.width}px;
    display: flex;
    flex-direction: column;

    color: #404040;
    background: #fff;
`

export const Routes: React.FC = observer(function Routes() {
    const { connection, config } = useStores()
    const location = useLocation()
    const nakedTitleBar = [
        locations.onboarding,
        locations.terms,
        locations.idRegistering,
        locations.idUpgrading,
        locations.loading,
    ].find((p) => location.pathname.startsWith(p))
    return (
        <WinContents>
            <Main>
                {nakedTitleBar ? <NakedTitleBar /> : <TitleBar />}
                <ReactRoutes>
                    <Route path="/loading" element={<StartupLoadingView />} />
                    <Route path="/onboarding/*">
                        <Route path="welcome" element={<Welcome />} />
                        <Route path="intro/*">
                            <Route path="*" element={<Step1 />} />
                            <Route path="2" element={<Step2 />} />
                            <Route path="3" element={<Step3 />} />
                            <Route path="4" element={<Step4 />} />
                        </Route>
                        <Route path="identity/setup" element={<IdentitySetup />} />
                        <Route path="identity/backup" element={<IdentityBackup />} />
                        <Route path="topup-prompt" element={<InitialTopup />} />
                        <Route path="wallet/topup/*" element={<TopupRoutes />} />
                        <Route path="*" element={<Navigate replace to="welcome" />} />
                    </Route>
                    <Route path="/terms" element={<AcceptTermsView />} />
                    <Route path="/registration" element={<IdentityRegistrationView />} />
                    <Route path="/id-upgrade" element={<IdentityUpgradeView />} />
                    <Route
                        path="/consumer"
                        element={
                            <Navigate
                                to={
                                    connection.status === ConnectionStatus.NOT_CONNECTED
                                        ? locations.proposals
                                        : locations.connection
                                }
                            />
                        }
                    />
                    <Route path="/consumer/proposals/*">
                        <Route path="quick-connect" element={<QuickConnectView />} />
                        <Route path="manual-connect" element={<ManualConnectView />} />
                        <Route
                            path="*"
                            element={<Navigate replace to={config.quickConnect ? "quick-connect" : "manual-connect"} />}
                        />
                    </Route>
                    <Route path="/consumer/connection" element={<ConnectedView />} />
                    <Route path="/settings/*" element={<SettingsView />}>
                        <Route path="filters" element={<SettingsFilters />} />
                        <Route path="connection" element={<SettingsConnection />} />
                        <Route path="mysterium-id" element={<SettingsMysteriumId />} />
                        <Route path="*" element={<Navigate replace to="filters" />} />
                    </Route>
                    <Route path="/wallet" element={<WalletView />} />
                    <Route path="/wallet/topup/*" element={<TopupRoutes />} />
                    <Route path="/help/*" element={<HelpView />}>
                        <Route path="bug-report" element={<HelpContentReportIssue />} />
                        <Route path="terms-and-conditions" element={<HelpContentTermsAndConditions />} />
                        <Route path="*" element={<Navigate to="bug-report" />} />
                    </Route>
                    <Route path="*" element={<Navigate replace to="/loading" />} />
                </ReactRoutes>
            </Main>
        </WinContents>
    )
})


================================================
FILE: src/app/navigation/components/TitleBar/NakedTitleBar.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import React from "react"
import styled from "styled-components"
import { observer } from "mobx-react-lite"

import { useStores } from "../../../store"

import { WindowButtonsWindows } from "./WindowButtonsWindows"
import { WindowButtonsLinux } from "./WindowButtonsLinux"
import { Container as TitlebarContainer } from "./TitleBar"

const Container = styled(TitlebarContainer)`
    justify-content: flex-end;
`

export const NakedTitleBar: React.FC = observer(function NakedTitleBar() {
    const root = useStores()
    return (
        <Container>
            {root.isWindows && <WindowButtonsWindows />}
            {root.isLinux && <WindowButtonsLinux />}
        </Container>
    )
})


================================================
FILE: src/app/navigation/components/TitleBar/TitleBar.tsx
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import React from "react"
import styled from "styled-components"
import { Currency } from "mysterium-vpn-js"
import { observer } from "mobx-react-lite"
import { faHome } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { useLocation, useNavigate } from "react-router-dom"

import { useStores } from "../../../store"
import { IconMystToken } from "../../../ui-kit/icons/IconMystToken"
import { locations } from "../../locations"
import { titleBarSize } from "../../../../config"
import { ProtectionStatus } from "../../../location/components/ProtectionStatus/ProtectionStatus"
import { CurrentIP } from "../../../location/components/CurrentIP/CurrentIP"
import { greyBlue1, greyBlue2, lightBlue } from "../../../ui-kit/colors"
import { displayTokens2 } from "../../../payment/display"

import { WindowButtonsWindows } from "./WindowButtonsWindows"
import { WindowButtonsLinux } from "./WindowButtonsLinux"

export const Container = styled.div`
    box-sizing: border-box;
    height: ${titleBarSize.height}px;
    flex-shrink: 0;

    padding: 0 15px;
    .win32 & {
        padding: 0;
    }
    .darwin & {
        padding-left: 80px;
    }

    color: ${lightBlue};
    background: #0c0c0c;
    display: flex;
    align-items: center;

    user-select: none;
    -webkit-app-region: drag;
`

const NavigationButton = styled.div<{ active: boolean }>`
    -webkit-app-region: no-drag;

    height: 28px;
    border-radius: 4px;
    margin-right: 8px;
    .win32 & {
        height: 100%;
        border-radius: 0;
        margin-right: 0;
    }
    .darwin & {
        height: 20px;
        border-radius: 18px;
    }

    line-height: 12px;
    padding: 0 16px;

    &:hover {
        background: ${(props) => (props.active ? greyBlue1 : "#aeaedb33")};
        color: ${(props) => (props.active ? "#fff" : "inherit")};
    }
    background: ${(props) => (props.active ? greyBlue2 : "inherit")};
    color: ${(props) => (props.active ? "#fff" : "inherit")};

    display: flex;
    justify-content: center;
    align-items: center;
`

const WalletButton = styled(NavigationButton)`
    box-sizing: border-box;
    min-width: 114px;
    margin: 0 16px 0 auto;
    .darwin & {
        margin-right: 0;
    }
    overflow: hidden;
    white-space: nowrap;
`

const Location = styled.div`
    margin: 0 auto;
    display: flex;
    flex: 0;
`

const IP = styled(CurrentIP)`
    opacity: 0.5;
`

const Money = styled.div`
    display: flex;
    justify-content: space-between;
    align-items: center;
    span {
        padding-left: 6px;
    }
`
export const TitleBar: React.FC = observer(function TitleBar() {
    const { navigation, identity, isWindows, isLinux } = useStores()
    const navigate = useNavigate()
    const location = useLocation()
    const isHomeActive = location.pathname.startsWith(locations.consumer)
    const isSettingsActive = location.pathname.startsWith(locations.settings)
    const isHelpActive = location.pathname.startsWith(locations.help)
    const isWalletActive = location.pathname.startsWith(locations.wallet)
    return (
        <Container>
            <NavigationButton active={isHomeActive} onClick={() => !isHomeActive && navigation.goHome()}>
                <FontAwesomeIcon icon={faHome} />
            </NavigationButton>
            <NavigationButton
                active={isSettingsActive}
                onClick={() => !isSettingsActive && navigate(locations.settings)}
            >
                Settings
            </NavigationButton>
            <NavigationButton active={isHelpActive} onClick={() => !isHelpActive && navigate(locations.help)}>
                Help
            </NavigationButton>
            <Location>
                <IP />
                <ProtectionStatus />
            </Location>
            <WalletButton active={isWalletActive} onClick={() => !isWalletActive && navigate(locations.wallet)}>
                <Money>
                    <IconMystToken color={isWalletActive ? "#fff" : greyBlue1} />
                    <span>
                        {displayTokens2(identity.identity?.balanceTokens)} {Currency.MYST}
                    </span>
                </Money>
            </WalletButton>
            {isWindows && <WindowButtonsWindows />}
            {isLinux && <WindowButtonsLinux />}
        </Container>
    )
})


================================================
FILE: src/app/navigation/components/TitleBar/WindowButtonsLinux.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import React from "react"
import styled from "styled-components"
import { ipcRenderer } from "electron"

import { MainIpcListenChannels } from "../../../../shared/ipc"

const Container = styled.div`
    display: flex;
    align-items: center;
    height: 100%;
`

const Button = styled.div`
    width: 24px;
    height: 24px;
    border-radius: 24px;
    display: flex;
    justify-content: center;
    align-items: center;

    user-select: none;
    -webkit-app-region: no-drag;

    fill: #fff;
    &:hover {
        background: #aeaedb33;
    }
    &:active {
        background: rgba(0, 0, 0, 0.3);
    }
`

export const WindowButtonsLinux: React.FC = () => {
    return (
        <Container>
            <Button onClick={() => ipcRenderer.send(MainIpcListenChannels.MinimizeWindow)} style={{ marginRight: 16 }}>
                <svg width="24" height="24" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
                    <g transform="translate(0 -1036.4)">
                        <rect id="rect4834" x="4" y="1044.4" width="8" height="1" fill="#3c3857" />
                    </g>
                </svg>
            </Button>
            <Button onClick={() => ipcRenderer.send(MainIpcListenChannels.CloseWindow)}>
                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 16 16" version="1.1">
                    <g transform="translate(0,-1036.3621)">
                        <path
                            d="m 10.299805,1041.3553 -0.3535201,0.3535 -4.5996095,4.5996 -0.35351,0.3535 0.70703,0.7071 0.35352,-0.3535 4.5996096,-4.5996 0.35351,-0.3536 -0.70703,-0.707 z"
                            id="path4535"
                            fill="#3c3857"
                        />
                        <path
                            d="m 5.7001954,1041.3553 -0.70703,0.707 0.35351,0.3536 4.5996095,4.5996 0.3535201,0.3535 0.70703,-0.7071 -0.35351,-0.3535 -4.5996096,-4.5996 -0.35352,-0.3535 z"
                            id="path4537"
                            fill="#3c3857"
                        />
                    </g>
                </svg>
            </Button>
        </Container>
    )
}


================================================
FILE: src/app/navigation/components/TitleBar/WindowButtonsWindows.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import React from "react"
import styled from "styled-components"
import { ipcRenderer } from "electron"

import { MainIpcListenChannels } from "../../../../shared/ipc"

const Container = styled.div`
    display: flex;
    align-items: center;
    height: 100%;
`

const Button = styled.div`
    width: 46px;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;

    user-select: none;
    -webkit-app-region: no-drag;

    fill: #fff;
    &:hover {
        background: #aeaedb33;
    }
    &:active {
        background: rgba(0, 0, 0, 0.3);
    }
`

export const WindowButtonsWindows: React.FC = () => {
    return (
        <Container>
            <Button onClick={() => ipcRenderer.send(MainIpcListenChannels.MinimizeWindow)}>
                <svg width="11" height="1" viewBox="0 0 11 1">
                    <path d="m11 0v1h-11v-1z" strokeWidth=".26208" />
                </svg>
            </Button>
            <Button onClick={() => ipcRenderer.send(MainIpcListenChannels.CloseWindow)}>
                <svg width="12" height="12" viewBox="0 0 12 12">
                    <path
                        d="m6.8496 6 5.1504 5.1504-0.84961 0.84961-5.1504-5.1504-5.1504 5.1504-0.84961-0.84961 5.1504-5.1504-5.1504-5.1504 0.84961-0.84961 5.1504 5.1504 5.1504-5.1504 0.84961 0.84961z"
                        strokeWidth=".3"
                    />
                </svg>
            </Button>
        </Container>
    )
}


================================================
FILE: src/app/navigation/components/ViewContainer/ViewContainer.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import styled from "styled-components"

import { bg1 } from "../../../ui-kit/colors"

export const ViewContainer = styled.div`
    flex: 1;
    overflow: hidden;

    background: ${bg1};
    color: #fff;
`


================================================
FILE: src/app/navigation/components/ViewContent/ViewContent.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import styled from "styled-components"

export const ViewContent = styled.div`
    width: 378px;
    box-sizing: border-box;
    border-radius: 10px;
    overflow: hidden;

    display: flex;
    flex-direction: column;
    align-items: center;
    background: #ffffff12;
`


================================================
FILE: src/app/navigation/components/ViewNavBar/ViewNavBar.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React, { PropsWithChildren } from "react"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faArrowCircleLeft } from "@fortawesome/free-solid-svg-icons"

import { LogoTitle } from "../../../ui-kit/components/LogoTitle/LogoTitle"
import { Heading2 } from "../../../ui-kit/typography"

const Container = styled.div`
    box-sizing: border-box;
    height: 58px;
    min-height: 58px;
    max-height: 58px;
    padding: 0 15px;

    display: grid;
    grid-template-columns: 222px 378px;
    column-gap: 10px;
    align-items: center;
`

const BackContainer = styled.div`
    display: flex;
    flex-direction: row;
    align-items: center;
    color: #ffffffcc;
    &:hover {
        color: #fff;
    }
`

const BackIcon = styled(FontAwesomeIcon)`
    margin-right: 10px;
`

export interface ViewNavBarProps {
    onBack?: () => void
}

export const ViewNavBar: React.FC<PropsWithChildren<ViewNavBarProps>> = ({ onBack, children }) => (
    <Container>
        {onBack && (
            <BackContainer onClick={onBack}>
                <BackIcon icon={faArrowCircleLeft} size="2x" />
                <Heading2>Back</Heading2>
            </BackContainer>
        )}
        {!onBack && <LogoTitle />}
        {children}
    </Container>
)


================================================
FILE: src/app/navigation/components/ViewSidebar/ViewSidebar.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import styled from "styled-components"

import { darkBlue } from "../../../ui-kit/colors"

export const ViewSidebar = styled.div`
    height: 100%;
    width: 222px;
    min-width: 222px;
    margin-right: 10px;
    border-radius: 10px;
    overflow: hidden;

    display: flex;
    flex-direction: column;
    background: #f8f8fd;
    color: ${darkBlue};
`


================================================
FILE: src/app/navigation/components/ViewSplit/ViewSplit.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import styled from "styled-components"

export const ViewSplit = styled.div`
    height: 486px;
    margin: 0 15px;

    display: flex;
`


================================================
FILE: src/app/navigation/locations.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

export const locations = {
    loading: "/loading",
    onboarding: "/onboarding",
    onboardingWelcome: "/onboarding/welcome",
    onboardingIntroIndex: "/onboarding/intro",
    onboardingIntro2: "/onboarding/intro/2",
    onboardingIntro3: "/onboarding/intro/3",
    onboardingIntro4: "/onboarding/intro/4",
    onboardingIdentitySetup: "/onboarding/identity/setup",
    onboardingIdentityBackup: "/onboarding/identity/backup",
    onboardingTopupPrompt: "/onboarding/topup-prompt",
    onboardingWalletTopup: "/onboarding/wallet/topup",
    terms: "/terms",
    idRegistering: "/registration",
    idUpgrading: "/id-upgrade",
    wallet: "/wallet",
    walletTopup: "/wallet/topup",
    consumer: "/consumer",
    proposals: "/consumer/proposals",
    proposalsManualConnect: "/consumer/proposals/manual-connect",
    proposalsQuickConnect: "/consumer/proposals/quick-connect",
    connection: "/consumer/connection",
    help: "/help",
    helpBugReport: "/help/bug-report",
    helpTermsAndConditions: "/help/terms-and-conditions",
    settings: "/settings",
    settingsFilters: "/settings/filters",
    settingsConnection: "/settings/connection",
    settingsMysteriumId: "/settings/mysterium-id",
}

export const topupSteps = {
    chooseMethod: "choose-method",
    coingate: "coingate", // entry point
    coingatePaymentOptions: "coingate-payment-options",
    coingateOrderSummary: "coingate-order-summary",
    coingateWaitingForPayment: "coingate-waiting-for-payment",
    stripe: "stripe", // entry point
    stripePaymentOptions: "stripe-payment-options",
    stripeOrderSummary: "stripe-order-summary",
    stripeWaitingForPayment: "stripe-waiting-for-payment",
    paypal: "paypal", // entry point
    paypalPaymentOptions: "paypal-payment-options",
    paypalOrderSummary: "paypal-order-summary",
    paypalWaitingForPayment: "paypal-waiting-for-payment",
    myst: "myst", // entry point
    mystSelectAmount: "myst-select-amount",
    mystPolygonWaitingForPayment: "myst-polygon-waiting-for-payment",
    success: "success",
    failed: "failed",
}


================================================
FILE: src/app/navigation/store.ts
================================================
/**
 * Copyright (c) 2020 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { action, computed, makeObservable } from "mobx"
import { ConnectionStatus, IdentityRegistrationStatus } from "mysterium-vpn-js"
import { ipcRenderer } from "electron"
import { History, Location } from "history"

import { RootStore } from "../store"
import { MainIpcListenChannels } from "../../shared/ipc"
import { connectionInProgress } from "../connection/status"
import { log } from "../../shared/log/log"

import { locations } from "./locations"

export class NavigationStore {
    root: RootStore
    history: History

    constructor(root: RootStore, history: History) {
        makeObservable(this, {
            location: computed,
            showLoading: action,
            goHome: action,
            navigateToInitialRoute: action,
            navigateOnConnectionStatus: action,
            openChat: action,
        })
        this.root = root
        this.history = history
    }

    push = (path: string): void => {
        log.debug("Navigating ->", path)
        this.history?.push(path)
    }

    get location(): Location {
        return this.history.location
    }

    showLoading = (): void => {
        this.push(locations.loading)
    }

    goHome = (): void => {
        if (connectionInProgress(this.root.connection.status)) {
            this.push(locations.connection)
            return
        }
        if (!this.root.config.quickConnect) {
            this.push(locations.proposalsManualConnect)
        } else {
            this.push(locations.proposalsQuickConnect)
        }
    }

    navigateToInitialRoute = (): void => {
        const newLocation = this.determineInitialLocation()
        if (newLocation) {
            this.push(newLocation)
        }
    }

    determineInitialLocation = (): string | undefined => {
        const { config, identity, connection } = this.root
        if (connectionInProgress(connection.status)) {
            return locations.connection
        }
        if (this.location.pathname == locations.wallet) {
            return undefined
        }
        if (!config.onboarded) {
            return locations.onboardingWelcome
        }
        if (!config.currentTermsAgreed) {
            return locations.terms
        }
        if (!identity.identity) {
            return locations.onboardingIdentitySetup
        }
        switch (identity.identity.registrationStatus) {
            case IdentityRegistrationStatus.Unknown:
                return undefined // Do nothing (leave loading)
            case IdentityRegistrationStatus.InProgress:
                return locations.idRegistering
            case IdentityRegistrationStatus.Unregistered:
            case IdentityRegistrationStatus.RegistrationError:
                return locations.onboardingTopupPrompt
        }
        // Proposals view
        if (!this.root.config.quickConnect) {
            return locations.proposalsManualConnect
        }
        return locations.proposalsQuickConnect
    }

    navigateOnConnectionStatus = (status: ConnectionStatus): void => {
        const isHomeActive = this.location.pathname.includes(locations.consumer)
        if (!isHomeActive) {
            // Do not change location if user is not in the home (consumer) view:
            // he might be viewing settings or wallet
            return
        }
        if (connectionInProgress(status)) {
            this.push(locations.connection)
        } else {
            this.push(locations.proposals)
        }
    }

    openChat = (): void => {
        ipcRenderer.send(MainIpcListenChannels.OpenSupportChat, this.root.identity.identity?.id)
    }
}


================================================
FILE: src/app/onboarding/components/IdentityBackup/IdentityBackup.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import React, { useState } from "react"
import { faFileExport, faArrowAltCircleLeft } from "@fortawesome/free-solid-svg-icons"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import Lottie from "react-lottie-player"
import toast from "react-hot-toast"
import { useNavigate } from "react-router-dom"

import { ViewContainer } from "../../../navigation/components/ViewContainer/ViewContainer"
import { ViewSplit } from "../../../navigation/components/ViewSplit/ViewSplit"
import { ViewSidebar } from "../../../navigation/components/ViewSidebar/ViewSidebar"
import { ViewContent } from "../../../navigation/components/ViewContent/ViewContent"
import { ViewNavBar } from "../../../navigation/components/ViewNavBar/ViewNavBar"
import { Heading2, Small } from "../../../ui-kit/typography"
import {
    ButtonContent,
    ButtonIcon,
    PrimarySidebarActionButton,
    SecondarySidebarActionButton,
} from "../../../ui-kit/components/Button/SidebarButtons"
import { useStores } from "../../../store"
import { ExportIdentityFormFields, ExportIdentityPrompt } from "../../../views/common/Settings/ExportIdentityPrompt"
import { brandLight } from "../../../ui-kit/colors"
import { dismissibleToast } from "../../../ui-kit/components/dismissibleToast"

import animationIdentityKeys from "./animation_identity_keys.json"

const SideTop = styled.div`
    box-sizing: border-box;
    height: 136px;
    padding: 20px;
    overflow: hidden;
    text-align: center;
`

const SectionIcon = styled(FontAwesomeIcon)`
    margin-bottom: 15px;
    font-size: 20px;
    color: ${brandLight};
`

const Title = styled(Heading2)`
    margin-bottom: 15px;
`

const SideBot = styled.div`
    background: #fff;
    box-shadow: 0px 0px 30px rgba(11, 0, 75, 0.1);
    border-radius: 10px;
    box-sizing: border-box;
    padding: 20px;
    flex: 1 0 auto;

    display: flex;
    flex-direction: column;
`

const Content = styled(ViewContent)`
    background: none;
    justify-content: center;
`

export const IdentityBackup: React.FC = observer(function IdentityBackup() {
    const { onboarding, identity } = useStores()

    const navigate = useNavigate()
    const nextStep = () => {
        onboarding.finishIDSetup()
    }

    const [exportPrompt, setExportPrompt] = useState(false)
    const handleBackupNow = () => {
        setExportPrompt(true)
    }
    const handleExportSubmit = async ({ passphrase }: ExportIdentityFormFields) => {
        setExportPrompt(false)
        try {
            await identity.exportIdentity({ id: identity.identity?.id ?? "", passphrase })
            nextStep()
        } catch (reason) {
            toast.error(
                dismissibleToast(
                    <span>
                        <>
                            <b>Identity backup failed 😶</b>
                            <br />
                            Error: {reason}
                        </>
                    </span>,
                ),
            )
        }
    }
    const handleExportCancel = () => {
        setExportPrompt(false)
    }
    return (
        <ViewContainer>
            <ViewNavBar />
            <ViewSplit>
                <ViewSidebar>
                    <SideTop>
                        <SectionIcon icon={faFileExport} />
                        <Title>Create backup</Title>
                        <Small>We don&apos;t store any account data. Back up to keep your tokens safe.</Small>
                    </SideTop>
                    <SideBot>
                        <PrimarySidebarActionButton onClick={handleBackupNow}>
                            <ButtonContent>
                                <ButtonIcon>
                                    <FontAwesomeIcon icon={faFileExport} />
                                </ButtonIcon>
                                Backup Private Key
                            </ButtonContent>
                        </PrimarySidebarActionButton>
                        <SecondarySidebarActionButton onClick={() => navigate(-1)}>
                            <ButtonContent>
                                <ButtonIcon>
                                    <FontAwesomeIcon icon={faArrowAltCircleLeft} />
                                </ButtonIcon>
                                Go Back
                            </ButtonContent>
                        </SecondarySidebarActionButton>
                    </SideBot>
                </ViewSidebar>
                <Content>
                    <Lottie
                        play
                        loop={false}
                        animationData={animationIdentityKeys}
                        style={{ width: 256, height: 256 }}
                        renderer="svg"
                    />
                </Content>
            </ViewSplit>
            <ExportIdentityPrompt visible={exportPrompt} onSubmit={handleExportSubmit} onCancel={handleExportCancel} />
        </ViewContainer>
    )
})


================================================
FILE: src/app/onboarding/components/IdentityBackup/animation_identity_keys.json
================================================
{"v":"5.7.4","fr":29.9700012207031,"ip":117.000004765508,"op":197.000008023974,"w":1110,"h":1080,"nm":"Композиция 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":5,"nm":"MYST","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":226,"s":[558.714,785.871,0],"to":[0,-14,0],"ti":[0,14,0]},{"t":263.00001071221,"s":[558.714,701.871,0]}],"ix":2,"l":2},"a":{"a":0,"k":[68.892,-18.129,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":226,"s":[0,0,100]},{"t":259.000010549286,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":51,"f":"Roboto-Regular","t":"MYST","j":0,"tr":0,"lh":61.2,"ls":0,"fc":[0.929,0.357,0.675]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":5,"nm":"0000","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":221,"s":[558.714,737.789,0],"to":[0,-21.667,0],"ti":[0,21.667,0]},{"t":261.000010630748,"s":[558.714,607.789,0]}],"ix":2,"l":2},"a":{"a":0,"k":[150.535,-46.211,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":221,"s":[0,0,100]},{"t":257.000010467825,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":130,"f":"Roboto-Black","t":"0000","j":0,"tr":0,"lh":156,"ls":0,"fc":[1,1,1]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Кривые Key","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,452,0],"ix":2,"l":2},"a":{"a":0,"k":[34,34.5,0],"ix":1,"l":2},"s":{"a":0,"k":[345.1,345.1,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.53,-3.53],[2.419,0],[1.71,1.709],[0,2.418],[-1.711,1.71],[-2.419,0],[-1.71,-1.71]],"o":[[-1.71,1.709],[-2.419,0],[-1.711,-1.711],[0,-2.419],[1.709,-1.71],[2.418,0],[3.53,3.531]],"v":[[18.413,-5.602],[12.011,-2.951],[5.608,-5.602],[2.956,-12.005],[5.608,-18.408],[12.011,-21.06],[18.413,-18.408]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[8.575,8.575],[5.873,0],[4.153,-4.153],[0,-5.874],[-1.152,-2.695],[0,0],[0.055,-0.619],[0,0],[-0.555,-0.554],[-0.782,0.069],[0,0],[-0.44,0.439],[0,0],[0.005,0.077],[0,0],[-0.262,0.262],[-0.372,-0.023],[0,0],[-0.073,-0.018],[0,0],[0.005,0.076],[0,0],[-0.263,0.262],[-0.371,-0.024],[0,0],[-0.073,-0.018],[0,0],[-3.02,0],[-4.153,4.152]],"o":[[-4.153,-4.153],[-5.874,0],[-4.153,4.154],[0,3.02],[0,0],[-0.441,0.439],[0,0],[-0.07,0.783],[0.555,0.556],[0,0],[0.619,-0.055],[0,0],[-0.017,-0.072],[0,0],[-0.025,-0.369],[0.262,-0.261],[0,0],[0.077,0.006],[0,0],[-0.018,-0.072],[0,0],[-0.025,-0.37],[0.261,-0.263],[0,0],[0.076,0.005],[0,0],[2.695,1.153],[5.874,0],[8.574,-8.574]],"v":[[27.559,-27.555],[12.011,-33.996],[-3.539,-27.555],[-9.98,-12.005],[-8.219,-3.365],[-32.447,20.864],[-33.214,22.502],[-33.981,31.109],[-33.215,33.219],[-31.105,33.986],[-22.498,33.219],[-20.858,32.453],[-18.249,29.842],[-18.284,29.619],[-18.712,23.381],[-18.34,22.387],[-17.346,22.015],[-11.108,22.442],[-10.884,22.478],[-8.836,20.43],[-8.872,20.207],[-9.3,13.969],[-8.927,12.975],[-7.933,12.602],[-1.695,13.03],[-1.472,13.066],[3.371,8.222],[12.012,9.983],[27.56,3.544]],"c":true},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"mm","mm":4,"nm":"Объединить контуры 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[34.991,36.015],"to":[10,-1.833],"ti":[-10,1.833]},{"i":{"x":0.53,"y":0.53},"o":{"x":1.019,"y":1.019},"t":150,"s":[94.991,25.015],"to":[0,0],"ti":[0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":210,"s":[94.991,25.015],"to":[0,0],"ti":[0,0]},{"t":239.00000973467,"s":[15.991,25.015]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16],"y":[1,1]},"o":{"x":[0.84,0.84],"y":[0,0]},"t":120,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":158,"s":[119,119]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":180,"s":[98,98]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":210,"s":[98,98]},{"t":239.00000973467,"s":[0,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.16],"y":[1]},"o":{"x":[0.84],"y":[0]},"t":120,"s":[-90]},{"t":158.000006435472,"s":[0]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Группа 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Слой-фигура 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,338.919,0],"ix":2,"l":2},"a":{"a":0,"k":[41.759,-208.081,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":17,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-74.227,-10.162],[-74.227,5],[-91.227,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[-91.227,-27.162]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":47,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[215.773,-10.162],[215.773,5],[198.773,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[198.773,-27.162]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":74,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[159.773,-10.162],[159.773,5],[142.773,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[142.773,-27.162]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[215.773,-10.162],[215.773,5],[198.773,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[198.773,-27.162]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-74.227,-10.162],[-74.227,5],[-91.227,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[-91.227,-27.162]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22,-204.5],"ix":2},"a":{"a":0,"k":[-124,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Слой-фигура 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,415.955,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":22,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":52,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[158.336,-8.127],[158.336,7.036],[141.336,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[141.336,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[236.336,-8.127],[236.336,7.036],[219.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[219.337,-25.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[158.336,-8.127],[158.336,7.036],[141.336,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[141.336,-25.127]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Слой-фигура 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,489.955,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":39,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":69,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[186.336,-8.127],[186.336,7.036],[169.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[169.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[140.336,-8.127],[140.336,7.036],[123.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[123.337,-25.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[186.336,-8.127],[186.336,7.036],[169.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[169.337,-25.127]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":39,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":44,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Слой-фигура 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,567.15,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":43,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":73,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[121.337,-8.127],[121.337,7.036],[104.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[104.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[209.336,-8.127],[209.336,7.036],[192.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[192.337,-25.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[121.337,-8.127],[121.337,7.036],[104.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[104.337,-25.127]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Слой-фигура 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[326,452,0],"ix":2,"l":2},"a":{"a":0,"k":[-214,-88,0],"ix":1,"l":2},"s":{"a":0,"k":[96.367,96.367,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[151.722,-115],[151.722,115],[121.722,145],[-113.942,145],[-143.942,115],[-143.942,-115],[-113.942,-145],[121.722,-145]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[10]},{"t":150.000006109625,"s":[5]}],"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":13,"s":[-208,60],"to":[0,-24.667],"ti":[0,24.667]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.167,"y":0.167},"t":30,"s":[-208,-88],"to":[0,0],"ti":[0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[-208,-88],"to":[37.703,-6.572],"ti":[-37.703,6.572]},{"i":{"x":0.231,"y":0.231},"o":{"x":0.208,"y":0.208},"t":150,"s":[18.219,-127.433],"to":[0,0],"ti":[0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":211,"s":[18.219,-127.433],"to":[0,0],"ti":[0,0]},{"t":239.00000973467,"s":[-297.781,-151.433]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16],"y":[1,1]},"o":{"x":[0.84,0.84],"y":[0,0]},"t":13,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":30,"s":[100,100]},{"i":{"x":[0.9,0.9],"y":[1.388,1.388]},"o":{"x":[0.688,0.688],"y":[0,0]},"t":120,"s":[100,100]},{"i":{"x":[0.027,0.027],"y":[1,1]},"o":{"x":[0.142,0.142],"y":[0.199,0.199]},"t":134,"s":[80,80]},{"i":{"x":[0.626,0.626],"y":[1,1]},"o":{"x":[0.158,0.158],"y":[0,0]},"t":150,"s":[143,143]},{"i":{"x":[0.34,0.34],"y":[1,1]},"o":{"x":[0.66,0.66],"y":[0,0]},"t":211,"s":[143,143]},{"t":239.00000973467,"s":[45,45]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.16],"y":[1]},"o":{"x":[0.84],"y":[0]},"t":120,"s":[0]},{"i":{"x":[0.811],"y":[1]},"o":{"x":[0.514],"y":[0]},"t":150,"s":[90]},{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":211,"s":[90]},{"t":239.00000973467,"s":[90]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Слой-фигура 13","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.259,687.15,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":18,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":70,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[175.336,-8.127],[175.336,7.036],[158.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[158.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":101,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[55.337,-8.127],[55.337,7.036],[38.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[38.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[175.336,-8.127],[175.336,7.036],[158.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[158.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":150,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[275.337,31.873],[275.337,75.036],[258.337,92.036],[72.614,92.036],[55.614,75.036],[55.614,31.873],[72.614,14.873],[258.337,14.873]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":180,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[459.337,31.873],[459.337,75.036],[442.337,92.036],[72.614,92.036],[55.614,75.036],[55.614,31.873],[72.614,14.873],[442.337,14.873]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":211,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[459.337,31.873],[459.337,75.036],[442.337,92.036],[72.614,92.036],[55.614,75.036],[55.614,31.873],[72.614,14.873],[442.337,14.873]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":241,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[643.337,-336.127],[643.337,-332.714],[626.337,-315.714],[488.614,-315.714],[471.614,-332.714],[471.614,-336.127],[488.613,-353.127],[626.337,-353.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":256,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[643.337,-336.127],[643.337,-332.714],[626.337,-315.714],[574.614,-315.714],[557.614,-332.714],[557.614,-336.127],[574.613,-353.127],[626.337,-353.127]],"c":true}]},{"t":270.000010997325,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[643.337,-336.127],[643.337,-332.714],[626.337,-315.714],[488.614,-315.714],[471.614,-332.714],[471.614,-336.127],[488.613,-353.127],[626.337,-353.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":18,"s":[0]},{"t":34.0000013848484,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Слой-фигура 14","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[187.259,765.15,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":30,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":80,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[145.336,-8.127],[145.336,7.036],[128.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[128.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":101,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[185.336,-8.127],[185.336,7.036],[168.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[168.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[145.336,-8.127],[145.336,7.036],[128.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[128.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":150,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[467.336,61.873],[467.336,104.036],[450.337,121.036],[73.614,121.036],[56.614,104.036],[56.614,61.873],[73.614,44.873],[450.337,44.873]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":180,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[295.336,61.873],[295.336,104.036],[278.337,121.036],[73.614,121.036],[56.614,104.036],[56.614,61.873],[73.614,44.873],[278.337,44.873]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":211,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[295.336,61.873],[295.336,104.036],[278.337,121.036],[73.614,121.036],[56.614,104.036],[56.614,61.873],[73.614,44.873],[278.337,44.873]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":241,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[642.336,-330.127],[642.336,-327.964],[625.337,-310.964],[540.616,-310.964],[523.616,-327.964],[523.616,-330.127],[540.616,-347.127],[625.337,-347.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0.167},"t":256,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[642.336,-330.127],[642.336,-327.964],[625.337,-310.964],[482.616,-310.964],[465.616,-327.964],[465.616,-330.127],[482.616,-347.127],[625.337,-347.127]],"c":true}]},{"t":270.000010997325,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[642.336,-330.127],[642.336,-327.964],[625.337,-310.964],[540.616,-310.964],[523.616,-327.964],[523.616,-330.127],[540.616,-347.127],[625.337,-347.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[0]},{"t":43.0000017514259,"s":[100]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":20,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Группа 1","np":1,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":0,"nm":"Back","refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[555,540,0],"ix":2,"l":2},"a":{"a":0,"k":[555,540,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":1110,"h":1080,"ip":0,"op":3896.00015868733,"st":0,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Слой-фигура 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":0,"s":[540,823,0],"to":[0,-44.5,0],"ti":[0,44.5,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.84,"y":0.84},"t":30,"s":[540,556,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[540,556,0],"to":[0,1.5,0],"ti":[0,-1.5,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.84,"y":0.84},"t":150,"s":[540,565,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":210,"s":[540,565,0],"to":[0,-1.5,0],"ti":[0,1.5,0]},{"t":239.00000973467,"s":[540,556,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":24,"s":[100,100,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":120,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":150,"s":[137,137,100]},{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":210,"s":[137,137,100]},{"t":239.00000973467,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":126,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[454.368,-278.924],[454.368,278.924],[424.368,308.924],[-424.368,308.924],[-454.368,278.924],[-454.368,-278.924],[-424.368,-308.924],[424.368,-308.924]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.84,"y":0},"t":150,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[214.368,-278.924],[214.368,278.924],[184.368,308.924],[-200.368,308.924],[-230.368,278.924],[-230.368,-278.924],[-200.368,-308.924],[184.368,-308.924]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":210,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[214.368,-278.924],[214.368,278.924],[184.368,308.924],[-200.368,308.924],[-230.368,278.924],[-230.368,-278.924],[-200.368,-308.924],[184.368,-308.924]],"c":true}]},{"t":239.00000973467,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[454.368,-278.924],[454.368,278.924],[424.368,308.924],[-424.368,308.924],[-454.368,278.924],[-454.368,-278.924],[-424.368,-308.924],[424.368,-308.924]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14,-7],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Слой-фигура 18","sr":1,"ks":{"o":{"a":0,"k":50,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":229,"s":[0]},{"t":270.000010997325,"s":[5]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":0,"s":[540,823,0],"to":[0,-44.5,0],"ti":[0,44.5,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.973,"y":0},"t":30,"s":[540,556,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.973,"y":0.973},"t":58,"s":[540,476,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.973,"y":0},"t":120,"s":[540,476,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0},"t":135,"s":[540,559,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.167,"y":0.167},"t":166,"s":[342,565,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0},"t":200,"s":[342,565,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":229,"s":[539,612.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":270.000010997325,"s":[539,443,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.232,0.232,0.232],"y":[0,0,0]},"t":24,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":58,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":119,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":166,"s":[124,124,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":200,"s":[124,124,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":229,"s":[50,50,100]},{"t":270.000010997325,"s":[89,89,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":122,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[454.368,-278.924],[454.368,278.924],[424.368,308.924],[-424.368,308.924],[-454.368,278.924],[-454.368,-278.924],[-424.368,-308.924],[424.368,-308.924]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.84,"y":0},"t":146,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[214.368,-278.924],[214.368,278.924],[184.368,308.924],[-200.368,308.924],[-230.368,278.924],[-230.368,-278.924],[-200.368,-308.924],[184.368,-308.924]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":206,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[214.368,-278.924],[214.368,278.924],[184.368,308.924],[-200.368,308.924],[-230.368,278.924],[-230.368,-278.924],[-200.368,-308.924],[184.368,-308.924]],"c":true}]},{"t":235.000009571746,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[454.368,-278.924],[454.368,278.924],[424.368,308.924],[-424.368,308.924],[-454.368,278.924],[-454.368,-278.924],[-424.368,-308.924],[424.368,-308.924]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":20,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14,-7],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Слой-фигура 16","sr":1,"ks":{"o":{"a":0,"k":49,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":229,"s":[0]},{"i":{"x":[0.34],"y":[1]},"o":{"x":[0.66],"y":[0]},"t":240,"s":[0]},{"t":281.000011445365,"s":[11]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":0,"s":[540,823,0],"to":[0,-44.5,0],"ti":[0,44.5,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.973,"y":0},"t":30,"s":[540,556,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.973,"y":0.973},"t":69,"s":[540,396,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.973,"y":0},"t":120,"s":[540,396,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0},"t":135,"s":[540,587,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.167,"y":0.167},"t":165,"s":[739,565,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0},"t":200,"s":[739,565,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.16,"y":0.16},"o":{"x":0.66,"y":0.66},"t":229,"s":[538,612.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":240,"s":[538,612.5,0],"to":[0,0,0],"ti":[0,0,0]},{"t":281.000011445365,"s":[562,328,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.232,0.232,0.232],"y":[0,0,0]},"t":24,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":69,"s":[82,82,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":120,"s":[82,82,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":165,"s":[124,124,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":200,"s":[124,124,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":229,"s":[50,50,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":240,"s":[50,50,100]},{"t":281.000011445365,"s":[73,73,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[454.368,-278.924],[454.368,278.924],[424.368,308.924],[-424.368,308.924],[-454.368,278.924],[-454.368,-278.924],[-424.368,-308.924],[424.368,-308.924]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.84,"y":0},"t":144,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[214.368,-278.924],[214.368,278.924],[184.368,308.924],[-200.368,308.924],[-230.368,278.924],[-230.368,-278.924],[-200.368,-308.924],[184.368,-308.924]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":204,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[214.368,-278.924],[214.368,278.924],[184.368,308.924],[-200.368,308.924],[-230.368,278.924],[-230.368,-278.924],[-200.368,-308.924],[184.368,-308.924]],"c":true}]},{"t":233.000009490285,"s":[{"i":[[0,-16.569],[0,0],[16.569,0],[0,0],[0,16.569],[0,0],[-16.569,0],[0,0]],"o":[[0,0],[0,16.569],[0,0],[-16.569,0],[0,0],[0,-16.569],[0,0],[16.569,0]],"v":[[454.368,-278.924],[454.368,278.924],[424.368,308.924],[-424.368,308.924],[-454.368,278.924],[-454.368,-278.924],[-424.368,-308.924],[424.368,-308.924]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":20,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14,-7],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0}]}],"fonts":{"list":[{"fName":"Roboto-Black","fFamily":"Roboto","fStyle":"Black","ascent":75},{"fName":"Roboto-Regular","fFamily":"Roboto","fStyle":"Regular","ascent":75}]},"layers":[{"ddd":0,"ind":1,"ty":0,"nm":"Total","refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.34,"y":0.34},"o":{"x":0.66,"y":0.66},"t":0,"s":[555,540,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":41,"s":[555,540,0],"to":[0,6.667,0],"ti":[0,-6.667,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.167,"y":0.167},"t":73,"s":[555,580,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0},"t":117,"s":[555,580,0],"to":[0,-6,0],"ti":[0,6,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.167,"y":0.167},"t":168,"s":[555,544,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":0.34},"o":{"x":0.167,"y":0.167},"t":197,"s":[555,544,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.167,"y":0},"t":251,"s":[555,544,0],"to":[0,8.667,0],"ti":[0,-8.667,0]},{"t":274.000011160249,"s":[555,596,0]}],"ix":2,"l":2},"a":{"a":0,"k":[555,540,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.34,0.34,0.34],"y":[1,1,1]},"o":{"x":[0.66,0.66,0.66],"y":[0,0,0]},"t":41,"s":[89,89,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":73,"s":[105,105,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":117,"s":[105,105,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":143,"s":[91,91,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":166,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":197,"s":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":251,"s":[89,89,100]},{"t":272.000011078787,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"w":1110,"h":1080,"ip":0,"op":3896.00015868733,"st":0,"bm":0}],"markers":[],"chars":[{"ch":"0","size":130,"style":"Black","w":57.91,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[4.329,5.241],[7.877,0],[4.313,-5.208],[0,-9.602],[0,0],[-4.33,-5.241],[-7.91,0],[-4.314,5.209],[0,9.603]],"o":[[0,-9.57],[-4.33,-5.241],[-7.878,0],[-4.314,5.209],[0,0],[0,9.538],[4.329,5.241],[7.845,0],[4.313,-5.208],[0,0]],"v":[[53.711,-41.992],[47.217,-64.209],[28.906,-72.07],[10.62,-64.258],[4.15,-42.041],[4.15,-29.053],[10.645,-6.885],[29.004,0.977],[47.241,-6.836],[53.711,-29.053]],"c":true},"ix":2},"nm":"0","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[1.334,-2.393],[2.799,0],[1.334,2.458],[0,5.306],[0,0],[-1.286,2.344],[-2.832,0],[-1.335,-2.522],[0,-5.11]],"o":[[-0.033,5.111],[-1.335,2.393],[-2.898,0],[-1.335,-2.457],[0,0],[0.098,-4.752],[1.286,-2.344],[2.897,0],[1.334,2.523],[0,0]],"v":[[37.256,-26.563],[35.205,-15.308],[29.004,-11.719],[22.656,-15.405],[20.654,-27.051],[20.654,-45.215],[22.729,-55.859],[28.906,-59.375],[35.254,-55.591],[37.256,-44.141]],"c":true},"ix":2},"nm":"0","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"0","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"M","size":51,"style":"Regular","w":87.3,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[8.252,-71.094],[8.252,0],[17.627,0],[17.627,-27.686],[16.699,-57.422],[39.99,0],[47.168,0],[70.508,-57.568],[69.629,-27.686],[69.629,0],[79.004,0],[79.004,-71.094],[66.846,-71.094],[43.604,-13.086],[20.361,-71.094]],"c":true},"ix":2},"nm":"M","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"M","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"Y","size":51,"style":"Regular","w":60.94,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[11.475,-71.094],[0.732,-71.094],[25.244,-26.514],[25.244,0],[34.619,0],[34.619,-26.514],[59.131,-71.094],[48.486,-71.094],[29.932,-35.4]],"c":true},"ix":2},"nm":"Y","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"Y","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"S","size":51,"style":"Regular","w":59.33,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.441,-1.904],[0,-3.483],[2.637,-2.018],[4.948,0],[3.174,2.49],[0,4.297],[0,0],[-2.295,-3.255],[-4.232,-1.871],[-4.883,0],[-4.492,3.467],[0,5.762],[1.611,2.67],[3.336,2.002],[5.908,1.663],[2.457,2.006],[0,2.903],[-2.588,2.039],[-4.623,0],[-2.734,-2.466],[0,-4.312],[0,0],[2.1,3.32],[3.825,1.888],[4.883,0],[4.475,-3.662],[0,-5.598],[-3.662,-3.369],[-8.041,-2.311]],"o":[[2.441,1.904],[0,3.484],[-2.637,2.019],[-5.306,0],[-3.174,-2.49],[0,0],[0,4.134],[2.295,3.255],[4.231,1.871],[7.52,0],[4.492,-3.467],[0,-3.613],[-1.611,-2.669],[-3.337,-2.002],[-5.908,-1.663],[-2.458,-2.005],[0,-3.619],[2.588,-2.039],[4.98,0],[2.734,2.467],[0,0],[0,-3.938],[-2.1,-3.32],[-3.825,-1.888],[-7.162,0],[-4.476,3.662],[0,4.948],[3.662,3.369],[6.413,1.855]],"v":[[42.48,-26.05],[46.143,-17.969],[42.188,-9.717],[30.811,-6.689],[18.091,-10.425],[13.33,-20.605],[3.906,-20.605],[7.349,-9.521],[17.139,-1.831],[30.811,0.977],[48.828,-4.224],[55.566,-18.066],[53.149,-27.49],[45.728,-34.497],[31.86,-39.995],[19.312,-45.498],[15.625,-52.86],[19.507,-61.346],[30.322,-64.404],[41.895,-60.705],[45.996,-50.537],[55.42,-50.537],[52.271,-61.426],[43.384,-69.238],[30.322,-72.07],[12.866,-66.577],[6.152,-52.686],[11.646,-40.21],[29.199,-31.689]],"c":true},"ix":2},"nm":"S","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"S","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"},{"ch":"T","size":51,"style":"Regular","w":60.45,"data":{"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[57.373,-71.094],[2.393,-71.094],[2.393,-63.428],[25.195,-63.428],[25.195,0],[34.521,0],[34.521,-63.428],[57.373,-63.428]],"c":true},"ix":2},"nm":"T","mn":"ADBE Vector Shape - Group","hd":false}],"nm":"T","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}]},"fFamily":"Roboto"}]}


================================================
FILE: src/app/onboarding/components/IdentitySetup/IdentitySetup.tsx
================================================
/**
 * Copyright (c) 2021 BlockDev AG
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
import { observer } from "mobx-react-lite"
import React, { useState } from "react"
import { faFileImport, faIdCardAlt, faUserPlus } from "@fortawesome/free-solid-svg-icons"
import styled from "styled-components"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import Lottie from "react-lottie-player"
import toast from "react-hot-toast"

import { ViewContainer } from "../../../navigation/components/ViewContainer/ViewContainer"
import { ViewSplit } from "../../../navigation/components/ViewSplit/ViewSplit"
import { ViewSidebar } from "../../../navigation/components/ViewSidebar/ViewSidebar"
import { ViewContent } from "../../../navigation/components/ViewContent/ViewContent"
import { ViewNavBar } from "../../../navigation/components/ViewNavBar/ViewNavBar"
import { Heading2, Small } from "../../../ui-kit/typography"
import {
    ButtonContent,
    ButtonIcon,
    PrimarySidebarActionButton,
    SecondarySidebarActionButton,
} from "../../../ui-kit/components/Button/SidebarButtons"
import { useStores } from "../../../store"
import { ImportIdentityFormFields, ImportIdentityPrompt } from "../../../views/common/Settings/ImportIdentityPrompt"
import { brandLight } from "../../../ui-kit/colors"

import animationIdentity from "./animation_identity.json"

const SideTop = styled.div`
    box-sizing: border-box;
    height: 136px;
    padding: 20px;
    overflow: hidden;
    text-align: center;
`

const SectionIcon = styled(FontAwesomeIcon)`
    margin-bottom: 15px;
    font-size: 20px;
    color: ${brandLight};
`

const Title = styled(Heading2)`
    margin-bottom: 15px;
`

const SideBot = styled.div`
    background: #fff;
    box-shadow: 0px 0px 30px rgba(11, 0, 75, 0.1);
    border-radius: 10px;
    box-sizing: border-box;
    padding: 20px;
    flex: 1 0 auto;

    display: flex;
    flex-direction: column;
`

const Content = styled(ViewContent)`
    background: none;
`

export const IdentitySetup: React.FC = observer(function IdentitySetup() {
    const { onboarding, identity } = useStores()

    const handleCreateNew = async () => {
        await onboarding.createNewID()
    }
    const [importPrompt, setImportPrompt] = useState(false)
    const [importFilename, setImportFilename] = useState("")
    const handleImportExisting = async () => {
        const filename = await identity.importIdentityChooseFile()
        if (!filename) {
            return
        }
        setImportFilename(filename)
        setImportPrompt(true)
    }
    const handleImportSubmit = async ({ passphrase }: ImportIdentityFormFields) => {
        setImportPrompt(false)
        const res = identity.importIdentity({ filename: importFilename, passphrase })
        toast
            .promise(res, {
                loading: "Importing identity...",
                success: function successToast() {
                    return (
                        <span>
                            <b>Mysterium ID imported!</b>
                        </span>
                    )
                },
                error: function errorToast(reason) {
                    return (
                        <span>
                            <b>Mysterium ID import failed 😶</b>
                            <br />
                            Error: {reason}
                        </span>
                    )
                },
            })
            .then(() => onboarding.finishIDSetup())
    }
    const handleImportCancel = () => {
        setImportPrompt(false)
    }
    return (
        <ViewContainer>
            <ViewNavBar />
            <ViewSplit>
                <ViewSidebar>
                    <SideTop>
                        <SectionIcon icon={faIdCardAlt} />
                        <Title>Mysterium ID</Title>
                        <Small>Your anonymous keys to access Mysterium Network.</Small>
                    </SideTop>
                    <SideBot>
                        <PrimarySidebarActionButton onClick={handleCreateNew}>
                            <ButtonContent>
                                <ButtonIcon>
                                    <FontAwesomeIcon icon={faUserPlus} />
                                </ButtonIcon>
                                Create New
                            </ButtonContent>
                        </PrimarySidebarActionButton>
                        <SecondarySidebarActionButton onClick={handleImportExisting}>
                            <ButtonContent>
                                <ButtonIcon>
                                    <FontAwesomeIcon icon={faFileImport} />
                                </ButtonIcon>
                                Import existing
                            </ButtonContent>
                        </SecondarySidebarActionButton>
                    </SideBot>
                </ViewSidebar>
                <Content>
                    <Lottie
                        play
                        loop={false}
                        animationData={animationIdentity}
                        style={{ width: 256, height: 256 }}
                        renderer="svg"
                    />
                </Content>
            </ViewSplit>
            <ImportIdentityPrompt visible={importPrompt} onSubmit={handleImportSubmit} onCancel={handleImportCancel} />
        </ViewContainer>
    )
})


================================================
FILE: src/app/onboarding/components/IdentitySetup/animation_identity.json
================================================
{"v":"5.7.4","fr":29.9700012207031,"ip":0,"op":117.000004765508,"w":1110,"h":1080,"nm":"Композиция 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":5,"nm":"MYST","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":226,"s":[558.714,785.871,0],"to":[0,-14,0],"ti":[0,14,0]},{"t":263.00001071221,"s":[558.714,701.871,0]}],"ix":2,"l":2},"a":{"a":0,"k":[68.892,-18.129,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":226,"s":[0,0,100]},{"t":259.000010549286,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":51,"f":"Roboto-Regular","t":"MYST","j":0,"tr":0,"lh":61.2,"ls":0,"fc":[0.929,0.357,0.675]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":5,"nm":"0000","sr":1,"ks":{"o":{"a":0,"k":20,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":221,"s":[558.714,737.789,0],"to":[0,-21.667,0],"ti":[0,21.667,0]},{"t":261.000010630748,"s":[558.714,607.789,0]}],"ix":2,"l":2},"a":{"a":0,"k":[150.535,-46.211,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16,0.16],"y":[1,1,1]},"o":{"x":[0.84,0.84,0.84],"y":[0,0,0]},"t":221,"s":[0,0,100]},{"t":257.000010467825,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"t":{"d":{"k":[{"s":{"s":130,"f":"Roboto-Black","t":"0000","j":0,"tr":0,"lh":156,"ls":0,"fc":[1,1,1]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Кривые Key","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[334,452,0],"ix":2,"l":2},"a":{"a":0,"k":[34,34.5,0],"ix":1,"l":2},"s":{"a":0,"k":[345.1,345.1,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[3.53,-3.53],[2.419,0],[1.71,1.709],[0,2.418],[-1.711,1.71],[-2.419,0],[-1.71,-1.71]],"o":[[-1.71,1.709],[-2.419,0],[-1.711,-1.711],[0,-2.419],[1.709,-1.71],[2.418,0],[3.53,3.531]],"v":[[18.413,-5.602],[12.011,-2.951],[5.608,-5.602],[2.956,-12.005],[5.608,-18.408],[12.011,-21.06],[18.413,-18.408]],"c":true},"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[8.575,8.575],[5.873,0],[4.153,-4.153],[0,-5.874],[-1.152,-2.695],[0,0],[0.055,-0.619],[0,0],[-0.555,-0.554],[-0.782,0.069],[0,0],[-0.44,0.439],[0,0],[0.005,0.077],[0,0],[-0.262,0.262],[-0.372,-0.023],[0,0],[-0.073,-0.018],[0,0],[0.005,0.076],[0,0],[-0.263,0.262],[-0.371,-0.024],[0,0],[-0.073,-0.018],[0,0],[-3.02,0],[-4.153,4.152]],"o":[[-4.153,-4.153],[-5.874,0],[-4.153,4.154],[0,3.02],[0,0],[-0.441,0.439],[0,0],[-0.07,0.783],[0.555,0.556],[0,0],[0.619,-0.055],[0,0],[-0.017,-0.072],[0,0],[-0.025,-0.369],[0.262,-0.261],[0,0],[0.077,0.006],[0,0],[-0.018,-0.072],[0,0],[-0.025,-0.37],[0.261,-0.263],[0,0],[0.076,0.005],[0,0],[2.695,1.153],[5.874,0],[8.574,-8.574]],"v":[[27.559,-27.555],[12.011,-33.996],[-3.539,-27.555],[-9.98,-12.005],[-8.219,-3.365],[-32.447,20.864],[-33.214,22.502],[-33.981,31.109],[-33.215,33.219],[-31.105,33.986],[-22.498,33.219],[-20.858,32.453],[-18.249,29.842],[-18.284,29.619],[-18.712,23.381],[-18.34,22.387],[-17.346,22.015],[-11.108,22.442],[-10.884,22.478],[-8.836,20.43],[-8.872,20.207],[-9.3,13.969],[-8.927,12.975],[-7.933,12.602],[-1.695,13.03],[-1.472,13.066],[3.371,8.222],[12.012,9.983],[27.56,3.544]],"c":true},"ix":2},"nm":"Контур 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Объединить контуры 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"mm","mm":4,"nm":"Объединить контуры 2","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":120,"s":[34.991,36.015],"to":[10,-1.833],"ti":[-10,1.833]},{"i":{"x":0.53,"y":0.53},"o":{"x":1.019,"y":1.019},"t":150,"s":[94.991,25.015],"to":[0,0],"ti":[0,0]},{"i":{"x":0.34,"y":1},"o":{"x":0.66,"y":0},"t":210,"s":[94.991,25.015],"to":[0,0],"ti":[0,0]},{"t":239.00000973467,"s":[15.991,25.015]}],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.16,0.16],"y":[1,1]},"o":{"x":[0.84,0.84],"y":[0,0]},"t":120,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":158,"s":[119,119]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":180,"s":[98,98]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":210,"s":[98,98]},{"t":239.00000973467,"s":[0,0]}],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.16],"y":[1]},"o":{"x":[0.84],"y":[0]},"t":120,"s":[-90]},{"t":158.000006435472,"s":[0]}],"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Группа 1","np":5,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Слой-фигура 10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,338.919,0],"ix":2,"l":2},"a":{"a":0,"k":[41.759,-208.081,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":17,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-74.227,-10.162],[-74.227,5],[-91.227,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[-91.227,-27.162]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":47,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[215.773,-10.162],[215.773,5],[198.773,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[198.773,-27.162]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":74,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[159.773,-10.162],[159.773,5],[142.773,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[142.773,-27.162]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[215.773,-10.162],[215.773,5],[198.773,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[198.773,-27.162]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-74.227,-10.162],[-74.227,5],[-91.227,22],[-87.241,22],[-104.241,5],[-104.241,-10.162],[-87.241,-27.162],[-91.227,-27.162]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[22,-204.5],"ix":2},"a":{"a":0,"k":[-124,1],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":24,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Слой-фигура 9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,415.955,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":22,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":52,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[158.336,-8.127],[158.336,7.036],[141.336,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[141.336,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[236.336,-8.127],[236.336,7.036],[219.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[219.337,-25.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[158.336,-8.127],[158.336,7.036],[141.336,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[141.336,-25.127]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":28,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Слой-фигура 11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,489.955,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":39,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":69,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[186.336,-8.127],[186.336,7.036],[169.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[169.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[140.336,-8.127],[140.336,7.036],[123.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[123.337,-25.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[186.336,-8.127],[186.336,7.036],[169.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[169.337,-25.127]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":10,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":39,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":44,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Слой-фигура 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[578.259,567.15,0],"ix":2,"l":2},"a":{"a":0,"k":[53.613,-208.045,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.16,"y":1},"o":{"x":0.84,"y":0},"t":43,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0},"t":73,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[121.337,-8.127],[121.337,7.036],[104.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[104.337,-25.127]],"c":true}]},{"i":{"x":0.16,"y":1},"o":{"x":0.167,"y":0.167},"t":90,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[209.336,-8.127],[209.336,7.036],[192.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[192.337,-25.127]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":120,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[121.337,-8.127],[121.337,7.036],[104.337,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[104.337,-25.127]],"c":true}]},{"t":135.000005498663,"s":[{"i":[[0,-9.389],[0,0],[9.389,0],[0,0],[0,9.389],[0,0],[-9.389,0],[0,0]],"o":[[0,0],[0,9.389],[0,0],[-9.389,0],[0,0],[0,-9.389],[0,0],[9.389,0]],"v":[[-66.664,-8.127],[-66.664,7.036],[-83.664,24.036],[-84.387,24.036],[-101.387,7.036],[-101.387,-8.127],[-84.387,-25.127],[-83.664,-25.127]],"c":true}]}],"ix":2},"nm":"Контур 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":10,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Обводка 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":5,"ix":5},"r":1,"bm":0,"nm":"Заливка 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54,-207.5],"ix":2},"a":{"a":0,"k":[-101,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":43,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":48,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":128,"s":[100]},{"t":135.000005498663,"s":[0]}],"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Преобразовать"}],"nm":"Прямоугольник 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":3896.00015868733,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Слой-фигура 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[326,452,0],"ix":2,"l":2},"a":{"a":0,"k":[-214,-88,0],"ix":1,"l":2},"s":{"a":0,"k":[96.367,96.3
Download .txt
gitextract_p7zwqvqt/

├── .eslintrc
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── stale.yml
│   └── workflows/
│       ├── lint.yml
│       └── release.yml
├── .gitignore
├── .prettierrc
├── .yarnclean
├── LICENSE
├── README.md
├── assets/
│   ├── README.md
│   └── backgrounds/
│       ├── identity-bg.xcf
│       └── terms-bg.xcf
├── build/
│   ├── background.tiff
│   ├── entitlements.mac.plist
│   └── nsis/
│       └── customize.nsi
├── ci/
│   ├── afterPack.js
│   └── notarize.js
├── docs/
│   ├── DEV_GUIDE.md
│   ├── DOCS_CONTRIBUTING
│   ├── myst-supervisor.mmd
│   └── node-tequilapi.mmd
├── monkey-patch-crypto.js
├── package.json
├── sentry-symbols.js
├── sentry.properties
├── src/
│   ├── app/
│   │   ├── .eslintrc
│   │   ├── analytics/
│   │   │   ├── analytics.ts
│   │   │   └── event.ts
│   │   ├── config/
│   │   │   ├── filters.ts
│   │   │   └── store.ts
│   │   ├── connection/
│   │   │   ├── components/
│   │   │   │   ├── ConnectButton/
│   │   │   │   │   └── ConnectButton.tsx
│   │   │   │   └── DisconnectButton/
│   │   │   │       └── DisconnectButton.tsx
│   │   │   ├── status.ts
│   │   │   └── store.ts
│   │   ├── daemon/
│   │   │   ├── components/
│   │   │   │   ├── AppVersion.tsx
│   │   │   │   └── StartupLoadingView/
│   │   │   │       └── StartupLoadingView.tsx
│   │   │   └── store.ts
│   │   ├── feedback/
│   │   │   └── store.ts
│   │   ├── identity/
│   │   │   ├── components/
│   │   │   │   ├── IdentityRegistrationView/
│   │   │   │   │   └── IdentityRegistrationView.tsx
│   │   │   │   └── IdentityUpgradeView/
│   │   │   │       └── IdentityUpgradeView.tsx
│   │   │   ├── identity.ts
│   │   │   └── store.ts
│   │   ├── index.tsx
│   │   ├── location/
│   │   │   ├── components/
│   │   │   │   ├── CurrentIP/
│   │   │   │   │   └── CurrentIP.tsx
│   │   │   │   ├── Flag/
│   │   │   │   │   └── Flag.tsx
│   │   │   │   └── ProtectionStatus/
│   │   │   │       └── ProtectionStatus.tsx
│   │   │   ├── countries.ts
│   │   │   └── states.ts
│   │   ├── navigation/
│   │   │   ├── components/
│   │   │   │   ├── Routes/
│   │   │   │   │   └── Routes.tsx
│   │   │   │   ├── TitleBar/
│   │   │   │   │   ├── NakedTitleBar.tsx
│   │   │   │   │   ├── TitleBar.tsx
│   │   │   │   │   ├── WindowButtonsLinux.tsx
│   │   │   │   │   └── WindowButtonsWindows.tsx
│   │   │   │   ├── ViewContainer/
│   │   │   │   │   └── ViewContainer.tsx
│   │   │   │   ├── ViewContent/
│   │   │   │   │   └── ViewContent.tsx
│   │   │   │   ├── ViewNavBar/
│   │   │   │   │   └── ViewNavBar.tsx
│   │   │   │   ├── ViewSidebar/
│   │   │   │   │   └── ViewSidebar.tsx
│   │   │   │   └── ViewSplit/
│   │   │   │       └── ViewSplit.tsx
│   │   │   ├── locations.ts
│   │   │   └── store.ts
│   │   ├── onboarding/
│   │   │   ├── components/
│   │   │   │   ├── IdentityBackup/
│   │   │   │   │   ├── IdentityBackup.tsx
│   │   │   │   │   └── animation_identity_keys.json
│   │   │   │   ├── IdentitySetup/
│   │   │   │   │   ├── IdentitySetup.tsx
│   │   │   │   │   └── animation_identity.json
│   │   │   │   ├── InitialTopup/
│   │   │   │   │   ├── InitialTopup.tsx
│   │   │   │   │   ├── UseReferralCodePrompt.tsx
│   │   │   │   │   └── animation_onboarding_topup.json
│   │   │   │   ├── IntroductionSteps/
│   │   │   │   │   ├── IntroductionSteps.tsx
│   │   │   │   │   ├── Step1.tsx
│   │   │   │   │   ├── Step2.tsx
│   │   │   │   │   ├── Step3.tsx
│   │   │   │   │   ├── Step4.tsx
│   │   │   │   │   ├── animation_crypto.json
│   │   │   │   │   ├── animation_network.json
│   │   │   │   │   ├── animation_payasyougo.json
│   │   │   │   │   └── animation_privacy.json
│   │   │   │   └── Welcome/
│   │   │   │       └── Welcome.tsx
│   │   │   └── store.ts
│   │   ├── payment/
│   │   │   ├── components/
│   │   │   │   ├── SelectTaxCountry/
│   │   │   │   │   └── SelectTaxCountry.tsx
│   │   │   │   └── SelectTaxState/
│   │   │   │       └── SelectTaxState.tsx
│   │   │   ├── currency.ts
│   │   │   ├── display.ts
│   │   │   ├── methods.ts
│   │   │   ├── rate.ts
│   │   │   └── store.ts
│   │   ├── proposals/
│   │   │   ├── components/
│   │   │   │   ├── CountryFilter/
│   │   │   │   │   └── CountryFilter.tsx
│   │   │   │   ├── Preset/
│   │   │   │   │   └── Preset.tsx
│   │   │   │   ├── ProposalQuality/
│   │   │   │   │   └── ProposalQuality.tsx
│   │   │   │   ├── ProposalTable/
│   │   │   │   │   ├── ProposalTable.tsx
│   │   │   │   │   └── RowRenderer.tsx
│   │   │   │   ├── QualityFilter/
│   │   │   │   │   └── QualityFilter.tsx
│   │   │   │   └── SelectedProposal/
│   │   │   │       └── SelectedProposal.tsx
│   │   │   ├── store.ts
│   │   │   └── uiProposal.ts
│   │   ├── referral/
│   │   │   └── store.ts
│   │   ├── storage/
│   │   │   └── localStorage.ts
│   │   ├── store.ts
│   │   ├── tequilapi/
│   │   │   └── index.ts
│   │   ├── ui-kit/
│   │   │   ├── colors.ts
│   │   │   ├── components/
│   │   │   │   ├── Anchor.tsx
│   │   │   │   ├── Button/
│   │   │   │   │   ├── BrandButton.tsx
│   │   │   │   │   ├── CancelButton.tsx
│   │   │   │   │   ├── GhostButton.tsx
│   │   │   │   │   ├── LightButton.tsx
│   │   │   │   │   ├── OutlineButton.tsx
│   │   │   │   │   ├── RippleButton.tsx
│   │   │   │   │   ├── SecondaryButton.tsx
│   │   │   │   │   └── SidebarButtons.tsx
│   │   │   │   ├── Clipboard/
│   │   │   │   │   └── Clipboard.tsx
│   │   │   │   ├── CryptoAnimation/
│   │   │   │   │   ├── CryptoAnimation.tsx
│   │   │   │   │   ├── animation_btc.json
│   │   │   │   │   ├── animation_dai.json
│   │   │   │   │   ├── animation_doge.json
│   │   │   │   │   ├── animation_eth.json
│   │   │   │   │   ├── animation_ltc.json
│   │   │   │   │   ├── animation_myst.json
│   │   │   │   │   └── animation_usdt.json
│   │   │   │   ├── LogoTitle/
│   │   │   │   │   └── LogoTitle.tsx
│   │   │   │   ├── MysteriumVPN2Toast/
│   │   │   │   │   └── MysteriumVPN2Toast.tsx
│   │   │   │   ├── Prompt/
│   │   │   │   │   └── Prompt.tsx
│   │   │   │   ├── QR/
│   │   │   │   │   └── QR.tsx
│   │   │   │   ├── SectionTitle/
│   │   │   │   │   └── SectionTitle.tsx
│   │   │   │   ├── Spinner/
│   │   │   │   │   ├── Spinner.tsx
│   │   │   │   │   └── animation_spinner.json
│   │   │   │   ├── StepProgressBar/
│   │   │   │   │   └── StepProgressBar.tsx
│   │   │   │   ├── Toggle/
│   │   │   │   │   └── Toggle.tsx
│   │   │   │   └── dismissibleToast.tsx
│   │   │   ├── form-components/
│   │   │   │   ├── Checkbox/
│   │   │   │   │   └── Checkbox.tsx
│   │   │   │   ├── Search.tsx
│   │   │   │   ├── Select.tsx
│   │   │   │   ├── TextArea.tsx
│   │   │   │   └── TextInput.tsx
│   │   │   ├── icons/
│   │   │   │   ├── IconBrowsing.tsx
│   │   │   │   ├── IconCloudDownload.tsx
│   │   │   │   ├── IconCopy.tsx
│   │   │   │   ├── IconDocument.tsx
│   │   │   │   ├── IconDownload.tsx
│   │   │   │   ├── IconDuration.tsx
│   │   │   │   ├── IconGlobe.tsx
│   │   │   │   ├── IconIdentity.tsx
│   │   │   │   ├── IconMedia.tsx
│   │   │   │   ├── IconMusic.tsx
│   │   │   │   ├── IconMystToken.tsx
│   │   │   │   ├── IconNoPreset.tsx
│   │   │   │   ├── IconPaid.tsx
│   │   │   │   ├── IconPerson.tsx
│   │   │   │   ├── IconPlay.tsx
│   │   │   │   ├── IconPriceTier.tsx
│   │   │   │   ├── IconReceived.tsx
│   │   │   │   ├── IconSent.tsx
│   │   │   │   ├── IconSettings.tsx
│   │   │   │   ├── IconWallet.tsx
│   │   │   │   └── Props.tsx
│   │   │   └── typography.ts
│   │   └── views/
│   │       ├── common/
│   │       │   ├── AcceptTerms/
│   │       │   │   └── AcceptTermsView.tsx
│   │       │   ├── Help/
│   │       │   │   ├── HelpContentReportIssue.tsx
│   │       │   │   ├── HelpContentTermsAndConditions.tsx
│   │       │   │   └── HelpView.tsx
│   │       │   ├── Loading/
│   │       │   │   ├── LoadingView.tsx
│   │       │   │   ├── animation_loading_loop.json
│   │       │   │   └── animation_loading_start.json
│   │       │   └── Settings/
│   │       │       ├── ExportIdentityPrompt.tsx
│   │       │       ├── ImportIdentityPrompt.tsx
│   │       │       ├── SettingsConnection.tsx
│   │       │       ├── SettingsFilters.tsx
│   │       │       ├── SettingsMysteriumId.tsx
│   │       │       └── SettingsView.tsx
│   │       └── consumer/
│   │           ├── Connected/
│   │           │   ├── ConnectedView.tsx
│   │           │   ├── ConnectionProposal.tsx
│   │           │   ├── ConnectionStatistics.tsx
│   │           │   ├── animation_connected_loop.json
│   │           │   ├── animation_connecting_loop.json
│   │           │   └── animation_connecting_start.json
│   │           ├── Proposals/
│   │           │   ├── ManualConnectView.tsx
│   │           │   ├── ProposalSearch.tsx
│   │           │   ├── QuickConnectView.tsx
│   │           │   ├── SwitchConnectView.tsx
│   │           │   └── animation_quick_connect.json
│   │           ├── Referral/
│   │           │   └── ReferralView.tsx
│   │           ├── Topup/
│   │           │   ├── TopupChooseMethod.tsx
│   │           │   ├── TopupFailed.tsx
│   │           │   ├── TopupRoutes.tsx
│   │           │   ├── TopupSuccess.tsx
│   │           │   ├── coingate/
│   │           │   │   ├── CoingateOrderSummary.tsx
│   │           │   │   ├── CoingatePaymentOptions.tsx
│   │           │   │   ├── CoingateSelectAmount.tsx
│   │           │   │   ├── CoingateWaitingForPayment.tsx
│   │           │   │   └── LogoCoingate.tsx
│   │           │   ├── common/
│   │           │   │   ├── OptionLabel.tsx
│   │           │   │   ├── OptionValue.tsx
│   │           │   │   └── OrderBreakdown.tsx
│   │           │   ├── myst/
│   │           │   │   ├── MystChooseChain.tsx
│   │           │   │   ├── MystPolygonWaitingForPayment.tsx
│   │           │   │   └── MystSelectAmount.tsx
│   │           │   ├── paypal/
│   │           │   │   ├── LogoPaypal.tsx
│   │           │   │   ├── PaypalOrderSummary.tsx
│   │           │   │   ├── PaypalPaymentOptions.tsx
│   │           │   │   ├── PaypalSelectAmount.tsx
│   │           │   │   └── PaypalWaitingForPayment.tsx
│   │           │   └── stripe/
│   │           │       ├── LogoStripe.tsx
│   │           │       ├── StripeOrderSummary.tsx
│   │           │       ├── StripePaymentOptions.tsx
│   │           │       ├── StripeSelectAmount.tsx
│   │           │       └── StripeWaitingForPayment.tsx
│   │           └── Wallet/
│   │               └── WalletView.tsx
│   ├── config.ts
│   ├── main/
│   │   ├── .eslintrc
│   │   ├── cliFlags.tsx
│   │   ├── index.tsx
│   │   ├── menu.ts
│   │   ├── node/
│   │   │   ├── mysteriumNode.ts
│   │   │   ├── supervisor.ts
│   │   │   └── tequila.ts
│   │   └── tray.ts
│   ├── shared/
│   │   ├── errors/
│   │   │   ├── parseError.ts
│   │   │   └── sentry.ts
│   │   ├── ipc.ts
│   │   ├── log/
│   │   │   └── log.ts
│   │   ├── node/
│   │   │   ├── mysteriumNodeIPC.ts
│   │   │   └── supervisorIPC.ts
│   │   └── push/
│   │       └── topics.ts
│   ├── typings/
│   │   ├── assets.d.ts
│   │   ├── libraries.d.ts
│   │   └── react-table-config.d.ts
│   └── utils/
│       ├── env.ts
│       ├── handleProcessExit.ts
│       ├── paths.ts
│       ├── spawn.ts
│       ├── sudo.ts
│       └── user.ts
├── static/
│   ├── logo.icns
│   ├── sudo-askpass.osascript.js
│   └── support.html
├── tsconfig.json
├── webpack.main.additions.js
└── webpack.renderer.additions.js
Download .txt
SYMBOL INDEX (232 symbols across 57 files)

FILE: sentry-symbols.js
  constant VERSION (line 16) | const VERSION = /\bv?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[...
  constant SYMBOL_CACHE_FOLDER (line 17) | const SYMBOL_CACHE_FOLDER = '.electron-symbols';
  function main (line 21) | async function main() {
  function getElectronVersion (line 71) | function getElectronVersion() {
  function downloadSymbols (line 88) | async function downloadSymbols(options) {

FILE: src/app/analytics/analytics.ts
  type Request (line 20) | interface Request extends Event {
  class Analytics (line 24) | class Analytics {
    method constructor (line 29) | constructor({ baseUrl, disabled }: { baseUrl: string; disabled: boolea...
    method initialize (line 34) | initialize(): void {

FILE: src/app/analytics/event.ts
  type EventName (line 8) | enum EventName {
  type Event (line 23) | interface Event {
  type Client (line 32) | interface Client {

FILE: src/app/config/filters.ts
  class Filters (line 15) | class Filters {
    method constructor (line 18) | constructor(root: RootStore) {
    method setupReactions (line 31) | setupReactions(): void {
    method config (line 43) | get config(): ProposalFilters {
    method country (line 51) | get country(): string | undefined {
    method presetID (line 55) | get presetID(): number | undefined | null {
    method initialized (line 59) | get initialized(): boolean {
    method defaults (line 63) | get defaults(): ProposalFilters {

FILE: src/app/config/store.ts
  type Config (line 18) | interface Config {
  type DesktopConfig (line 29) | interface DesktopConfig {
  type ProposalFilters (line 42) | interface ProposalFilters {
  type PriceCeiling (line 55) | interface PriceCeiling {
  class ConfigStore (line 59) | class ConfigStore {
    method constructor (line 65) | constructor(root: RootStore) {
    method currentTermsAgreed (line 119) | get currentTermsAgreed(): boolean {
    method onboarded (line 134) | get onboarded(): boolean {
    method dnsOption (line 142) | get dnsOption(): DNSOption {
    method autoNATCompatibility (line 150) | get autoNATCompatibility(): boolean {
    method quickConnect (line 158) | get quickConnect(): boolean {
    method killSwitch (line 166) | get killSwitch(): boolean {
    method vpn2Offered (line 176) | get vpn2Offered(): boolean {

FILE: src/app/connection/components/ConnectButton/ConnectButton.tsx
  type ConnectButtonProps (line 17) | type ConnectButtonProps = {

FILE: src/app/connection/store.ts
  class ConnectionStore (line 23) | class ConnectionStore {
    method constructor (line 36) | constructor(root: RootStore) {
    method setupReactions (line 66) | setupReactions(): void {
    method connect (line 132) | async connect(): Promise<void> {
    method quickConnect (line 137) | async quickConnect(): Promise<void> {
    method _doConnect (line 144) | private async _doConnect(): Promise<void> {
    method statusCheck (line 195) | async statusCheck(): Promise<void> {
    method disconnect (line 212) | async disconnect(): Promise<void> {
    method resolveOriginalLocation (line 233) | async resolveOriginalLocation(): Promise<void> {
    method resetLocation (line 243) | resetLocation(): void {
    method resolveLocation (line 255) | async resolveLocation(): Promise<void> {
    method resolveNATType (line 279) | async resolveNATType(): Promise<void> {
    method currentIp (line 294) | get currentIp(): string {

FILE: src/app/daemon/store.ts
  type DaemonStatusType (line 19) | enum DaemonStatusType {
  type StartupStatus (line 24) | enum StartupStatus {
  class DaemonStore (line 34) | class DaemonStore {
    method constructor (line 44) | constructor(root: RootStore) {
    method setupReactions (line 65) | setupReactions(): void {
    method setStartupStatus (line 84) | setStartupStatus(status: StartupStatus): void {
    method healthcheck (line 88) | async healthcheck(): Promise<void> {
    method update (line 109) | async update(): Promise<void> {
    method start (line 137) | async start(): Promise<void> {
    method supervisorInstall (line 164) | async supervisorInstall(): Promise<void> {

FILE: src/app/feedback/store.ts
  class FeedbackStore (line 16) | class FeedbackStore {
    method constructor (line 21) | constructor(root: RootStore) {
    method reportIssue (line 30) | async reportIssue(issue: Issue): Promise<string> {

FILE: src/app/identity/store.ts
  class IdentityStore (line 20) | class IdentityStore {
    method constructor (line 28) | constructor(root: RootStore) {
    method setupReactions (line 56) | setupReactions(): void {
    method hasIdentities (line 80) | async hasIdentities(): Promise<boolean> {
    method loadIdentity (line 85) | async loadIdentity(): Promise<void> {
    method identityExists (line 93) | get identityExists(): boolean {
    method fetchIdentity (line 97) | async fetchIdentity(): Promise<Identity | undefined> {
    method create (line 128) | async create(): Promise<void> {
    method unlock (line 136) | async unlock(): Promise<void> {
    method balanceSufficientToRegister (line 154) | async balanceSufficientToRegister(): Promise<boolean> {
    method register (line 176) | async register(id: Identity, referralToken?: string): Promise<void> {
    method requireId (line 186) | requireId(): Identity {
    method upgradeRequired (line 194) | async upgradeRequired(): Promise<boolean> {
    method upgrade (line 209) | async upgrade(): Promise<void> {
    method registerWithReferralToken (line 224) | async registerWithReferralToken(token: string): Promise<void> {
    method refreshBalance (line 238) | async refreshBalance(): Promise<void> {
    method exportIdentity (line 257) | async exportIdentity(opts: ExportIdentityOpts): Promise<string> {
    method importIdentityChooseFile (line 265) | importIdentityChooseFile(): Promise<string> {
    method importIdentity (line 269) | async importIdentity(opts: ImportIdentityOpts): Promise<string> {

FILE: src/app/location/components/Flag/Flag.tsx
  type FlagProps (line 10) | interface FlagProps {

FILE: src/app/location/states.ts
  type CountryCode (line 8) | type CountryCode = string
  type StateCode (line 9) | type StateCode = string
  type StateName (line 10) | type StateName = string
  constant STATES (line 12) | const STATES: { [countryCode: CountryCode]: { [stateCode: StateCode]: St...

FILE: src/app/navigation/components/ViewNavBar/ViewNavBar.tsx
  type ViewNavBarProps (line 43) | interface ViewNavBarProps {

FILE: src/app/navigation/store.ts
  class NavigationStore (line 19) | class NavigationStore {
    method constructor (line 23) | constructor(root: RootStore, history: History) {
    method location (line 41) | get location(): Location {

FILE: src/app/onboarding/components/InitialTopup/UseReferralCodePrompt.tsx
  type UseReferralCodePromptProps (line 52) | interface UseReferralCodePromptProps {
  type ReferralCodeFormFields (line 58) | interface ReferralCodeFormFields {

FILE: src/app/onboarding/components/IntroductionSteps/IntroductionSteps.tsx
  type IntroductionStepProps (line 105) | interface IntroductionStepProps {

FILE: src/app/onboarding/store.ts
  class OnboardingStore (line 13) | class OnboardingStore {
    method constructor (line 16) | constructor(root: RootStore) {

FILE: src/app/payment/currency.ts
  type AmountMultiCurrency (line 11) | interface AmountMultiCurrency {

FILE: src/app/payment/methods.ts
  type PaymentMethodName (line 13) | enum PaymentMethodName {
  type Gateway (line 20) | enum Gateway {
  type PaymentMethodMetadata (line 26) | interface PaymentMethodMetadata {
  type PaymentMethod (line 33) | type PaymentMethod = {
  constant SUPPORTED_METHODS (line 38) | const SUPPORTED_METHODS: { [key: string]: PaymentMethodMetadata } = {

FILE: src/app/payment/store.ts
  type OrderStatus (line 31) | enum OrderStatus {
  type MystChain (line 37) | enum MystChain {
  class PaymentStore (line 42) | class PaymentStore {
    method constructor (line 62) | constructor(root: RootStore) {
    method setupReactions (line 101) | setupReactions(): void {
    method fetchTransactorFees (line 110) | async fetchTransactorFees(): Promise<void> {
    method fetchMystToUsdRate (line 117) | async fetchMystToUsdRate(): Promise<void> {
    method fiatEquivalent (line 124) | fiatEquivalent(amount: number): number {
    method fetchPaymentGateways (line 129) | async fetchPaymentGateways(): Promise<void> {
    method orderOptions (line 146) | get orderOptions(): number[] {
    method currencies (line 150) | get currencies(): string[] {
    method buildCallerData (line 154) | buildCallerData(): CreatePaymentOrderRequest["gatewayCallerData"] {
    method validateOrderResponse (line 168) | validateOrderResponse(order: PaymentOrder): void {
    method createOrder (line 183) | async createOrder(): Promise<void> {
    method openOrderSecureForm (line 230) | async openOrderSecureForm(): Promise<void> {
    method orderStatus (line 239) | get orderStatus(): OrderStatus {
    method downloadInvoice (line 252) | async downloadInvoice(): Promise<void> {
    method startTopupFlow (line 286) | async startTopupFlow(location: string): Promise<void> {
    method onPaymentMethodChosen (line 294) | async onPaymentMethodChosen(): Promise<void> {
    method clearOrder (line 299) | clearOrder(): void {
    method clearPaymentOptions (line 304) | clearPaymentOptions(): void {

FILE: src/app/proposals/components/ProposalQuality/ProposalQuality.tsx
  type QualityProps (line 13) | interface QualityProps {

FILE: src/app/proposals/components/ProposalTable/ProposalTable.tsx
  type TableProps (line 81) | type TableProps = {

FILE: src/app/proposals/components/ProposalTable/RowRenderer.tsx
  type RowRendererProps (line 25) | type RowRendererProps = {

FILE: src/app/proposals/store.ts
  type Dict (line 25) | type Dict = _.Dictionary<UIProposal[]>
  type CountryCounts (line 26) | type CountryCounts = { [code: string]: number }
  class ProposalStore (line 28) | class ProposalStore {
    method constructor (line 44) | constructor(root: RootStore) {
    method setupReactions (line 74) | setupReactions(): void {
    method filters (line 102) | get filters(): ProposalFilters {
    method fetchProposals (line 106) | async fetchProposals(): Promise<void> {
    method fetchProposalFilterPresets (line 127) | async fetchProposalFilterPresets(): Promise<void> {
    method toggleFilterPreset (line 141) | async toggleFilterPreset(id: number | null): Promise<void> {
    method setQualityFilter (line 154) | async setQualityFilter(level: QualityLevel): Promise<void> {
    method setIncludeFailed (line 161) | setIncludeFailed(includeFailed: boolean): void {
    method setCountryFilter (line 173) | async setCountryFilter(countryCode?: string): Promise<void> {
    method toggleCountryFilter (line 184) | toggleCountryFilter(countryCode?: string): void {
    method filteredProposals (line 193) | get filteredProposals(): UIProposal[] {
    method priceCeil (line 197) | get priceCeil(): PriceCeiling {
    method toggleActiveProposal (line 221) | toggleActiveProposal(proposal?: UIProposal): void {
    method setActiveProposal (line 225) | setActiveProposal(proposal?: UIProposal): void {
    method prepareForQuickSearch (line 229) | async prepareForQuickSearch(): Promise<void> {
    method fetchAllProposalsForQuickSearch (line 238) | async fetchAllProposalsForQuickSearch(): Promise<void> {
    method useQuickSearchSuggestion (line 249) | async useQuickSearchSuggestion(proposal?: UIProposal): Promise<void> {

FILE: src/app/proposals/uiProposal.ts
  type ProposalKey (line 11) | type ProposalKey = string
  type UIProposal (line 13) | interface UIProposal extends Proposal {

FILE: src/app/referral/store.ts
  class ReferralStore (line 17) | class ReferralStore {
    method constructor (line 25) | constructor(root: RootStore) {
    method validateToken (line 41) | async validateToken(code: string): Promise<boolean> {
    method resetToken (line 58) | resetToken(): void {
    method generateToken (line 63) | async generateToken(): Promise<void> {
    method setToken (line 83) | setToken(token: string): void {
    method setMessage (line 88) | setMessage(message?: string): void {
    method setLoading (line 93) | setLoading(b: boolean): void {

FILE: src/app/store.ts
  type Step (line 41) | enum Step {
  class RootStore (line 58) | class RootStore {
    method constructor (line 73) | constructor(history: History) {
    method setupReactions (line 109) | setupReactions(): void {
    method startupSequence (line 132) | async startupSequence(resumeFromStep: Step): Promise<void> {
    method isWindows (line 228) | get isWindows(): boolean {
    method isMacOS (line 232) | get isMacOS(): boolean {
    method isLinux (line 236) | get isLinux(): boolean {

FILE: src/app/tequilapi/index.ts
  constant TEQUILAPI_PORT (line 14) | const TEQUILAPI_PORT = 44050
  constant SSE_URL (line 17) | const SSE_URL = `http://127.0.0.1:${TEQUILAPI_PORT}/events/state`

FILE: src/app/ui-kit/components/Button/BrandButton.tsx
  type BrandButtonProps (line 14) | type BrandButtonProps = {

FILE: src/app/ui-kit/components/Clipboard/Clipboard.tsx
  type Props (line 15) | interface Props {

FILE: src/app/ui-kit/components/CryptoAnimation/CryptoAnimation.tsx
  type IconCurrencyProps (line 21) | type IconCurrencyProps = IconProps & {

FILE: src/app/ui-kit/components/Prompt/Prompt.tsx
  type PromptProps (line 59) | interface PromptProps {

FILE: src/app/ui-kit/components/QR/QR.tsx
  type QRProps (line 10) | interface QRProps {

FILE: src/app/ui-kit/components/Spinner/Spinner.tsx
  type SpinnerProps (line 13) | interface SpinnerProps {

FILE: src/app/ui-kit/components/Toggle/Toggle.tsx
  type ToggleProps (line 12) | interface ToggleProps {

FILE: src/app/ui-kit/form-components/Checkbox/Checkbox.tsx
  type CheckboxProps (line 36) | type CheckboxProps = React.DetailedHTMLProps<React.InputHTMLAttributes<H...

FILE: src/app/ui-kit/form-components/Search.tsx
  type SearchProps (line 12) | interface SearchProps {

FILE: src/app/ui-kit/icons/IconMystToken.tsx
  type MystTokenProps (line 11) | interface MystTokenProps {

FILE: src/app/ui-kit/icons/IconPriceTier.tsx
  type IconPriceTierProps (line 23) | interface IconPriceTierProps {

FILE: src/app/ui-kit/icons/Props.tsx
  type IconProps (line 8) | interface IconProps {

FILE: src/app/views/common/Help/HelpView.tsx
  type NavButtonProps (line 66) | interface NavButtonProps {

FILE: src/app/views/common/Loading/LoadingView.tsx
  type LoadingViewProps (line 35) | interface LoadingViewProps {

FILE: src/app/views/common/Settings/ExportIdentityPrompt.tsx
  type ExportIdentityPromptProps (line 36) | interface ExportIdentityPromptProps {
  type ExportIdentityFormFields (line 42) | interface ExportIdentityFormFields {

FILE: src/app/views/common/Settings/ImportIdentityPrompt.tsx
  type ImportIdentityPromptProps (line 36) | interface ImportIdentityPromptProps {
  type ImportIdentityFormFields (line 42) | interface ImportIdentityFormFields {

FILE: src/app/views/common/Settings/SettingsView.tsx
  type NavButtonProps (line 57) | interface NavButtonProps {

FILE: src/app/views/consumer/Proposals/ProposalSearch.tsx
  function doChunk (line 105) | function doChunk() {

FILE: src/app/views/consumer/Referral/ReferralView.tsx
  constant MYST_APP_URL (line 59) | const MYST_APP_URL = "https://mysterium.network/apps"

FILE: src/main/node/mysteriumNode.ts
  class MysteriumNode (line 37) | class MysteriumNode {
    method registerIPC (line 41) | registerIPC(getMainWindow: () => BrowserWindow | null): void {
    method start (line 92) | start(port = TEQUILAPI_PORT): Promise<void> {
    method killGhost (line 123) | async killGhost(port: number): Promise<void> {
    method stop (line 152) | async stop(): Promise<void> {
    method exportIdentity (line 176) | exportIdentity({
    method importIdentity (line 218) | importIdentity({ filename, passphrase }: ImportIdentityOpts): Promise<...

FILE: src/main/node/supervisor.ts
  function mystSockPath (line 22) | function mystSockPath(): string {
  class Supervisor (line 33) | class Supervisor {
    method registerIPC (line 36) | registerIPC(): void {
    method connect (line 43) | async connect(): Promise<void> {
    method request (line 65) | request(command: string, timeout = 2000): Promise<string | void> {
    method runningVersion (line 92) | runningVersion(): Promise<string> {
    method upgrade (line 96) | async upgrade(): Promise<void> {
    method install (line 122) | async install(): Promise<void> {
    method disconnect (line 134) | disconnect(): void {

FILE: src/main/node/tequila.ts
  class Tequila (line 14) | class Tequila {
    method constructor (line 17) | constructor() {
    method registerIPC (line 22) | registerIPC() {

FILE: src/shared/errors/parseError.ts
  type ParsedMessage (line 9) | interface ParsedMessage {

FILE: src/shared/ipc.ts
  type WebIpcListenChannels (line 7) | enum WebIpcListenChannels {
  type MainIpcListenChannels (line 15) | enum MainIpcListenChannels {
  type IpcResponse (line 38) | interface IpcResponse {

FILE: src/shared/node/mysteriumNodeIPC.ts
  class MysteriumNodeIPC (line 11) | class MysteriumNodeIPC {
    method start (line 12) | start(): Promise<void> {
    method stop (line 15) | stop(): Promise<void> {
    method killGhosts (line 18) | killGhosts(): Promise<void> {
    method importIdentity (line 21) | importIdentity(opts: ImportIdentityOpts): Promise<IpcResponse> {
    method importIdentityChooseFile (line 24) | importIdentityChooseFile(): Promise<string> {
    method exportIdentity (line 29) | exportIdentity(opts: ExportIdentityOpts): Promise<IpcResponse> {
  type ImportIdentityOpts (line 34) | interface ImportIdentityOpts {
  type ExportIdentityOpts (line 39) | interface ExportIdentityOpts {

FILE: src/shared/node/supervisorIPC.ts
  class SupervisorIPC (line 11) | class SupervisorIPC {
    method connect (line 12) | async connect(): Promise<void> {
    method install (line 15) | async install(): Promise<void> {
    method upgrade (line 18) | async upgrade(): Promise<void> {
    method disconnect (line 21) | async disconnect(): Promise<void> {

FILE: src/shared/push/topics.ts
  type PushTopic (line 7) | enum PushTopic {

FILE: src/typings/libraries.d.ts
  type ByteSize (line 8) | type ByteSize = {
  type App (line 30) | interface App {

FILE: src/typings/react-table-config.d.ts
  type TableOptions (line 62) | interface TableOptions<D extends object>
  type Hooks (line 77) | interface Hooks<D extends object = {}>
  type TableInstance (line 83) | interface TableInstance<D extends object = {}>
  type TableState (line 94) | interface TableState<D extends object = {}>
  type ColumnInterface (line 106) | interface ColumnInterface<D extends object = {}>
  type ColumnInstance (line 113) | interface ColumnInstance<D extends object = {}>
  type Cell (line 119) | interface Cell<D extends object = {}, V = any>
  type Row (line 124) | interface Row<D extends object = {}>
Condensed preview — 239 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,500K chars).
[
  {
    "path": ".eslintrc",
    "chars": 923,
    "preview": "{\n  \"parser\": \"@typescript-eslint/parser\",\n  \"extends\": [\n    \"plugin:react/recommended\",\n    \"plugin:@typescript-eslint"
  },
  {
    "path": ".gitattributes",
    "chars": 18,
    "preview": "* text=auto eol=lf"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 778,
    "preview": "---\nname: Bug report\nabout: Submit a bug report to help us improve\ntitle: \"[bug]\"\nlabels: bug\nassignees: ''\n\n---\n\n**Desc"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 621,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: \"[feature request]\"\nlabels: enhancement\nassigne"
  },
  {
    "path": ".github/stale.yml",
    "chars": 682,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 90\n# Number of days of inactivity before a "
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 284,
    "preview": "name: Lint\n\non: [pull_request, push]\n\njobs:\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: "
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1483,
    "preview": "name: Release\n\non:\n  push:\n    # If the commit is tagged with a version (e.g. \"1.0.0\") release the app after building\n  "
  },
  {
    "path": ".gitignore",
    "chars": 61,
    "preview": "node_modules\ndist/\nstatic/bin/\n*.log\n.idea\n.electron-symbols\n"
  },
  {
    "path": ".prettierrc",
    "chars": 108,
    "preview": "{\n  \"semi\": false,\n  \"trailingComma\": \"all\",\n  \"singleQuote\": false,\n  \"printWidth\": 120,\n  \"tabWidth\": 4\n}\n"
  },
  {
    "path": ".yarnclean",
    "chars": 21,
    "preview": "@types/react-native\n\n"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2020 BlockDev AG\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "README.md",
    "chars": 3707,
    "preview": "# mysterium-vpn-desktop\n\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/mysteriumnetwork/my"
  },
  {
    "path": "assets/README.md",
    "chars": 90,
    "preview": "A directory for original assets used throughout the application.\nNot to be used directly!\n"
  },
  {
    "path": "build/entitlements.mac.plist",
    "chars": 401,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "build/nsis/customize.nsi",
    "chars": 789,
    "preview": "RequestExecutionLevel admin\n\n!macro customInstall\n  File \"/oname=$INSTDIR\\resources\\app.asar.unpacked\\node_modules\\@myst"
  },
  {
    "path": "ci/afterPack.js",
    "chars": 957,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "ci/notarize.js",
    "chars": 1071,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "docs/DEV_GUIDE.md",
    "chars": 1562,
    "preview": "# Developer's guide to Mysterium VPN desktop app\n\nThis desktop app is built with:\n- [Typescript](https://www.typescriptl"
  },
  {
    "path": "docs/DOCS_CONTRIBUTING",
    "chars": 89,
    "preview": "PNGs generated by MermaidJS Live Editor:\nhttps://mermaid-js.github.io/mermaid-live-editor"
  },
  {
    "path": "docs/myst-supervisor.mmd",
    "chars": 665,
    "preview": "sequenceDiagram\nNote right of Desktop app: Application starts\nDesktop app->>Node: Healthcheck\nNode-->>Desktop app: Faile"
  },
  {
    "path": "docs/node-tequilapi.mmd",
    "chars": 619,
    "preview": "sequenceDiagram\nNote right of Desktop app: Application starts\nDesktop app->>Node: healthcheck()\nNode-->>Desktop app: ok\n"
  },
  {
    "path": "monkey-patch-crypto.js",
    "chars": 967,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "package.json",
    "chars": 5870,
    "preview": "{\n  \"name\": \"mysterium-vpn-desktop\",\n  \"productName\": \"MysteriumDark\",\n  \"description\": \"Desktop VPN client (legacy) for"
  },
  {
    "path": "sentry-symbols.js",
    "chars": 2732,
    "preview": "#!/usr/bin/env node\n/* eslint-disable */\n\nlet SentryCli;\nlet download;\n\ntry {\n  SentryCli = require('@sentry/cli');\n  do"
  },
  {
    "path": "sentry.properties",
    "chars": 183,
    "preview": "defaults.url=https://sentry.io/\ndefaults.org=mysterium-network\ndefaults.project=mysterium-vpn-desktop\ncli.executable=../"
  },
  {
    "path": "src/app/.eslintrc",
    "chars": 154,
    "preview": "{\n  \"extends\": [\n    \"../../.eslintrc\"\n  ],\n  \"rules\": {\n    \"no-restricted-imports\": [\"error\", {\n      \"patterns\": [\n  "
  },
  {
    "path": "src/app/analytics/analytics.ts",
    "chars": 2553,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/analytics/event.ts",
    "chars": 995,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/config/filters.ts",
    "chars": 1969,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/config/store.ts",
    "chars": 5244,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/connection/components/ConnectButton/ConnectButton.tsx",
    "chars": 2312,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/connection/components/DisconnectButton/DisconnectButton.tsx",
    "chars": 1342,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/connection/status.ts",
    "chars": 645,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/connection/store.ts",
    "chars": 11527,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/daemon/components/AppVersion.tsx",
    "chars": 759,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/daemon/components/StartupLoadingView/StartupLoadingView.tsx",
    "chars": 537,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/daemon/store.ts",
    "chars": 5835,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/feedback/store.ts",
    "chars": 1274,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/identity/components/IdentityRegistrationView/IdentityRegistrationView.tsx",
    "chars": 2221,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/identity/components/IdentityUpgradeView/IdentityUpgradeView.tsx",
    "chars": 1042,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/identity/identity.ts",
    "chars": 373,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/identity/store.ts",
    "chars": 8909,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/index.tsx",
    "chars": 3861,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/location/components/CurrentIP/CurrentIP.tsx",
    "chars": 667,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/location/components/Flag/Flag.tsx",
    "chars": 645,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/location/components/ProtectionStatus/ProtectionStatus.tsx",
    "chars": 1053,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/location/countries.ts",
    "chars": 5556,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/location/states.ts",
    "chars": 1592,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/Routes/Routes.tsx",
    "chars": 6942,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/TitleBar/NakedTitleBar.tsx",
    "chars": 861,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/TitleBar/TitleBar.tsx",
    "chars": 4543,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/TitleBar/WindowButtonsLinux.tsx",
    "chars": 2354,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/TitleBar/WindowButtonsWindows.tsx",
    "chars": 1635,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/ViewContainer/ViewContainer.tsx",
    "chars": 377,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/ViewContent/ViewContent.tsx",
    "chars": 445,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/ViewNavBar/ViewNavBar.tsx",
    "chars": 1478,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/ViewSidebar/ViewSidebar.tsx",
    "chars": 529,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/components/ViewSplit/ViewSplit.tsx",
    "chars": 309,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/locations.ts",
    "chars": 2243,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/navigation/store.ts",
    "chars": 3771,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IdentityBackup/IdentityBackup.tsx",
    "chars": 5198,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IdentityBackup/animation_identity_keys.json",
    "chars": 50089,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":117.000004765508,\"op\":197.000008023974,\"w\":1110,\"h\":1080,\"nm\":\"Композиция 1\",\"dd"
  },
  {
    "path": "src/app/onboarding/components/IdentitySetup/IdentitySetup.tsx",
    "chars": 5517,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IdentitySetup/animation_identity.json",
    "chars": 50074,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":117.000004765508,\"w\":1110,\"h\":1080,\"nm\":\"Композиция 1\",\"ddd\":0,\"assets\":["
  },
  {
    "path": "src/app/onboarding/components/InitialTopup/InitialTopup.tsx",
    "chars": 5599,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/InitialTopup/UseReferralCodePrompt.tsx",
    "chars": 3928,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/InitialTopup/animation_onboarding_topup.json",
    "chars": 50089,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":197.000008023974,\"op\":282.000011486096,\"w\":1110,\"h\":1080,\"nm\":\"Композиция 1\",\"dd"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/IntroductionSteps.tsx",
    "chars": 3540,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/Step1.tsx",
    "chars": 1138,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/Step2.tsx",
    "chars": 1139,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/Step3.tsx",
    "chars": 1346,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/Step4.tsx",
    "chars": 1058,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/animation_crypto.json",
    "chars": 166849,
    "preview": "{\n  \"v\": \"5.7.4\",\n  \"fr\": 29.9700012207031,\n  \"ip\": 0,\n  \"op\": 212.000008634937,\n  \"w\": 1000,\n  \"h\": 1400,\n  \"nm\": \"OB_3"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/animation_network.json",
    "chars": 205263,
    "preview": "{\n  \"v\": \"5.7.4\",\n  \"fr\": 29.9700012207031,\n  \"ip\": 0,\n  \"op\": 155.000006313279,\n  \"w\": 1000,\n  \"h\": 1400,\n  \"nm\": \"OB_1"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/animation_payasyougo.json",
    "chars": 25827,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":180.00000733155,\"w\":1000,\"h\":1000,\"nm\":\"ДЩсл1\",\"ddd\":0,\"assets\":[{\"id\":\"c"
  },
  {
    "path": "src/app/onboarding/components/IntroductionSteps/animation_privacy.json",
    "chars": 23684,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":149.000006068894,\"w\":1000,\"h\":1000,\"nm\":\"Lock\",\"ddd\":0,\"assets\":[{\"id\":\"c"
  },
  {
    "path": "src/app/onboarding/components/Welcome/Welcome.tsx",
    "chars": 2123,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/onboarding/store.ts",
    "chars": 1562,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/components/SelectTaxCountry/SelectTaxCountry.tsx",
    "chars": 1308,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/components/SelectTaxState/SelectTaxState.tsx",
    "chars": 1045,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/currency.ts",
    "chars": 370,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/display.ts",
    "chars": 683,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/methods.ts",
    "chars": 1556,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/rate.ts",
    "chars": 383,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/payment/store.ts",
    "chars": 11841,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/CountryFilter/CountryFilter.tsx",
    "chars": 3201,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/Preset/Preset.tsx",
    "chars": 2000,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/ProposalQuality/ProposalQuality.tsx",
    "chars": 1061,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/ProposalTable/ProposalTable.tsx",
    "chars": 8467,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/ProposalTable/RowRenderer.tsx",
    "chars": 2117,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/QualityFilter/QualityFilter.tsx",
    "chars": 1893,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/components/SelectedProposal/SelectedProposal.tsx",
    "chars": 2262,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/store.ts",
    "chars": 8708,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/proposals/uiProposal.ts",
    "chars": 1107,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/referral/store.ts",
    "chars": 2685,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/storage/localStorage.ts",
    "chars": 591,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/store.ts",
    "chars": 9318,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/tequilapi/index.ts",
    "chars": 992,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/colors.ts",
    "chars": 494,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Anchor.tsx",
    "chars": 364,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/BrandButton.tsx",
    "chars": 2261,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/CancelButton.tsx",
    "chars": 402,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/GhostButton.tsx",
    "chars": 455,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/LightButton.tsx",
    "chars": 453,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/OutlineButton.tsx",
    "chars": 547,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/RippleButton.tsx",
    "chars": 1695,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/SecondaryButton.tsx",
    "chars": 410,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Button/SidebarButtons.tsx",
    "chars": 1139,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Clipboard/Clipboard.tsx",
    "chars": 815,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/CryptoAnimation.tsx",
    "chars": 1308,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_btc.json",
    "chars": 6681,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":134.000005457932,\"w\":1000,\"h\":1000,\"nm\":\"Bitcoin\",\"ddd\":0,\"assets\":[{\"id\""
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_dai.json",
    "chars": 6618,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":134.000005457932,\"w\":1000,\"h\":1000,\"nm\":\"Bitcoin\",\"ddd\":0,\"assets\":[{\"id\""
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_doge.json",
    "chars": 5189,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":90.0000036657751,\"w\":1000,\"h\":1000,\"nm\":\"Currencies_main\",\"ddd\":0,\"assets"
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_eth.json",
    "chars": 7675,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":134.000005457932,\"w\":1000,\"h\":1000,\"nm\":\"Bitcoin\",\"ddd\":0,\"assets\":[{\"id\""
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_ltc.json",
    "chars": 6345,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":134.000005457932,\"w\":1000,\"h\":1000,\"nm\":\"Bitcoin\",\"ddd\":0,\"assets\":[{\"id\""
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_myst.json",
    "chars": 23837,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":134.000005457932,\"w\":1000,\"h\":1000,\"nm\":\"Bitcoin\",\"ddd\":0,\"assets\":[{\"id\""
  },
  {
    "path": "src/app/ui-kit/components/CryptoAnimation/animation_usdt.json",
    "chars": 6247,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":134.000005457932,\"w\":1000,\"h\":1000,\"nm\":\"Bitcoin\",\"ddd\":0,\"assets\":[{\"id\""
  },
  {
    "path": "src/app/ui-kit/components/LogoTitle/LogoTitle.tsx",
    "chars": 9365,
    "preview": "/* eslint-disable */\n/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license foun"
  },
  {
    "path": "src/app/ui-kit/components/MysteriumVPN2Toast/MysteriumVPN2Toast.tsx",
    "chars": 1744,
    "preview": "/* eslint-disable */\n/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license foun"
  },
  {
    "path": "src/app/ui-kit/components/Prompt/Prompt.tsx",
    "chars": 2332,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/QR/QR.tsx",
    "chars": 479,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/SectionTitle/SectionTitle.tsx",
    "chars": 406,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Spinner/Spinner.tsx",
    "chars": 658,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Spinner/animation_spinner.json",
    "chars": 5704,
    "preview": "{\"v\":\"4.6.0\",\"fr\":29.9700012207031,\"ip\":0,\"op\":49.0000019958109,\"w\":200,\"h\":200,\"nm\":\"loading_ring_medium\",\"ddd\":0,\"asse"
  },
  {
    "path": "src/app/ui-kit/components/StepProgressBar/StepProgressBar.tsx",
    "chars": 1626,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/Toggle/Toggle.tsx",
    "chars": 2976,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/components/dismissibleToast.tsx",
    "chars": 1094,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/form-components/Checkbox/Checkbox.tsx",
    "chars": 1788,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/form-components/Search.tsx",
    "chars": 1254,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/form-components/Select.tsx",
    "chars": 552,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/form-components/TextArea.tsx",
    "chars": 563,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/form-components/TextInput.tsx",
    "chars": 679,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconBrowsing.tsx",
    "chars": 1256,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconCloudDownload.tsx",
    "chars": 1320,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconCopy.tsx",
    "chars": 1249,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconDocument.tsx",
    "chars": 663,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconDownload.tsx",
    "chars": 2004,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconDuration.tsx",
    "chars": 748,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconGlobe.tsx",
    "chars": 2370,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconIdentity.tsx",
    "chars": 1403,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconMedia.tsx",
    "chars": 1002,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconMusic.tsx",
    "chars": 729,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconMystToken.tsx",
    "chars": 1171,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconNoPreset.tsx",
    "chars": 899,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconPaid.tsx",
    "chars": 2373,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconPerson.tsx",
    "chars": 1066,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconPlay.tsx",
    "chars": 676,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconPriceTier.tsx",
    "chars": 987,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconReceived.tsx",
    "chars": 790,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconSent.tsx",
    "chars": 851,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconSettings.tsx",
    "chars": 1075,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/IconWallet.tsx",
    "chars": 636,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/icons/Props.tsx",
    "chars": 222,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/ui-kit/typography.ts",
    "chars": 792,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/AcceptTerms/AcceptTermsView.tsx",
    "chars": 3681,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Help/HelpContentReportIssue.tsx",
    "chars": 3410,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Help/HelpContentTermsAndConditions.tsx",
    "chars": 2154,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Help/HelpView.tsx",
    "chars": 7556,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Loading/LoadingView.tsx",
    "chars": 1777,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Loading/animation_loading_loop.json",
    "chars": 25704,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":119.000004846969,\"op\":240.0000097754,\"w\":640,\"h\":560,\"nm\":\"Logo\",\"ddd\":0,\"assets"
  },
  {
    "path": "src/app/views/common/Loading/animation_loading_start.json",
    "chars": 25257,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":119.000004846969,\"w\":640,\"h\":560,\"nm\":\"Logo\",\"ddd\":0,\"assets\":[{\"id\":\"com"
  },
  {
    "path": "src/app/views/common/Settings/ExportIdentityPrompt.tsx",
    "chars": 3128,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Settings/ImportIdentityPrompt.tsx",
    "chars": 2148,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Settings/SettingsConnection.tsx",
    "chars": 5019,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Settings/SettingsFilters.tsx",
    "chars": 1193,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Settings/SettingsMysteriumId.tsx",
    "chars": 5197,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/common/Settings/SettingsView.tsx",
    "chars": 4413,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Connected/ConnectedView.tsx",
    "chars": 6246,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Connected/ConnectionProposal.tsx",
    "chars": 1322,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Connected/ConnectionStatistics.tsx",
    "chars": 4081,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Connected/animation_connected_loop.json",
    "chars": 24257,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":120.0000048877,\"op\":249.000010141978,\"w\":1492,\"h\":1920,\"nm\":\"2\",\"ddd\":0,\"assets\""
  },
  {
    "path": "src/app/views/consumer/Connected/animation_connecting_loop.json",
    "chars": 56013,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":89.0000036250443,\"op\":271.000011038056,\"w\":1492,\"h\":1920,\"nm\":\"1\",\"ddd\":0,\"asset"
  },
  {
    "path": "src/app/views/consumer/Connected/animation_connecting_start.json",
    "chars": 32889,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":90.0000036657751,\"w\":1492,\"h\":1920,\"nm\":\"1\",\"ddd\":0,\"assets\":[{\"id\":\"comp"
  },
  {
    "path": "src/app/views/consumer/Proposals/ManualConnectView.tsx",
    "chars": 3353,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Proposals/ProposalSearch.tsx",
    "chars": 6370,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Proposals/QuickConnectView.tsx",
    "chars": 4871,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Proposals/SwitchConnectView.tsx",
    "chars": 2274,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Proposals/animation_quick_connect.json",
    "chars": 26284,
    "preview": "{\"v\":\"5.7.4\",\"fr\":29.9700012207031,\"ip\":0,\"op\":66.0000026882351,\"w\":1492,\"h\":1920,\"nm\":\"1\",\"ddd\":0,\"assets\":[{\"id\":\"comp"
  },
  {
    "path": "src/app/views/consumer/Referral/ReferralView.tsx",
    "chars": 3809,
    "preview": "/**\n * Copyright (c) 2020 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/TopupChooseMethod.tsx",
    "chars": 5107,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/TopupFailed.tsx",
    "chars": 3306,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/TopupRoutes.tsx",
    "chars": 3312,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/TopupSuccess.tsx",
    "chars": 4600,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/coingate/CoingateOrderSummary.tsx",
    "chars": 4173,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/coingate/CoingatePaymentOptions.tsx",
    "chars": 7654,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/coingate/CoingateSelectAmount.tsx",
    "chars": 8058,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/coingate/CoingateWaitingForPayment.tsx",
    "chars": 5265,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/coingate/LogoCoingate.tsx",
    "chars": 4394,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/common/OptionLabel.tsx",
    "chars": 534,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/common/OptionValue.tsx",
    "chars": 425,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/common/OrderBreakdown.tsx",
    "chars": 2647,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/myst/MystChooseChain.tsx",
    "chars": 4691,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/myst/MystPolygonWaitingForPayment.tsx",
    "chars": 5532,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/myst/MystSelectAmount.tsx",
    "chars": 8062,
    "preview": "/**\n * Copyright (c) 2021 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/paypal/LogoPaypal.tsx",
    "chars": 3822,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/paypal/PaypalOrderSummary.tsx",
    "chars": 4007,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/paypal/PaypalPaymentOptions.tsx",
    "chars": 8019,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/paypal/PaypalSelectAmount.tsx",
    "chars": 8054,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/paypal/PaypalWaitingForPayment.tsx",
    "chars": 4628,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  },
  {
    "path": "src/app/views/consumer/Topup/stripe/LogoStripe.tsx",
    "chars": 3493,
    "preview": "/**\n * Copyright (c) 2022 BlockDev AG\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE f"
  }
]

// ... and 39 more files (download for full content)

About this extraction

This page contains the full source code of the mysteriumnetwork/mysterium-vpn-desktop GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 239 files (1.3 MB), approximately 435.2k tokens, and a symbol index with 232 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!