diff --git a/.eslintignore b/.eslintignore
index d19fc7bf..9c2bae7c 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,3 @@
!.github
node_modules/
+dist
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..519f5cd6
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,56 @@
+module.exports = {
+ root: true,
+ env: {
+ es2020: true,
+ browser: true,
+ node: true,
+ },
+ extends: [
+ 'plugin:@typescript-eslint/recommended',
+ 'eslint:recommended',
+ 'plugin:import/typescript',
+ 'plugin:jsx-a11y/recommended',
+ 'plugin:@typescript-eslint/recommended',
+ ],
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ sourceType: 'module',
+ ecmaVersion: 2020,
+ ecmaFeatures: { jsx: true },
+ },
+ plugins: [
+ 'html',
+ 'node',
+ '@typescript-eslint',
+ 'jest',
+ '@typescript-eslint',
+ 'simple-import-sort',
+ ],
+ rules: {
+ 'no-param-reassign': 'error',
+ 'no-use-before-define': 'off',
+ 'no-underscore-dangle': 'off',
+ 'no-implicit-coercion': ['error', { allow: ['!!'] }],
+ 'block-scoped-var': 'error',
+ 'no-shadow': 'warn',
+ 'no-unsafe-optional-chaining': ['error', { disallowArithmeticOperators: true }],
+ 'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
+ 'no-return-assign': 'warn',
+ 'consistent-return': 'off',
+ 'no-redeclare': 'error',
+ 'simple-import-sort/imports': 'error',
+ 'simple-import-sort/exports': 'error',
+ 'import/prefer-default-export': 'off',
+ 'jsx-a11y/anchor-is-valid': 'warn',
+ 'jsx-a11y/no-noninteractive-tabindex': 'warn',
+ 'jsx-a11y/tabindex-no-positive': 'warn',
+ 'jsx-a11y/click-events-have-key-events': 'warn',
+ 'jsx-a11y/no-static-element-interactions': 'warn',
+ 'jsx-a11y/no-noninteractive-element-interactions': 'warn',
+ // TODO: Re-ctivate rule when we have a custom logger
+ 'no-console': 'off',
+ 'no-empty': ['error', { allowEmptyCatch: true }],
+ '@typescript-eslint/no-explicit-any': 'off',
+ },
+ ignorePatterns: ['**/dist/**', '**/dev/**', '**/coverage/**', '**/temp/**'],
+};
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 01f96c60..00000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,104 +0,0 @@
-{
- "env": {
- "es2020": true,
- "browser": true,
- "node": true
- },
- "extends": [
- "plugin:@typescript-eslint/recommended",
- "plugin:prettier/recommended",
- "eslint:recommended",
- "plugin:react/recommended",
- "plugin:react-hooks/recommended",
- "plugin:import/typescript",
- "plugin:jsx-a11y/recommended",
- "plugin:cypress/recommended",
- "prettier",
- "plugin:jest/recommended",
- "plugin:@typescript-eslint/recommended"
- ],
- "parser": "@typescript-eslint/parser",
- "parserOptions": {
- "sourceType": "module",
- "ecmaVersion": 2020,
- "ecmaFeatures": { "jsx": true }
- },
- "plugins": [
- "html",
- "prettier",
- "react",
- "node",
- "react-hooks",
- "cypress",
- "@typescript-eslint",
- "jest",
- "@typescript-eslint",
- "simple-import-sort"
- ],
- "settings": {
- "react": {
- "version": "detect"
- }
- },
- "rules": {
- "semi": "off",
- "prettier/prettier": "error",
- "maxWarnings": "off",
- "react/self-closing-comp": [
- "error",
- {
- "component": true,
- "html": true
- }
- ],
- "react/prop-types": "off",
- "no-param-reassign": "error",
- "no-use-before-define": "off",
- "no-underscore-dangle": "off",
- "no-implicit-coercion": ["error", { "allow": ["!!"] }],
- "block-scoped-var": "error",
- "no-shadow": "warn",
- "no-unsafe-optional-chaining": ["error", { "disallowArithmeticOperators": true }],
- "no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
- "no-return-assign": "warn",
- "consistent-return": "off",
- "no-redeclare": "error",
- "simple-import-sort/imports": "error",
- "simple-import-sort/exports": "error",
- "react/jsx-props-no-spreading": "off",
- "import/prefer-default-export": "off",
- "jsx-a11y/anchor-is-valid": "warn",
- "jsx-a11y/no-noninteractive-tabindex": [
- "warn",
- {
- "tags": [],
- "roles": ["tabpanel"],
- "allowExpressionValues": true
- }
- ],
- "jsx-a11y/tabindex-no-positive": "warn",
- "jsx-a11y/click-events-have-key-events": "warn",
- "jsx-a11y/no-static-element-interactions": "warn",
- "jsx-a11y/no-noninteractive-element-interactions": "warn",
- "no-console": "warn",
- "no-empty": ["error", { "allowEmptyCatch": true }],
- "react-hooks/rules-of-hooks": "error",
- "react-hooks/exhaustive-deps": "error",
- "react/react-in-jsx-scope": "off",
- "react/jsx-uses-react": "off",
- "@typescript-eslint/no-explicit-any": "off"
- },
- "overrides": [
- {
- "files": [".*rc.js", ".*rc.cjs", "*.config.js", "*.config.cjs"],
- "env": {
- "node": true
- }
- }
- ],
- "ignorePatterns": ["dist/**", "dev/**", "coverage/**"],
- "globals": {
- "chrome": true,
- "browser": true
- }
-}
diff --git a/.github/actions/constants.cjs b/.github/actions/constants.cjs
index aeb9b973..82bcbe5f 100644
--- a/.github/actions/constants.cjs
+++ b/.github/actions/constants.cjs
@@ -1,10 +1,10 @@
const BADGE =
- ''
-const BROWSERS = ['chrome', 'firefox', 'opera', 'edge']
+ '';
+const BROWSERS = ['chrome', 'firefox', 'opera', 'edge'];
const COLORS = {
green: '3fb950',
red: 'd73a49',
-}
+};
const TEMPLATE_VARS = {
tableBody: '{{ TABLE_BODY }}',
sha: '{{ SHA }}',
@@ -12,11 +12,11 @@ const TEMPLATE_VARS = {
badgeColor: '{{ BADGE_COLOR }}',
badgeLabel: '{{ BADGE_LABEL }}',
jobLogs: '{{ JOB_LOGS }}',
-}
+};
module.exports = {
BADGE,
BROWSERS,
COLORS,
TEMPLATE_VARS,
-}
+};
diff --git a/.github/actions/delete-artifacts.cjs b/.github/actions/delete-artifacts.cjs
index 216bc033..2a891330 100644
--- a/.github/actions/delete-artifacts.cjs
+++ b/.github/actions/delete-artifacts.cjs
@@ -1,25 +1,25 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-console */
-const { BROWSERS } = require('./constants.cjs')
+const { BROWSERS } = require('./constants.cjs');
async function getBrowserArfifacts({ github, owner, repo, name }) {
- const artifacts = []
+ const artifacts = [];
const result = await github.rest.actions.listArtifactsForRepo({
owner,
repo,
name,
- })
+ });
for (let i = 0; i < result.data.total_count; i++) {
- artifacts.push(result.data.artifacts[i].id)
+ artifacts.push(result.data.artifacts[i].id);
}
- return artifacts
+ return artifacts;
}
async function getPRArtifacts({ github, owner, repo, prNumber }) {
- const promises = []
- const artifacts = []
+ const promises = [];
+ const artifacts = [];
BROWSERS.forEach(browser =>
promises.push(
@@ -30,27 +30,27 @@ async function getPRArtifacts({ github, owner, repo, prNumber }) {
name: `${prNumber}-${browser}`,
}),
),
- )
+ );
- const data = await Promise.all(promises)
+ const data = await Promise.all(promises);
for (let i = 0; i < data.length; i++) {
- artifacts.push.apply(artifacts, data[i])
+ artifacts.push.apply(artifacts, data[i]);
}
- return artifacts
+ return artifacts;
}
module.exports = async ({ github, context, core }) => {
if (context.payload.action !== 'closed') {
- core.setFailed('This action only works on closed PRs.')
+ core.setFailed('This action only works on closed PRs.');
}
- const { owner, repo } = context.repo
- const prNumber = context.payload.number
- const promises = []
+ const { owner, repo } = context.repo;
+ const prNumber = context.payload.number;
+ const promises = [];
- const artifacts = await getPRArtifacts({ github, owner, repo, prNumber })
+ const artifacts = await getPRArtifacts({ github, owner, repo, prNumber });
for (let i = 0; i < artifacts.length; i++) {
promises.push(
@@ -59,9 +59,9 @@ module.exports = async ({ github, context, core }) => {
repo,
artifact_id: artifacts[i],
}),
- )
+ );
}
- await Promise.all(promises)
- console.log(`Deleted ${artifacts.length} artifacts for PR #${prNumber}.`)
-}
+ await Promise.all(promises);
+ console.log(`Deleted ${artifacts.length} artifacts for PR #${prNumber}.`);
+};
diff --git a/.github/actions/get-workflow-artifacts.cjs b/.github/actions/get-workflow-artifacts.cjs
index e6ada883..2db0e7e3 100644
--- a/.github/actions/get-workflow-artifacts.cjs
+++ b/.github/actions/get-workflow-artifacts.cjs
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable no-console */
-const fs = require('node:fs/promises')
-const { COLORS, TEMPLATE_VARS, BADGE } = require('./constants.cjs')
+const fs = require('node:fs/promises');
+const { COLORS, TEMPLATE_VARS, BADGE } = require('./constants.cjs');
const ARTIFACTS_DATA = {
chrome: {
@@ -24,73 +24,73 @@ const ARTIFACTS_DATA = {
url: null,
size: null,
},
-}
+};
function getBadge(conclusion, badgeColor, badgeLabel) {
return BADGE.replace(TEMPLATE_VARS.conslusion, conclusion)
.replace(TEMPLATE_VARS.badgeColor, badgeColor)
- .replace(TEMPLATE_VARS.badgeLabel, badgeLabel)
+ .replace(TEMPLATE_VARS.badgeLabel, badgeLabel);
}
function formatBytes(bytes, decimals = 2) {
- if (!Number(bytes)) return '0B'
- const k = 1024
- const dm = decimals < 0 ? 0 : decimals
- const sizes = ['B', 'KB', 'MB', 'GB']
- const i = Math.floor(Math.log(bytes) / Math.log(k))
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`
+ if (!Number(bytes)) return '0B';
+ const k = 1024;
+ const dm = decimals < 0 ? 0 : decimals;
+ const sizes = ['B', 'KB', 'MB', 'GB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`;
}
module.exports = async ({ github, context, core }) => {
- const { owner, repo } = context.repo
- const baseUrl = context.payload.repository.html_url
- const suiteId = context.payload.workflow_run.check_suite_id
- const runId = context.payload.workflow_run.id
- const conclusion = context.payload.workflow_run.conclusion
- const sha = context.payload.workflow_run.pull_requests[0].head.sha
- const prNumber = context.payload.workflow_run.pull_requests[0].number
- const jobLogsUrl = `${baseUrl}/actions/runs/${context.payload.workflow_run.id}`
- const template = await fs.readFile('./.github/actions/templates/build-status.md', 'utf8')
- const tableRows = []
+ const { owner, repo } = context.repo;
+ const baseUrl = context.payload.repository.html_url;
+ const suiteId = context.payload.workflow_run.check_suite_id;
+ const runId = context.payload.workflow_run.id;
+ const conclusion = context.payload.workflow_run.conclusion;
+ const sha = context.payload.workflow_run.pull_requests[0].head.sha;
+ const prNumber = context.payload.workflow_run.pull_requests[0].number;
+ const jobLogsUrl = `${baseUrl}/actions/runs/${context.payload.workflow_run.id}`;
+ const template = await fs.readFile('./.github/actions/templates/build-status.md', 'utf8');
+ const tableRows = [];
- core.setOutput('conclusion', conclusion)
+ core.setOutput('conclusion', conclusion);
if (conclusion === 'cancelled') {
- return
+ return;
}
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner,
repo,
run_id: runId,
- })
+ });
artifacts.data.artifacts.forEach(artifact => {
- const [, key] = artifact.name.split('-')
- ARTIFACTS_DATA[key].url = `${baseUrl}/suites/${suiteId}/artifacts/${artifact.id}`
- ARTIFACTS_DATA[key].size = formatBytes(artifact.size_in_bytes)
- })
+ const [, key] = artifact.name.split('-');
+ ARTIFACTS_DATA[key].url = `${baseUrl}/suites/${suiteId}/artifacts/${artifact.id}`;
+ ARTIFACTS_DATA[key].size = formatBytes(artifact.size_in_bytes);
+ });
Object.keys(ARTIFACTS_DATA).forEach(k => {
- const { name, url, size } = ARTIFACTS_DATA[k]
+ const { name, url, size } = ARTIFACTS_DATA[k];
if (url === null && size === null) {
- const badgeUrl = getBadge('failure', COLORS.red, name)
- tableRows.push(`
${badgeUrl} | N/A |
`)
+ const badgeUrl = getBadge('failure', COLORS.red, name);
+ tableRows.push(`${badgeUrl} | N/A |
`);
} else {
- const badgeUrl = getBadge('success', COLORS.green, `${name} (${size})`)
+ const badgeUrl = getBadge('success', COLORS.green, `${name} (${size})`);
tableRows.push(
`${badgeUrl} | Download |
`,
- )
+ );
}
- })
+ });
- const tableBody = tableRows.join('')
+ const tableBody = tableRows.join('');
const commentBody = template
.replace(TEMPLATE_VARS.conslusion, conclusion)
.replace(TEMPLATE_VARS.sha, sha)
.replace(TEMPLATE_VARS.jobLogs, `Run #${runId}`)
- .replace(TEMPLATE_VARS.tableBody, tableBody)
+ .replace(TEMPLATE_VARS.tableBody, tableBody);
- core.setOutput('comment_body', commentBody)
- core.setOutput('pr_number', prNumber)
-}
+ core.setOutput('comment_body', commentBody);
+ core.setOutput('pr_number', prNumber);
+};
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 330791c3..f7f3f897 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,7 +1,12 @@
+'area: server':
+ - any:
+ - changed-files:
+ - any-glob-to-any-file: 'packages/server/src/**/*'
+
'area: background':
- any:
- changed-files:
- - any-glob-to-any-file: 'src/background/**/*'
+ - any-glob-to-any-file: 'packages/extension/src/background/**/*'
'area: ci':
- any:
@@ -11,12 +16,12 @@
'area: content':
- any:
- changed-files:
- - any-glob-to-any-file: 'src/content/**/*'
+ - any-glob-to-any-file: 'packages/extension/src/content/**/*'
'area: popup':
- any:
- changed-files:
- - any-glob-to-any-file: 'src/popup/**/*'
+ - any-glob-to-any-file: 'packages/extension/src/popup/**/*'
'area: documentation':
- any:
diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml
index 9e04e362..d2ceae19 100644
--- a/.github/workflows/pr-checks.yml
+++ b/.github/workflows/pr-checks.yml
@@ -14,8 +14,27 @@ concurrency:
# TODO(@raducristianpopa): add lint/format checks and tests
jobs:
- build:
- name: Build
+ checks:
+ name: ESLint, Prettier & Typecheck
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Environment setup
+ uses: ./.github/actions/setup
+
+ - name: Lint
+ run: pnpm lint
+
+ - name: Format
+ run: pnpm format
+
+ - name: Typecheck
+ run: pnpm typecheck
+
+ build-extension:
+ name: Build Extension
strategy:
fail-fast: false
matrix:
@@ -30,17 +49,17 @@ jobs:
- name: Build
shell: bash
- run: pnpm build ${{ matrix.browser }}
+ run: pnpm extension build ${{ matrix.browser }}
- name: Upload artifacts
uses: actions/upload-artifact@v3.1.2
with:
name: ${{ github.event.pull_request.number }}-${{ matrix.browser }}
- path: dist/${{ matrix.browser }}/${{ matrix.browser }}.zip
+ path: packages/extension/dist/${{ matrix.browser }}/${{ matrix.browser }}.zip
if-no-files-found: error
- test:
- name: Test
+ test-extension:
+ name: Test Extension
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
@@ -50,4 +69,33 @@ jobs:
uses: ./.github/actions/setup
- name: Test
- run: pnpm test:ci
+ run: pnpm extension test:ci
+
+ build-server:
+ name: Build Server
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Environment setup
+ uses: ./.github/actions/setup
+
+ - name: Build
+ shell: bash
+ run: pnpm wm-server build
+
+ # TODO: add tests for server
+ # test-server:
+ # name: Test Server
+ # runs-on: ubuntu-22.04
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@v3
+
+ # - name: Environment setup
+ # uses: ./.github/actions/setup
+
+ # - name: Test
+ # shell: bash
+ # run: pnpm wm-server test:ci
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 87f6f127..d3ada03a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -39,7 +39,7 @@ jobs:
- name: Build
shell: bash
- run: pnpm build
+ run: pnpm extension build
- name: Release
uses: softprops/action-gh-release@v1
@@ -49,7 +49,7 @@ jobs:
prerelease: false
generate_release_notes: true
files: |
- ./dist/chrome/chrome.zip
- ./dist/firefox/firefox.zip
- ./dist/opera/opera.zip
- ./dist/edge/edge.zip
+ ./packages/extension/dist/chrome/chrome.zip
+ ./packages/extension/dist/firefox/firefox.zip
+ ./packages/extension/dist/opera/opera.zip
+ ./packages/extension/dist/edge/edge.zip
diff --git a/.github/workflows/sanity.yml b/.github/workflows/sanity.yml
index 4eaf55d4..894618b0 100644
--- a/.github/workflows/sanity.yml
+++ b/.github/workflows/sanity.yml
@@ -5,10 +5,29 @@ on:
branches:
- main
-# TODO(@raducristianpopa): add lint/format checks and tests
+# TODO(@raducristianpopa): add server tests
jobs:
- build:
+ checks:
+ name: ESLint, Prettier, Typecheck
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Environment setup
+ uses: ./.github/actions/setup
+
+ - name: Lint
+ run: pnpm lint
+
+ - name: Format
+ run: pnpm format
+
+ - name: Typecheck
+ run: pnpm typecheck
+
+ build-extension:
name: Build extension
strategy:
fail-fast: false
@@ -24,10 +43,10 @@ jobs:
- name: Build
shell: bash
- run: pnpm build ${{ matrix.browser}}
+ run: pnpm extension build ${{ matrix.browser}}
- test:
- name: Test
+ test-extension:
+ name: Test Extension
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
@@ -37,4 +56,32 @@ jobs:
uses: ./.github/actions/setup
- name: Test
- run: pnpm test:ci
+ run: pnpm extension test:ci
+
+ build-server:
+ name: Build Server
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Environment setup
+ uses: ./.github/actions/setup
+
+ - name: Build
+ shell: bash
+ run: pnpm wm-server build
+
+ # test-server:
+ # name: Test Server
+ # runs-on: ubuntu-22.04
+ # steps:
+ # - name: Checkout repository
+ # uses: actions/checkout@v3
+
+ # - name: Environment setup
+ # uses: ./.github/actions/setup
+
+ # - name: Test
+ # shell: bash
+ # run: pnpm wm-server test:ci
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 00000000..f8b3d7de
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,2 @@
+auto-install-peers=true
+engine-strict=true
diff --git a/.prettierignore b/.prettierignore
index 26f5c409..3d96fd5e 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,6 +1,8 @@
public
generated
dist
+temp
+dev
pnpm-lock.yaml
*.svg
.gitignore
diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 00000000..ad2fae3b
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,13 @@
+/** @typedef {import("prettier").Config} PrettierConfig */
+
+/** @type { PrettierConfig } */
+module.exports = {
+ singleQuote: true,
+ printWidth: 100,
+ trailingComma: 'all',
+ quoteProps: 'as-needed',
+ bracketSpacing: true,
+ arrowParens: 'avoid',
+ bracketSameLine: false,
+ semi: true,
+};
diff --git a/.prettierrc.json b/.prettierrc.json
deleted file mode 100644
index e5e9ede8..00000000
--- a/.prettierrc.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "singleQuote": true,
- "printWidth": 100,
- "trailingComma": "all",
- "quoteProps": "as-needed",
- "bracketSpacing": true,
- "arrowParens": "avoid",
- "bracketSameLine": true,
- "semi": false
-}
diff --git a/README.md b/README.md
index 2d9c2ff3..ac5cd605 100755
--- a/README.md
+++ b/README.md
@@ -3,9 +3,7 @@
![Github Actions CI](https://github.com/interledger/web-monetization-extension/actions/workflows/sanity.yml/badge.svg?branch=main)
[![Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://prettier.io/)
-Web Monetization is a browser extension that detects Web Monetization on websites using a browser API that allows the
-creation of a payment stream from the user agent to the website. This extension is built with React, TypeScript, and
-Vite.
+Web Monetization is a browser extension that detects Web Monetization on websites using a browser API that allows the creation of a payment stream from the user agent to the website.
### Contributing
@@ -19,26 +17,35 @@ Please read the [contribution guidelines](.github/CONTRIBUTING.md) before submit
### Development
-To run the extension in development mode with hot reload, use the following command:
+To run the extension and the Web Monetization server in development mode with hot reload, use the following command:
-`pnpm dev`
+```sh
+pnpm dev # default Chrome
+pnpm dev firefox # For Firefox
+pnpm dev opera # For Opera
+pnpm dev edge # For Edge
+```
-This command builds the extension using Vite's hot reload feature, allowing you to see immediate changes in the browser
-as you modify the code.
+### Building the extension
-### Building the Extension
+To build the extension for production (all browsers), use the following command:
-To build the extension for production, use the following command:
+```sh
+pnpm extension build
+```
-`pnpm build`
+### Building the extension for a specific browser
-### Building the Extension for Firefox
+To build the extension for a specific browser, use one of the following commands:
-To build the extension for Firefox, use the following command:
+```sh
+pnpm extension build chrome
+pnpm extension build firefox
+pnpm extension build opera
+pnpm extension build edge
+```
-`pnpm build firefox`
-
-This command transpiles the TypeScript code and generates a production-ready build of the extension in the dist
+The `build` command transpiles the TypeScript code and generates a production-ready build of the extension in the `packages/extension/dist`
directory.
### Installing the Extension in Chrome
@@ -77,7 +84,7 @@ To install the extension in Chrome, follow these steps:
4. Select the Extension File:
Navigate to the folder where you extracted the extension files and select the manifest.json file or the main folder of the extension.
-### Testing the Extension
+### Testing
To run the tests, use the following command:
@@ -85,11 +92,9 @@ To run the tests, use the following command:
This command runs the tests using Jest and generates a coverage report in the coverage directory.
-### Linting and Pre-Commit Hooks
+### Linting
-The extension is set up with ESLint and Prettier for code linting and formatting. Husky and lint-staged are configured
-to enforce linting and formatting rules before each commit. When you commit changes, Husky will run the linting and
-formatting tasks to ensure code quality.
+The extension is set up with ESLint and Prettier for code linting and formatting.
## License
diff --git a/jest.config.ts b/jest.config.ts
index ce9b19f5..fc9ff9c2 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -1,7 +1,6 @@
export default {
preset: 'ts-jest',
clearMocks: true,
- displayName: 'WM Extension',
collectCoverageFrom: [
'src/**/*.test.{js,jsx,ts,tsx}',
'!src/**/*.css',
@@ -16,13 +15,11 @@ export default {
moduleNameMapper: {
'@/(.*)': '/src/$1',
},
- setupFilesAfterEnv: ['./jest.setup.ts'],
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
- testEnvironment: 'jsdom',
testPathIgnorePatterns: ['/node_modules/', '/jest.config.ts'],
transform: {
'^.+\\.(js|jsx)$': 'babel-jest',
'^.+\\.(ts|tsx)?$': 'ts-jest',
'\\.(css|less|scss|sass|svg)$': 'jest-transform-stub',
},
-}
+};
diff --git a/jest.setup.ts b/jest.setup.ts
deleted file mode 100644
index 60d87370..00000000
--- a/jest.setup.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import '@testing-library/jest-dom'
-
-import { chrome } from 'jest-chrome'
-
-Object.assign(global, { chrome: chrome, browser: chrome })
diff --git a/package.json b/package.json
index 7cb1f3b6..c38df004 100644
--- a/package.json
+++ b/package.json
@@ -1,88 +1,42 @@
{
- "name": "web-monetization-extension",
- "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website.",
+ "name": "@interledger/web-monetization",
"private": true,
- "version": "0.2.3",
- "license": "Apache-2.0",
+ "homepage": "https://github.com/interledger/web-monetization-extension",
"repository": {
"type": "git",
- "url": "https://github.com/interledger/web-monetization-extension"
+ "url": "git@github.com:interledeger/web-monetization-extension.git"
},
+ "license": "Apache-2.0",
+ "author": "Interledger Tech Team ",
"scripts": {
- "analyze": "bash ./scripts/analyze.sh",
- "build": "bash ./scripts/build.sh",
- "dev": "bash ./scripts/dev.sh",
- "lint": "concurrently \"lint:*\"",
- "lint:fix": "eslint --ext js,jsx,ts,tsx, src --fix",
- "lint:eslint": "eslint . --ext .js,.ts,.tsx --max-warnings 0 --ignore-path .gitignore",
- "lint:prettier": "prettier \"**/*.(md|json|yml)\" --ignore-path .gitignore --check",
- "lint:type": "tsc --noEmit",
- "local-signatures": "pnpm tsx --watch ./local-signatures/index.ts",
- "test": "jest --maxWorkers=2 --passWithNoTests",
- "test:ci": "pnpm test -- --reporters=default --reporters=github-actions"
- },
- "dependencies": {
- "axios": "^1.5.1",
- "class-variance-authority": "^0.7.0",
- "clean-webpack-plugin": "^4.0.0",
- "copy-webpack-plugin": "^11.0.0",
- "core-js": "^3.32.1",
- "css-loader": "^6.8.1",
- "dotenv": "^16.3.1",
- "events": "^3.3.0",
- "html-webpack-plugin": "^5.5.3",
- "prop-types": "^15.8.1",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "sass": "^1.66.1",
- "sass-loader": "^13.3.2",
- "style-loader": "^3.3.3",
- "tailwind-merge": "^2.1.0",
- "terser-webpack-plugin": "^5.3.9",
- "uuid": "^9.0.1",
- "webextension-polyfill": "^0.10.0",
- "webpack": "^5.88.2",
- "webpack-bundle-analyzer": "^4.9.1",
- "webpack-ext-reloader-mv3": "^2.1.1",
- "webpack-extension-manifest-plugin": "^0.8.0",
- "zip-webpack-plugin": "^4.0.1"
+ "build": "pnpm -r build",
+ "dev": "concurrently -P -n SERVER,EXTESNION -c \"red,blue\" \"pnpm wm-server dev\" \"pnpm extension dev {1}\" --",
+ "format": "prettier --check .",
+ "format:fix": "prettier --write .",
+ "lint": "pnpm -r lint",
+ "lint:fix": "pnpm -r lint:fix",
+ "test": "pnpm -r test",
+ "test:ci": "pnpm -r test:ci",
+ "typecheck": "pnpm -r typecheck",
+ "extension": "pnpm --filter @interledger/wm-extension",
+ "wm-server": "pnpm --filter @interledger/wm-server"
},
"devDependencies": {
- "@interledger/http-signature-utils": "^2.0.0",
- "@tailwindcss/forms": "^0.5.7",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
- "@types/chrome": "^0.0.244",
- "@types/inboxsdk": "^2.0.11",
"@types/jest": "^29.5.5",
- "@types/jquery": "^3.5.18",
- "@types/koa": "^2.13.12",
- "@types/koa-bodyparser": "^4.3.12",
- "@types/lodash": "^4.14.197",
- "@types/node": "^20.8.4",
- "@types/react": "^18.2.21",
- "@types/react-dom": "^18.2.7",
- "@types/react-router-dom": "^5.3.3",
- "@types/redux": "^3.6.31",
- "@types/uuid": "^9.0.5",
- "@types/webextension-polyfill": "^0.10.7",
- "@types/webpack-bundle-analyzer": "^4.6.0",
- "@types/zip-webpack-plugin": "^3.0.4",
+ "@types/node": "^18.19.9",
"@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^6.5.0",
- "autoprefixer": "^10.4.16",
"concurrently": "^8.2.2",
"eslint": "^8.48.0",
"eslint-config-airbnb": "^19.0.4",
- "eslint-config-prettier": "^9.0.0",
"eslint-plugin-babel": "^5.3.1",
- "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-import": "^2.28.0",
- "eslint-plugin-jest": "^26.1.5",
+ "eslint-plugin-jest": "^27.6.3",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-node": "^11.1.0",
- "eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
@@ -91,19 +45,13 @@
"jest-chrome": "^0.8.0",
"jest-environment-jsdom": "^29.7.0",
"jest-transform-stub": "^2.0.0",
- "koa": "^2.14.2",
- "koa-bodyparser": "^4.4.1",
- "postcss": "^8.4.32",
- "postcss-loader": "^7.3.3",
"prettier": "^3.0.3",
- "react-router-dom": "^6.21.1",
+ "prettier-plugin-tailwindcss": "^0.5.11",
"tailwindcss": "^3.4.0",
"ts-jest": "^29.1.1",
"ts-loader": "^9.4.4",
"ts-node": "^10.9.2",
- "tsx": "^4.6.2",
- "typescript": "^5.2.2",
- "webpack-cli": "^5.1.4"
+ "typescript": "^5.2.2"
},
"engines": {
"pnpm": "^8.10.5",
diff --git a/packages/extension/.eslintrc.js b/packages/extension/.eslintrc.js
new file mode 100644
index 00000000..1c0b00a3
--- /dev/null
+++ b/packages/extension/.eslintrc.js
@@ -0,0 +1,33 @@
+module.exports = {
+ env: {
+ es2020: true,
+ browser: true,
+ node: true,
+ },
+ globals: {
+ chrome: true,
+ browser: true,
+ },
+ extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
+ plugins: ['react', 'react-hooks'],
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ },
+ rules: {
+ 'react/self-closing-comp': [
+ 'error',
+ {
+ component: true,
+ html: true,
+ },
+ ],
+ 'react/prop-types': 'off',
+ 'react/jsx-props-no-spreading': 'off',
+ 'react-hooks/rules-of-hooks': 'error',
+ 'react-hooks/exhaustive-deps': 'error',
+ 'react/react-in-jsx-scope': 'off',
+ 'react/jsx-uses-react': 'off',
+ },
+};
diff --git a/packages/extension/jest.config.ts b/packages/extension/jest.config.ts
new file mode 100644
index 00000000..088c80f9
--- /dev/null
+++ b/packages/extension/jest.config.ts
@@ -0,0 +1,8 @@
+import config from '../../jest.config';
+
+export default {
+ ...config,
+ displayName: '[WM] Extension',
+ setupFilesAfterEnv: ['./jest.setup.ts'],
+ testEnvironment: 'jsdom',
+};
diff --git a/packages/extension/jest.setup.ts b/packages/extension/jest.setup.ts
new file mode 100644
index 00000000..e524c769
--- /dev/null
+++ b/packages/extension/jest.setup.ts
@@ -0,0 +1,5 @@
+import '@testing-library/jest-dom';
+
+import { chrome } from 'jest-chrome';
+
+Object.assign(global, { chrome: chrome, browser: chrome });
diff --git a/packages/extension/package.json b/packages/extension/package.json
new file mode 100644
index 00000000..9d2bd0b2
--- /dev/null
+++ b/packages/extension/package.json
@@ -0,0 +1,77 @@
+{
+ "name": "@interledger/wm-extension",
+ "private": true,
+ "version": "0.2.3",
+ "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website.",
+ "homepage": "https://github.com/interledger/web-monetization-extension/tree/main/packages/extension",
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:interledeger/web-monetization-extension.git",
+ "directory": "packages/extension"
+ },
+ "license": "Apache-2.0",
+ "author": "Interledger Tech Team ",
+ "scripts": {
+ "analyze": "bash ./scripts/analyze.sh",
+ "build": "bash ./scripts/build.sh",
+ "dev": "bash ./scripts/dev.sh",
+ "format": "prettier --write .",
+ "lint": "eslint . --ext ts,tsx --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0",
+ "lint:fix": "eslint --ext ts,tsx --cache --cache-location 'node_modules/.cache/.eslintcache' --fix",
+ "typecheck": "tsc --noEmit",
+ "test": "jest --maxWorkers=2 --passWithNoTests",
+ "test:ci": "pnpm test -- --reporters=default --reporters=github-actions"
+ },
+ "dependencies": {
+ "axios": "^1.5.1",
+ "class-variance-authority": "^0.7.0",
+ "clean-webpack-plugin": "^4.0.0",
+ "copy-webpack-plugin": "^11.0.0",
+ "css-loader": "^6.8.1",
+ "events": "^3.3.0",
+ "html-webpack-plugin": "^5.5.3",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.21.1",
+ "sass": "^1.66.1",
+ "sass-loader": "^13.3.2",
+ "style-loader": "^3.3.3",
+ "tailwind-merge": "^2.1.0",
+ "terser-webpack-plugin": "^5.3.9",
+ "uuid": "^9.0.1",
+ "webextension-polyfill": "^0.10.0",
+ "webpack": "^5.88.2",
+ "webpack-bundle-analyzer": "^4.9.1",
+ "webpack-ext-reloader-mv3": "^2.1.1",
+ "webpack-extension-manifest-plugin": "^0.8.0",
+ "zip-webpack-plugin": "^4.0.1"
+ },
+ "devDependencies": {
+ "@tailwindcss/forms": "^0.5.7",
+ "@types/chrome": "^0.0.244",
+ "@types/inboxsdk": "^2.0.11",
+ "@types/jest": "^29.5.5",
+ "@types/node": "^20.8.4",
+ "@types/react": "^18.2.21",
+ "@types/react-dom": "^18.2.7",
+ "@types/react-router-dom": "^5.3.3",
+ "@types/uuid": "^9.0.5",
+ "@types/webextension-polyfill": "^0.10.7",
+ "@types/webpack-bundle-analyzer": "^4.6.0",
+ "@types/zip-webpack-plugin": "^3.0.4",
+ "@typescript-eslint/eslint-plugin": "^6.5.0",
+ "@typescript-eslint/parser": "^6.5.0",
+ "autoprefixer": "^10.4.16",
+ "postcss": "^8.4.32",
+ "postcss-loader": "^7.3.3",
+ "tailwindcss": "^3.4.0",
+ "webpack-cli": "^5.1.4"
+ },
+ "engines": {
+ "pnpm": "^8.10.5",
+ "npm": "pnpm",
+ "yarn": "pnpm",
+ "node": "^18.19.0"
+ },
+ "packageManager": "pnpm@8.10.5"
+}
diff --git a/scripts/analyze.sh b/packages/extension/scripts/analyze.sh
similarity index 100%
rename from scripts/analyze.sh
rename to packages/extension/scripts/analyze.sh
diff --git a/scripts/build.sh b/packages/extension/scripts/build.sh
similarity index 100%
rename from scripts/build.sh
rename to packages/extension/scripts/build.sh
diff --git a/packages/extension/scripts/dev.sh b/packages/extension/scripts/dev.sh
new file mode 100755
index 00000000..9c6c6632
--- /dev/null
+++ b/packages/extension/scripts/dev.sh
@@ -0,0 +1,9 @@
+#!/bin/bash -u
+
+set -ex
+
+TARGET=${1:-chrome}
+NODE_ENV=development
+
+echo Running ${NODE_ENV} build for ${TARGET}...
+NODE_ENV=${NODE_ENV} TARGET=${TARGET} pnpm webpack
diff --git a/packages/extension/src/_locales/en/messages.json b/packages/extension/src/_locales/en/messages.json
new file mode 100755
index 00000000..e3a1b4f3
--- /dev/null
+++ b/packages/extension/src/_locales/en/messages.json
@@ -0,0 +1,14 @@
+{
+ "appName": {
+ "message": "Web Monetization",
+ "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website."
+ },
+ "appDescription": {
+ "message": "Web Monetization Extension",
+ "description": "Web Monetization is a browser API that allows the creation of a payment stream from the user agent to the website."
+ },
+ "btnTooltip": {
+ "message": "Web Monetization",
+ "description": "..."
+ }
+}
diff --git a/src/assets/icons/icon-128.png b/packages/extension/src/assets/icons/icon-128.png
similarity index 100%
rename from src/assets/icons/icon-128.png
rename to packages/extension/src/assets/icons/icon-128.png
diff --git a/src/assets/icons/icon-34.png b/packages/extension/src/assets/icons/icon-34.png
similarity index 100%
rename from src/assets/icons/icon-34.png
rename to packages/extension/src/assets/icons/icon-34.png
diff --git a/src/assets/icons/icon-active-128.png b/packages/extension/src/assets/icons/icon-active-128.png
similarity index 100%
rename from src/assets/icons/icon-active-128.png
rename to packages/extension/src/assets/icons/icon-active-128.png
diff --git a/src/assets/icons/icon-active-34.png b/packages/extension/src/assets/icons/icon-active-34.png
similarity index 100%
rename from src/assets/icons/icon-active-34.png
rename to packages/extension/src/assets/icons/icon-active-34.png
diff --git a/src/assets/icons/icon-inactive-128.png b/packages/extension/src/assets/icons/icon-inactive-128.png
similarity index 100%
rename from src/assets/icons/icon-inactive-128.png
rename to packages/extension/src/assets/icons/icon-inactive-128.png
diff --git a/src/assets/icons/icon-inactive-34.png b/packages/extension/src/assets/icons/icon-inactive-34.png
similarity index 100%
rename from src/assets/icons/icon-inactive-34.png
rename to packages/extension/src/assets/icons/icon-inactive-34.png
diff --git a/src/assets/images/check.svg b/packages/extension/src/assets/images/check.svg
similarity index 100%
rename from src/assets/images/check.svg
rename to packages/extension/src/assets/images/check.svg
diff --git a/src/assets/images/close.svg b/packages/extension/src/assets/images/close.svg
similarity index 100%
rename from src/assets/images/close.svg
rename to packages/extension/src/assets/images/close.svg
diff --git a/src/assets/images/dollar.svg b/packages/extension/src/assets/images/dollar.svg
similarity index 100%
rename from src/assets/images/dollar.svg
rename to packages/extension/src/assets/images/dollar.svg
diff --git a/src/assets/images/edit.svg b/packages/extension/src/assets/images/edit.svg
similarity index 100%
rename from src/assets/images/edit.svg
rename to packages/extension/src/assets/images/edit.svg
diff --git a/src/assets/images/logo.svg b/packages/extension/src/assets/images/logo.svg
similarity index 100%
rename from src/assets/images/logo.svg
rename to packages/extension/src/assets/images/logo.svg
diff --git a/src/assets/images/web-monetization-fail.svg b/packages/extension/src/assets/images/web-monetization-fail.svg
similarity index 100%
rename from src/assets/images/web-monetization-fail.svg
rename to packages/extension/src/assets/images/web-monetization-fail.svg
diff --git a/src/assets/images/web-monetization-success.svg b/packages/extension/src/assets/images/web-monetization-success.svg
similarity index 100%
rename from src/assets/images/web-monetization-success.svg
rename to packages/extension/src/assets/images/web-monetization-success.svg
diff --git a/packages/extension/src/background/config.ts b/packages/extension/src/background/config.ts
new file mode 100644
index 00000000..5198fb72
--- /dev/null
+++ b/packages/extension/src/background/config.ts
@@ -0,0 +1,5 @@
+declare const CONFIG_SIGNATURES_URL: string;
+declare const CONFIG_WM_WALLET_ADDRESS: string;
+
+export const SIGNATURES_URL = CONFIG_SIGNATURES_URL;
+export const WM_WALLET_ADDRESS = CONFIG_WM_WALLET_ADDRESS;
diff --git a/src/background/grant/confirmPayment.ts b/packages/extension/src/background/grant/confirmPayment.ts
similarity index 55%
rename from src/background/grant/confirmPayment.ts
rename to packages/extension/src/background/grant/confirmPayment.ts
index b7374727..5d538082 100644
--- a/src/background/grant/confirmPayment.ts
+++ b/packages/extension/src/background/grant/confirmPayment.ts
@@ -1,12 +1,12 @@
-import { tabs } from 'webextension-polyfill'
+import { tabs } from 'webextension-polyfill';
const getCurrentActiveTabId = async () => {
- const activeTabs = await tabs.query({ active: true, currentWindow: true })
- return activeTabs[0].id
-}
+ const activeTabs = await tabs.query({ active: true, currentWindow: true });
+ return activeTabs[0].id;
+};
export const confirmPayment = async (url: string) => {
- const currentTabId = await getCurrentActiveTabId()
+ const currentTabId = await getCurrentActiveTabId();
return await new Promise(resolve => {
if (url) {
@@ -14,20 +14,20 @@ export const confirmPayment = async (url: string) => {
if (tab.id) {
tabs.onUpdated.addListener((tabId, changeInfo) => {
try {
- const tabUrl = new URL(changeInfo.url || '')
- const interactRef = tabUrl.searchParams.get('interact_ref')
+ const tabUrl = new URL(changeInfo.url || '');
+ const interactRef = tabUrl.searchParams.get('interact_ref');
if (tabId === tab.id && interactRef) {
- tabs.update(currentTabId, { active: true })
- tabs.remove(tab.id)
- resolve(interactRef)
+ tabs.update(currentTabId, { active: true });
+ tabs.remove(tab.id);
+ resolve(interactRef);
}
} catch (e) {
- throw new Error('Invalid interact ref url.')
+ throw new Error('Invalid interact ref url.');
}
- })
+ });
}
- })
+ });
}
- })
-}
+ });
+};
diff --git a/src/background/grant/createQuote.ts b/packages/extension/src/background/grant/createQuote.ts
similarity index 64%
rename from src/background/grant/createQuote.ts
rename to packages/extension/src/background/grant/createQuote.ts
index 5262bdd4..731bd46a 100644
--- a/src/background/grant/createQuote.ts
+++ b/packages/extension/src/background/grant/createQuote.ts
@@ -1,15 +1,15 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
-import { getHeaders } from './getHeaders'
+import { getHeaders } from './getHeaders';
type TCreateQuote = (_params: {
- receiver: string
- walletAddress: any
- sendingUrl: string
- token: string
- amount: string
- instance: AxiosInstance
-}) => Promise
+ receiver: string;
+ walletAddress: any;
+ sendingUrl: string;
+ token: string;
+ amount: string;
+ instance: AxiosInstance;
+}) => Promise;
export const createQuote: TCreateQuote = async ({
receiver,
@@ -28,17 +28,17 @@ export const createQuote: TCreateQuote = async ({
assetCode: walletAddress.assetCode, // 'USD'
assetScale: walletAddress.assetScale, // 9
},
- }
+ };
const quote = await instance.post(
new URL(sendingUrl).origin + '/quotes',
payload,
getHeaders(token),
- )
+ );
if (!quote.data.id) {
- throw new Error('No quote url id')
+ throw new Error('No quote url id');
}
- return quote.data.id
-}
+ return quote.data.id;
+};
diff --git a/src/background/grant/getContinuationRequest.ts b/packages/extension/src/background/grant/getContinuationRequest.ts
similarity index 67%
rename from src/background/grant/getContinuationRequest.ts
rename to packages/extension/src/background/grant/getContinuationRequest.ts
index 6947f267..f3c23cdd 100644
--- a/src/background/grant/getContinuationRequest.ts
+++ b/packages/extension/src/background/grant/getContinuationRequest.ts
@@ -1,13 +1,13 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
-import { getHeaders } from './getHeaders'
+import { getHeaders } from './getHeaders';
type TGetContinuationRequest = (_params: {
- url: string
- interactRef: any
- token: string
- instance: AxiosInstance
-}) => Promise
+ url: string;
+ interactRef: any;
+ token: string;
+ instance: AxiosInstance;
+}) => Promise;
export const getContinuationRequest: TGetContinuationRequest = async ({
url,
@@ -21,14 +21,14 @@ export const getContinuationRequest: TGetContinuationRequest = async ({
interact_ref: interactRef,
},
getHeaders(token),
- )
+ );
if (!continuationRequest.data.access_token.value) {
- throw new Error('No continuation request')
+ throw new Error('No continuation request');
}
return {
manageUrl: continuationRequest.data.access_token.manage,
continuationRequestToken: continuationRequest.data.access_token.value,
- }
-}
+ };
+};
diff --git a/src/background/grant/getHeaders.ts b/packages/extension/src/background/grant/getHeaders.ts
similarity index 96%
rename from src/background/grant/getHeaders.ts
rename to packages/extension/src/background/grant/getHeaders.ts
index 803f7b03..0b71ec99 100644
--- a/src/background/grant/getHeaders.ts
+++ b/packages/extension/src/background/grant/getHeaders.ts
@@ -2,4 +2,4 @@ export const getHeaders = (gnapToken: string) => ({
headers: {
Authorization: `GNAP ${gnapToken}`,
},
-})
+});
diff --git a/src/background/grant/getIcomingPaymentGrant.ts b/packages/extension/src/background/grant/getIcomingPaymentGrant.ts
similarity index 68%
rename from src/background/grant/getIcomingPaymentGrant.ts
rename to packages/extension/src/background/grant/getIcomingPaymentGrant.ts
index df0e2f09..2dd22e73 100644
--- a/src/background/grant/getIcomingPaymentGrant.ts
+++ b/packages/extension/src/background/grant/getIcomingPaymentGrant.ts
@@ -1,11 +1,11 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
type TGetIncomingPaymentGrant = (_params: {
- client: string
- identifier: string
- wallet: Record
- instance: AxiosInstance
-}) => Promise
+ client: string;
+ identifier: string;
+ wallet: Record;
+ instance: AxiosInstance;
+}) => Promise;
export const getIncomingPaymentGrant: TGetIncomingPaymentGrant = async ({
client,
@@ -24,13 +24,13 @@ export const getIncomingPaymentGrant: TGetIncomingPaymentGrant = async ({
],
},
client, // WM_PAYMENT_POINTER_URL
- }
+ };
- const response = await instance.post(wallet.authServer + '/', payload)
+ const response = await instance.post(wallet.authServer + '/', payload);
if (!response.data.access_token.value) {
- throw new Error('No client auth')
+ throw new Error('No client auth');
}
- return response.data.access_token.value
-}
+ return response.data.access_token.value;
+};
diff --git a/src/background/grant/getIncomingPaymentUrlId.ts b/packages/extension/src/background/grant/getIncomingPaymentUrlId.ts
similarity index 64%
rename from src/background/grant/getIncomingPaymentUrlId.ts
rename to packages/extension/src/background/grant/getIncomingPaymentUrlId.ts
index 18fbbe9c..a78627db 100644
--- a/src/background/grant/getIncomingPaymentUrlId.ts
+++ b/packages/extension/src/background/grant/getIncomingPaymentUrlId.ts
@@ -1,12 +1,12 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
-import { getHeaders } from './getHeaders'
+import { getHeaders } from './getHeaders';
type TGetIncomingPaymentUrlId = (_params: {
- walletAddress: string
- token: string
- instance: AxiosInstance
-}) => Promise
+ walletAddress: string;
+ token: string;
+ instance: AxiosInstance;
+}) => Promise;
export const getIncomingPaymentUrlId: TGetIncomingPaymentUrlId = async ({
walletAddress,
@@ -20,11 +20,11 @@ export const getIncomingPaymentUrlId: TGetIncomingPaymentUrlId = async ({
expiresAt: new Date(Date.now() + 6000 * 60 * 10).toISOString(),
},
getHeaders(token),
- )
+ );
if (!incomingPayment?.data?.id) {
- throw new Error('No incoming payment id')
+ throw new Error('No incoming payment id');
}
- return incomingPayment.data.id
-}
+ return incomingPayment.data.id;
+};
diff --git a/src/background/grant/getOutgoingPaymentGrant.ts b/packages/extension/src/background/grant/getOutgoingPaymentGrant.ts
similarity index 83%
rename from src/background/grant/getOutgoingPaymentGrant.ts
rename to packages/extension/src/background/grant/getOutgoingPaymentGrant.ts
index a9b258c2..7e68f137 100644
--- a/src/background/grant/getOutgoingPaymentGrant.ts
+++ b/packages/extension/src/background/grant/getOutgoingPaymentGrant.ts
@@ -1,12 +1,12 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
type TGetOutgoingPaymentGrant = (_params: {
- client: string
- identifier: string
- wallet: Record
- amount: string | number
- instance: AxiosInstance
-}) => Promise
+ client: string;
+ identifier: string;
+ wallet: Record;
+ amount: string | number;
+ instance: AxiosInstance;
+}) => Promise;
export const getOutgoingPaymentGrant: TGetOutgoingPaymentGrant = async ({
client,
@@ -44,16 +44,16 @@ export const getOutgoingPaymentGrant: TGetOutgoingPaymentGrant = async ({
nonce: new Date().getTime().toString(),
},
},
- }
+ };
- const outgoingPaymentGrant = await instance.post(wallet.authServer + '/', payload)
+ const outgoingPaymentGrant = await instance.post(wallet.authServer + '/', payload);
if (!outgoingPaymentGrant.data.interact.redirect) {
- throw new Error('No redirect')
+ throw new Error('No redirect');
}
return {
outgoingPaymentGrantToken: outgoingPaymentGrant.data.continue.access_token.value,
outgoingPaymentGrantData: outgoingPaymentGrant.data,
- }
-}
+ };
+};
diff --git a/src/background/grant/getQuoteGrant.ts b/packages/extension/src/background/grant/getQuoteGrant.ts
similarity index 63%
rename from src/background/grant/getQuoteGrant.ts
rename to packages/extension/src/background/grant/getQuoteGrant.ts
index 1c449e35..aa62b7f0 100644
--- a/src/background/grant/getQuoteGrant.ts
+++ b/packages/extension/src/background/grant/getQuoteGrant.ts
@@ -1,11 +1,11 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
type TGetQuoteGrant = (_params: {
- client: string
- identifier: string
- wallet: Record
- instance: AxiosInstance
-}) => Promise
+ client: string;
+ identifier: string;
+ wallet: Record;
+ instance: AxiosInstance;
+}) => Promise;
export const getQuoteGrant: TGetQuoteGrant = async ({ client, identifier, wallet, instance }) => {
const quotePayload = {
@@ -19,12 +19,12 @@ export const getQuoteGrant: TGetQuoteGrant = async ({ client, identifier, wallet
],
},
client, // WM_PAYMENT_POINTER_URL
- }
- const quoteGrant = await instance.post(wallet.authServer + '/', quotePayload)
+ };
+ const quoteGrant = await instance.post(wallet.authServer + '/', quotePayload);
if (!quoteGrant.data?.access_token?.value) {
- throw new Error('No quote grant')
+ throw new Error('No quote grant');
}
- return quoteGrant.data.access_token.value
-}
+ return quoteGrant.data.access_token.value;
+};
diff --git a/src/background/grant/index.ts b/packages/extension/src/background/grant/index.ts
similarity index 64%
rename from src/background/grant/index.ts
rename to packages/extension/src/background/grant/index.ts
index 7364d9b5..0947b7a5 100644
--- a/src/background/grant/index.ts
+++ b/packages/extension/src/background/grant/index.ts
@@ -1,12 +1,12 @@
-import { confirmPayment } from './confirmPayment'
-import { createQuote } from './createQuote'
-import { getContinuationRequest } from './getContinuationRequest'
-import { getHeaders } from './getHeaders'
-import { getIncomingPaymentGrant } from './getIcomingPaymentGrant'
-import { getIncomingPaymentUrlId } from './getIncomingPaymentUrlId'
-import { getOutgoingPaymentGrant } from './getOutgoingPaymentGrant'
-import { getQuoteGrant } from './getQuoteGrant'
-import { rotateToken } from './rotateToken'
+import { confirmPayment } from './confirmPayment';
+import { createQuote } from './createQuote';
+import { getContinuationRequest } from './getContinuationRequest';
+import { getHeaders } from './getHeaders';
+import { getIncomingPaymentGrant } from './getIcomingPaymentGrant';
+import { getIncomingPaymentUrlId } from './getIncomingPaymentUrlId';
+import { getOutgoingPaymentGrant } from './getOutgoingPaymentGrant';
+import { getQuoteGrant } from './getQuoteGrant';
+import { rotateToken } from './rotateToken';
export {
confirmPayment,
@@ -18,4 +18,4 @@ export {
getOutgoingPaymentGrant,
getQuoteGrant,
rotateToken,
-}
+};
diff --git a/src/background/grant/rotateToken.ts b/packages/extension/src/background/grant/rotateToken.ts
similarity index 64%
rename from src/background/grant/rotateToken.ts
rename to packages/extension/src/background/grant/rotateToken.ts
index b4d8087f..77927501 100644
--- a/src/background/grant/rotateToken.ts
+++ b/packages/extension/src/background/grant/rotateToken.ts
@@ -1,26 +1,26 @@
-import { AxiosInstance } from 'axios'
+import { AxiosInstance } from 'axios';
-import { getHeaders } from '@/background/grant/getHeaders'
+import { getHeaders } from '@/background/grant/getHeaders';
type TRotateToken = (_params: {
- url: string
- token: string
- instance: AxiosInstance
-}) => Promise
+ url: string;
+ token: string;
+ instance: AxiosInstance;
+}) => Promise;
export const rotateToken: TRotateToken = async ({ url, token, instance }) => {
const response = await instance.post(
url, // this.manageUrl
undefined,
getHeaders(token), // this.continuationRequestToken
- )
+ );
if (!response.data.access_token.value) {
- throw new Error('No continuation request')
+ throw new Error('No continuation request');
}
return {
manageUrl: response.data.access_token.manage,
continuationRequestToken: response.data.access_token.value,
- }
-}
+ };
+};
diff --git a/src/background/grantFlow.ts b/packages/extension/src/background/grantFlow.ts
similarity index 71%
rename from src/background/grantFlow.ts
rename to packages/extension/src/background/grantFlow.ts
index 18fac5fb..0ad90f39 100644
--- a/src/background/grantFlow.ts
+++ b/packages/extension/src/background/grantFlow.ts
@@ -1,6 +1,6 @@
-import { tabs } from 'webextension-polyfill'
+import { tabs } from 'webextension-polyfill';
-import { WM_WALLET_ADDRESS } from '@/background/config'
+import { WM_WALLET_ADDRESS } from '@/background/config';
import {
createQuote,
getHeaders,
@@ -8,67 +8,67 @@ import {
getIncomingPaymentUrlId,
getOutgoingPaymentGrant,
getQuoteGrant,
-} from '@/background/grant'
-import { confirmPayment } from '@/background/grant/confirmPayment'
-import { getContinuationRequest } from '@/background/grant/getContinuationRequest'
-import { getAxiosInstance } from '@/background/requestConfig'
+} from '@/background/grant';
+import { confirmPayment } from '@/background/grant/confirmPayment';
+import { getContinuationRequest } from '@/background/grant/getContinuationRequest';
+import { getAxiosInstance } from '@/background/requestConfig';
export class PaymentFlowService {
- axiosInstance = getAxiosInstance()
+ axiosInstance = getAxiosInstance();
- sendingPaymentPointerUrl: string
- receivingPaymentPointerUrl: string
+ sendingPaymentPointerUrl: string;
+ receivingPaymentPointerUrl: string;
- incomingPaymentUrlId: string
- quoteUrlId: string
- outgoingPaymentGrantData: any
+ incomingPaymentUrlId: string;
+ quoteUrlId: string;
+ outgoingPaymentGrantData: any;
- clientAuthToken: string
- quoteGrantToken: string
- outgoingPaymentGrantToken: string
- continuationRequestToken: string
- interactRef: string
+ clientAuthToken: string;
+ quoteGrantToken: string;
+ outgoingPaymentGrantToken: string;
+ continuationRequestToken: string;
+ interactRef: string;
- manageUrl: string
+ manageUrl: string;
- amount: string | number
+ amount: string | number;
- sendingWalletAddress: any
- receivingWalletAddress: any
+ sendingWalletAddress: any;
+ receivingWalletAddress: any;
constructor(
sendingPaymentPointerUrl: string,
receivingPaymentPointerUrl: string,
amount: string,
) {
- this.sendingPaymentPointerUrl = sendingPaymentPointerUrl
- this.receivingPaymentPointerUrl = receivingPaymentPointerUrl
- this.amount = amount
+ this.sendingPaymentPointerUrl = sendingPaymentPointerUrl;
+ this.receivingPaymentPointerUrl = receivingPaymentPointerUrl;
+ this.amount = amount;
}
async initPaymentFlow() {
- this.sendingWalletAddress = await this.getWalletAddress(this.sendingPaymentPointerUrl)
- this.receivingWalletAddress = await this.getWalletAddress(this.receivingPaymentPointerUrl)
+ this.sendingWalletAddress = await this.getWalletAddress(this.sendingPaymentPointerUrl);
+ this.receivingWalletAddress = await this.getWalletAddress(this.receivingPaymentPointerUrl);
this.clientAuthToken = await getIncomingPaymentGrant({
client: WM_WALLET_ADDRESS,
identifier: this.receivingPaymentPointerUrl,
wallet: this.receivingWalletAddress,
instance: this.axiosInstance,
- })
+ });
this.incomingPaymentUrlId = await getIncomingPaymentUrlId({
walletAddress: this.receivingPaymentPointerUrl,
token: this.clientAuthToken,
instance: this.axiosInstance,
- })
+ });
this.quoteGrantToken = await getQuoteGrant({
client: WM_WALLET_ADDRESS,
identifier: this.sendingPaymentPointerUrl,
wallet: this.sendingWalletAddress,
instance: this.axiosInstance,
- })
+ });
const outgoingData = await getOutgoingPaymentGrant({
client: WM_WALLET_ADDRESS,
@@ -76,25 +76,25 @@ export class PaymentFlowService {
wallet: this.sendingWalletAddress,
amount: this.amount,
instance: this.axiosInstance,
- })
+ });
- this.outgoingPaymentGrantToken = outgoingData.outgoingPaymentGrantToken
- this.outgoingPaymentGrantData = outgoingData.outgoingPaymentGrantData
+ this.outgoingPaymentGrantToken = outgoingData.outgoingPaymentGrantToken;
+ this.outgoingPaymentGrantData = outgoingData.outgoingPaymentGrantData;
- this.interactRef = await confirmPayment(this.outgoingPaymentGrantData.interact.redirect)
+ this.interactRef = await confirmPayment(this.outgoingPaymentGrantData.interact.redirect);
const continuationRequest = await getContinuationRequest({
url: this.outgoingPaymentGrantData.continue.uri,
interactRef: this.interactRef,
token: this.outgoingPaymentGrantToken,
instance: this.axiosInstance,
- })
+ });
- this.manageUrl = continuationRequest.manageUrl
- this.continuationRequestToken = continuationRequest.continuationRequestToken
+ this.manageUrl = continuationRequest.manageUrl;
+ this.continuationRequestToken = continuationRequest.continuationRequestToken;
- const currentTabId = await this.getCurrentActiveTabId()
- await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' })
+ const currentTabId = await this.getCurrentActiveTabId();
+ await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' });
}
async getWalletAddress(paymentPointerUrl: string) {
@@ -102,37 +102,37 @@ export class PaymentFlowService {
headers: {
Accept: 'application/json',
},
- })
+ });
if (!response?.data?.id) {
- throw new Error('No client auth')
+ throw new Error('No client auth');
}
- return response.data
+ return response.data;
}
async runPayment() {
const payload = {
walletAddress: this.sendingPaymentPointerUrl,
quoteId: this.quoteUrlId,
- }
+ };
const response = await this.axiosInstance.post(
new URL(this.sendingPaymentPointerUrl).origin + '/outgoing-payments',
payload,
getHeaders(this.continuationRequestToken),
- )
+ );
const {
receiveAmount,
receiver: incomingPayment,
walletAddress: paymentPointer,
- } = response.data
- const currentTabId = await this.getCurrentActiveTabId()
+ } = response.data;
+ const currentTabId = await this.getCurrentActiveTabId();
await tabs.sendMessage(currentTabId ?? 0, {
type: 'PAYMENT_SUCCESS',
data: { receiveAmount, incomingPayment, paymentPointer },
- })
+ });
// console.log('outgoingPayment', outgoingPayment)
}
@@ -144,13 +144,13 @@ export class PaymentFlowService {
token: this.quoteGrantToken,
amount: '1000000',
instance: this.axiosInstance,
- })
+ });
- await this.runPayment()
+ await this.runPayment();
}
async getCurrentActiveTabId() {
- const activeTabs = await tabs.query({ active: true, currentWindow: true })
- return activeTabs[0].id
+ const activeTabs = await tabs.query({ active: true, currentWindow: true });
+ return activeTabs[0].id;
}
}
diff --git a/src/background/index.ts b/packages/extension/src/background/index.ts
similarity index 67%
rename from src/background/index.ts
rename to packages/extension/src/background/index.ts
index 7c65b53a..ebf9eac1 100755
--- a/src/background/index.ts
+++ b/packages/extension/src/background/index.ts
@@ -1,11 +1,11 @@
-import { action, Runtime, runtime, Tabs, tabs } from 'webextension-polyfill'
+import { action, Runtime, runtime, Tabs, tabs } from 'webextension-polyfill';
-import { PaymentFlowService } from '@/background/grantFlow'
+import { PaymentFlowService } from '@/background/grantFlow';
-const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png')
-const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png')
-const iconInactive34 = runtime.getURL('assets/icons/icon-inactive-34.png')
-const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png')
+const iconActive34 = runtime.getURL('assets/icons/icon-active-34.png');
+const iconActive128 = runtime.getURL('assets/icons/icon-active-128.png');
+const iconInactive34 = runtime.getURL('assets/icons/icon-inactive-34.png');
+const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png');
// const SENDING_PAYMENT_POINTER_URL = 'https://ilp.rafiki.money/wmuser' // cel din extensie al userului
// const RECEIVING_PAYMENT_POINTER_URL = 'https://ilp.rafiki.money/web-page' // cel din dom
@@ -15,13 +15,13 @@ const iconInactive128 = runtime.getURL('assets/icons/icon-inactive-128.png')
* @type {class}
*/
class Background {
- _port: number
- grantFlow: PaymentFlowService | null = null
- spentAmount: number = 0
- paymentStarted = false
+ _port: number;
+ grantFlow: PaymentFlowService | null = null;
+ spentAmount: number = 0;
+ paymentStarted = false;
constructor() {
- this.init()
+ this.init();
}
/**
@@ -30,23 +30,23 @@ class Background {
* @returns {void}
*/
init = async () => {
- console.log('[===== Loaded Background Scripts =====]')
+ console.log('[===== Loaded Background Scripts =====]');
//When extension installed
- runtime.onInstalled.addListener(this.onInstalled)
+ runtime.onInstalled.addListener(this.onInstalled);
//Add message listener in Browser.
- runtime.onMessage.addListener(this.onMessage)
+ runtime.onMessage.addListener(this.onMessage);
//Add Update listener for tab
- tabs.onUpdated.addListener(this.onUpdatedTab)
+ tabs.onUpdated.addListener(this.onUpdatedTab);
//Add New tab create listener
- tabs.onCreated.addListener(this.onCreatedTab)
+ tabs.onCreated.addListener(this.onCreatedTab);
//Add tab change listener
- tabs.onActivated.addListener(this.handleTabChange)
- }
+ tabs.onActivated.addListener(this.handleTabChange);
+ };
//TODO: Listeners
@@ -54,8 +54,8 @@ class Background {
* Extension Installed
*/
onInstalled = () => {
- console.log('[===== Installed Extension!] =====')
- }
+ console.log('[===== Installed Extension!] =====');
+ };
/**
* Message Handler Function
@@ -66,13 +66,13 @@ class Background {
*/
onMessage = async (message: EXTMessage, sender: Runtime.MessageSender) => {
try {
- console.log('[===== Received message =====]', message, sender)
+ console.log('[===== Received message =====]', message, sender);
switch (message.type) {
case 'IS_MONETIZATION_READY': {
if (message?.data) {
- this.updateIcon(message.data.monetization)
+ this.updateIcon(message.data.monetization);
}
- break
+ break;
}
case 'SET_INCOMING_POINTER': {
@@ -80,66 +80,66 @@ class Background {
incomingPayment: receivingPaymentPointerUrl,
paymentPointer: sendingPaymentPointerUrl,
amount,
- } = message.data
+ } = message.data;
if (this.grantFlow?.sendingPaymentPointerUrl === sendingPaymentPointerUrl) {
if (!this.paymentStarted) {
- this.paymentStarted = true
- const currentTabId = await this.grantFlow?.getCurrentActiveTabId()
- await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' })
+ this.paymentStarted = true;
+ const currentTabId = await this.grantFlow?.getCurrentActiveTabId();
+ await tabs.sendMessage(currentTabId ?? 0, { type: 'START_PAYMENTS' });
}
} else {
this.grantFlow = new PaymentFlowService(
sendingPaymentPointerUrl,
receivingPaymentPointerUrl,
amount,
- )
+ );
- this.grantFlow.initPaymentFlow()
+ this.grantFlow.initPaymentFlow();
}
- break
+ break;
}
case 'GET_SENDING_PAYMENT_POINTER': {
if (this.grantFlow) {
- const { sendingPaymentPointerUrl, amount } = this.grantFlow
+ const { sendingPaymentPointerUrl, amount } = this.grantFlow;
return {
type: 'SUCCESS',
data: { sendingPaymentPointerUrl, amount, started: this.paymentStarted },
- }
+ };
}
return {
type: 'ERROR',
data: { sendingPaymentPointerUrl: '' },
- }
+ };
}
case 'RUN_PAYMENT': {
if (this.grantFlow) {
- this.grantFlow.sendPayment()
+ this.grantFlow.sendPayment();
this.spentAmount = Number(
parseFloat(String(this.spentAmount + 1000000 / 10 ** 9)).toFixed(3),
- )
- this.sendSpendAmount()
- this.paymentStarted = true
+ );
+ this.sendSpendAmount();
+ this.paymentStarted = true;
}
- break
+ break;
}
case 'PAUSE_PAYMENTS': {
- this.paymentStarted = false
- break
+ this.paymentStarted = false;
+ break;
}
}
- return true // result to reply
+ return true; // result to reply
} catch (error) {
- console.log('[===== Error in MessageListener =====]', error)
- return error
+ console.log('[===== Error in MessageListener =====]', error);
+ return error;
}
- }
+ };
/**
* Message from Long Live Connection
@@ -147,22 +147,22 @@ class Background {
* @param msg
*/
onMessageFromExtension = (msg: EXTMessage) => {
- console.log('[===== Message from Long Live Connection =====]', msg)
- }
+ console.log('[===== Message from Long Live Connection =====]', msg);
+ };
/**
*
* @param tab
*/
onCreatedTab = (tab: Tabs.Tab) => {
- console.log('[===== New Tab Created =====]', tab)
- }
+ console.log('[===== New Tab Created =====]', tab);
+ };
sendSpendAmount() {
runtime.sendMessage({
type: 'SPENT_AMOUNT',
data: { spentAmount: this.spentAmount },
- })
+ });
}
/**
@@ -174,12 +174,12 @@ class Background {
*/
onUpdatedTab = async (tabId: number, changeInfo: Tabs.OnUpdatedChangeInfoType, tab: Tabs.Tab) => {
if (tab.status === 'complete' && tab.url?.match(/^http/)) {
- const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' })
+ const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' });
if (response.data) {
- await this.updateIcon(response.data.monetization)
+ await this.updateIcon(response.data.monetization);
}
}
- }
+ };
/**
* Get url from tabId
@@ -187,13 +187,13 @@ class Background {
*/
getURLFromTab = async (tabId: number) => {
try {
- const tab = await tabs.get(tabId)
- return tab.url || ''
+ const tab = await tabs.get(tabId);
+ return tab.url || '';
} catch (error) {
- console.log(`[===== Could not get Tab Info$(tabId) in getURLFromTab =====]`, error)
- throw ''
+ console.log(`[===== Could not get Tab Info$(tabId) in getURLFromTab =====]`, error);
+ throw '';
}
- }
+ };
/**
* Open new tab by url
@@ -201,13 +201,13 @@ class Background {
*/
openNewTab = async (url: string) => {
try {
- const tab = await tabs.create({ url })
- return tab
+ const tab = await tabs.create({ url });
+ return tab;
} catch (error) {
- console.log(`[===== Error in openNewTab =====]`, error)
- return null
+ console.log(`[===== Error in openNewTab =====]`, error);
+ return null;
}
- }
+ };
/**
* Close specific tab
@@ -216,49 +216,49 @@ class Background {
*/
closeTab = async (tab: Tabs.Tab) => {
try {
- await tabs.remove(tab.id ?? 0)
+ await tabs.remove(tab.id ?? 0);
} catch (error) {
- console.log(`[===== Error in closeTab =====]`, error)
+ console.log(`[===== Error in closeTab =====]`, error);
}
- }
+ };
/**
* send message
*/
sendMessage = async (tab: Tabs.Tab, msg: EXTMessage) => {
try {
- const res = await tabs.sendMessage(tab.id ?? 0, msg)
- return res
+ const res = await tabs.sendMessage(tab.id ?? 0, msg);
+ return res;
} catch (error) {
- console.log(`[===== Error in sendMessage =====]`, error)
- return null
+ console.log(`[===== Error in sendMessage =====]`, error);
+ return null;
}
- }
+ };
updateIcon = async (active: boolean) => {
const iconData = {
'34': active ? iconActive34 : iconInactive34,
'128': active ? iconActive128 : iconInactive128,
- }
+ };
if (action) {
- await action.setIcon({ path: iconData })
+ await action.setIcon({ path: iconData });
} else if (chrome.browserAction) {
- chrome.browserAction.setIcon({ path: iconData })
+ chrome.browserAction.setIcon({ path: iconData });
}
- }
+ };
handleTabChange = async (activeInfo: chrome.tabs.TabActiveInfo) => {
- const tabId = activeInfo.tabId
+ const tabId = activeInfo.tabId;
- const tab = await tabs.get(tabId)
+ const tab = await tabs.get(tabId);
if (tab && tab.url?.includes('https') && tab.status === 'complete') {
- const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' })
+ const response = await this.sendMessage(tab, { type: 'IS_MONETIZATION_READY' });
if (response?.data) {
- this.updateIcon(response.data.monetization)
+ this.updateIcon(response.data.monetization);
}
}
- }
+ };
}
-export const background = new Background()
+export const background = new Background();
diff --git a/src/background/requestConfig.ts b/packages/extension/src/background/requestConfig.ts
similarity index 85%
rename from src/background/requestConfig.ts
rename to packages/extension/src/background/requestConfig.ts
index 44953061..3dd95a13 100644
--- a/src/background/requestConfig.ts
+++ b/packages/extension/src/background/requestConfig.ts
@@ -1,6 +1,6 @@
-import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios'
+import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
-import { SIGNATURES_URL } from '@/background/config'
+import { SIGNATURES_URL } from '@/background/config';
export const getAxiosInstance = (timeout = 10000): AxiosInstance => {
const axiosInstance = axios.create({
@@ -11,12 +11,12 @@ export const getAxiosInstance = (timeout = 10000): AxiosInstance => {
},
},
timeout,
- })
+ });
axiosInstance.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
if (!config.method || !config.url) {
- throw new Error('Cannot intercept request: url or method missing')
+ throw new Error('Cannot intercept request: url or method missing');
}
const payload = {
@@ -27,17 +27,17 @@ export const getAxiosInstance = (timeout = 10000): AxiosInstance => {
method: config.method.toUpperCase(),
url: config.url,
body: JSON.stringify(config.data),
- }
+ };
const contentAndSigHeaders = await axios.post(SIGNATURES_URL, payload, {
headers: { 'Content-Type': 'application/json' },
- })
+ });
- config.headers['Signature'] = contentAndSigHeaders.data['Signature']
- config.headers['Signature-Input'] = contentAndSigHeaders.data['Signature-Input']
- config.headers['Content-Digest'] = contentAndSigHeaders.data['Content-Digest']
+ config.headers['Signature'] = contentAndSigHeaders.data['Signature'];
+ config.headers['Signature-Input'] = contentAndSigHeaders.data['Signature-Input'];
+ config.headers['Content-Digest'] = contentAndSigHeaders.data['Content-Digest'];
- return config
+ return config;
},
undefined,
{
@@ -45,7 +45,7 @@ export const getAxiosInstance = (timeout = 10000): AxiosInstance => {
config.method?.toLowerCase() === 'post' ||
!!(config.headers && config.headers['Authorization']),
},
- )
+ );
- return axiosInstance
-}
+ return axiosInstance;
+};
diff --git a/src/components/__tests__/button.test.tsx b/packages/extension/src/components/__tests__/button.test.tsx
similarity index 58%
rename from src/components/__tests__/button.test.tsx
rename to packages/extension/src/components/__tests__/button.test.tsx
index a9a3b286..a1809236 100644
--- a/src/components/__tests__/button.test.tsx
+++ b/packages/extension/src/components/__tests__/button.test.tsx
@@ -1,60 +1,60 @@
-import { render } from '@testing-library/react'
-import React from 'react'
+import { render } from '@testing-library/react';
+import React from 'react';
-import { Button } from '@/components/button'
+import { Button } from '@/components/button';
describe('Button', () => {
it('should render a button with the `aria-label` attribute', () => {
- const { queryByRole } = render()
+ const { queryByRole } = render();
- expect(queryByRole('button')).toBeInTheDocument()
- expect(queryByRole('button')).toHaveAttribute('aria-label', 'test button')
- })
+ expect(queryByRole('button')).toBeInTheDocument();
+ expect(queryByRole('button')).toHaveAttribute('aria-label', 'test button');
+ });
it('should default to `type="button"`', () => {
- const { queryByRole } = render()
+ const { queryByRole } = render();
- expect(queryByRole('button')).toBeInTheDocument()
- expect(queryByRole('button')).toHaveAttribute('type', 'button')
- })
+ expect(queryByRole('button')).toBeInTheDocument();
+ expect(queryByRole('button')).toHaveAttribute('type', 'button');
+ });
it('should not have the `disabled` attribute and `aria-disabled="false"` if `loading` is false', () => {
- const { queryByRole } = render()
+ const { queryByRole } = render();
- expect(queryByRole('button')).toBeInTheDocument()
- expect(queryByRole('button')).not.toHaveAttribute('disabled')
- expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'false')
- expect(queryByRole('button')).not.toBeDisabled()
- })
+ expect(queryByRole('button')).toBeInTheDocument();
+ expect(queryByRole('button')).not.toHaveAttribute('disabled');
+ expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'false');
+ expect(queryByRole('button')).not.toBeDisabled();
+ });
it('should have the `disabled` and `aria-disabled="true"` attributes if `loading` is true', () => {
const { queryByRole } = render(
,
- )
+ );
- expect(queryByRole('button')).toBeInTheDocument()
- expect(queryByRole('button')).toHaveAttribute('disabled')
- expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'true')
- expect(queryByRole('button')).toBeDisabled()
- })
+ expect(queryByRole('button')).toBeInTheDocument();
+ expect(queryByRole('button')).toHaveAttribute('disabled');
+ expect(queryByRole('button')).toHaveAttribute('aria-disabled', 'true');
+ expect(queryByRole('button')).toBeDisabled();
+ });
it('should have the `bg-button-base` class by default', () => {
- const { queryByRole } = render()
+ const { queryByRole } = render();
- expect(queryByRole('button')).toBeInTheDocument()
- expect(queryByRole('button')).toHaveClass('bg-button-base')
- })
+ expect(queryByRole('button')).toBeInTheDocument();
+ expect(queryByRole('button')).toHaveClass('bg-button-base');
+ });
it('should have the `bg-error` class when the `destructive` variant is passed', () => {
const { queryByRole } = render(
,
- )
+ );
- expect(queryByRole('button')).toBeInTheDocument()
- expect(queryByRole('button')).toHaveClass('bg-error')
- })
-})
+ expect(queryByRole('button')).toBeInTheDocument();
+ expect(queryByRole('button')).toHaveClass('bg-error');
+ });
+});
diff --git a/src/components/__tests__/input.test.tsx b/packages/extension/src/components/__tests__/input.test.tsx
similarity index 62%
rename from src/components/__tests__/input.test.tsx
rename to packages/extension/src/components/__tests__/input.test.tsx
index 6f66e7a4..7541daef 100644
--- a/src/components/__tests__/input.test.tsx
+++ b/packages/extension/src/components/__tests__/input.test.tsx
@@ -1,55 +1,55 @@
-import { render } from '@testing-library/react'
-import React from 'react'
+import { render } from '@testing-library/react';
+import React from 'react';
-import { Input } from '@/components/input'
+import { Input } from '@/components/input';
describe('Input', () => {
it('should default to `type="text"`', () => {
- const { queryByLabelText } = render()
+ const { queryByLabelText } = render();
- expect(queryByLabelText('test input')).toBeInTheDocument()
- expect(queryByLabelText('test input')).toHaveAttribute('type', 'text')
- })
+ expect(queryByLabelText('test input')).toBeInTheDocument();
+ expect(queryByLabelText('test input')).toHaveAttribute('type', 'text');
+ });
it('should not have the `disabled` attribute and `aria-disabled="false"` if `loading` is false', () => {
- const { queryByLabelText } = render()
+ const { queryByLabelText } = render();
- expect(queryByLabelText('test input')).toBeInTheDocument()
- expect(queryByLabelText('test input')).not.toHaveAttribute('disabled')
- expect(queryByLabelText('test input')).toHaveAttribute('aria-disabled', 'false')
- expect(queryByLabelText('test input')).not.toBeDisabled()
- })
+ expect(queryByLabelText('test input')).toBeInTheDocument();
+ expect(queryByLabelText('test input')).not.toHaveAttribute('disabled');
+ expect(queryByLabelText('test input')).toHaveAttribute('aria-disabled', 'false');
+ expect(queryByLabelText('test input')).not.toBeDisabled();
+ });
it('should have the `border-base` class by default', () => {
- const { queryByLabelText } = render()
+ const { queryByLabelText } = render();
- expect(queryByLabelText('test input')).toBeInTheDocument()
- expect(queryByLabelText('test input')).toHaveClass('border-base')
- })
+ expect(queryByLabelText('test input')).toBeInTheDocument();
+ expect(queryByLabelText('test input')).toHaveClass('border-base');
+ });
it('should have the `pl-12` class when the `icon` variant is passed', () => {
- const { queryByLabelText } = render(} />)
+ const { queryByLabelText } = render(} />);
- expect(queryByLabelText('test input')).toBeInTheDocument()
- expect(queryByLabelText('test input')).toHaveClass('pl-12')
- })
+ expect(queryByLabelText('test input')).toBeInTheDocument();
+ expect(queryByLabelText('test input')).toHaveClass('pl-12');
+ });
it('should have the `bg-disabled` and `border-transparent` classes when the `disabled` variant is passed', () => {
- const { queryByLabelText } = render()
+ const { queryByLabelText } = render();
- expect(queryByLabelText('test input')).toBeInTheDocument()
- expect(queryByLabelText('test input')).toHaveClass('bg-disabled')
- expect(queryByLabelText('test input')).toHaveClass('border-transparent')
- })
+ expect(queryByLabelText('test input')).toBeInTheDocument();
+ expect(queryByLabelText('test input')).toHaveClass('bg-disabled');
+ expect(queryByLabelText('test input')).toHaveClass('border-transparent');
+ });
it('should have the `aria-invalid` and `aria-describedby` attributes if errorMessage is present', () => {
const { queryByLabelText, queryByText } = render(
,
- )
-
- expect(queryByLabelText('test input')).toBeInTheDocument()
- expect(queryByLabelText('test input')).toHaveAttribute('aria-invalid')
- expect(queryByLabelText('test input')).toHaveAttribute('aria-describedby')
- expect(queryByText('some error')).toBeInTheDocument()
- })
-})
+ );
+
+ expect(queryByLabelText('test input')).toBeInTheDocument();
+ expect(queryByLabelText('test input')).toHaveAttribute('aria-invalid');
+ expect(queryByLabelText('test input')).toHaveAttribute('aria-describedby');
+ expect(queryByText('some error')).toBeInTheDocument();
+ });
+});
diff --git a/packages/extension/src/components/__tests__/radio-group.test.tsx b/packages/extension/src/components/__tests__/radio-group.test.tsx
new file mode 100644
index 00000000..41cdfb5d
--- /dev/null
+++ b/packages/extension/src/components/__tests__/radio-group.test.tsx
@@ -0,0 +1,106 @@
+import { fireEvent, render } from '@testing-library/react';
+import React from 'react';
+
+import { Radio, RadioGroup } from '../radio-group';
+
+describe('RadioGroup', () => {
+ const radioItems = [
+ { label: 'Option 1', value: 'option1', checked: true },
+ { label: 'Option 2', value: 'option2' },
+ ];
+
+ it('should have the `flex-row` class when the `inline` variant is passed', () => {
+ const { queryByRole } = render(
+ ,
+ );
+
+ expect(queryByRole('radiogroup')).toBeInTheDocument();
+ expect(queryByRole('radiogroup')).toHaveClass('flex-row');
+ });
+
+ it('renders radio group correctly with items', () => {
+ const { getByRole } = render();
+
+ const radioGroup = getByRole('radiogroup');
+ expect(radioGroup).toBeInTheDocument();
+ expect(radioGroup.childNodes.length).toBe(2); // Ensure two radio buttons are rendered
+ });
+
+ it('renders radio group with no element checked by default', () => {
+ const radioItemsNotChecked = [
+ { label: 'Option 1', value: 'option1' },
+ { label: 'Option 2', value: 'option2' },
+ ];
+ const { getByLabelText } = render(
+ ,
+ );
+
+ const firstRadioButton = getByLabelText('Option 1');
+ const secondRadioButton = getByLabelText('Option 2');
+
+ expect(firstRadioButton).not.toBeChecked();
+ expect(secondRadioButton).not.toBeChecked();
+ });
+
+ it('handles keyboard navigation', () => {
+ const { getByLabelText } = render();
+
+ const radioGroup = getByLabelText('Option 1');
+ fireEvent.keyDown(radioGroup, { key: 'ArrowRight', code: 'ArrowRight' });
+ let secondRadioButton = getByLabelText('Option 2');
+ expect(secondRadioButton).toBeChecked();
+
+ fireEvent.keyDown(radioGroup, { key: 'ArrowLeft', code: 'ArrowLeft' });
+ let firstRadioButton = getByLabelText('Option 1');
+ expect(firstRadioButton).toBeChecked();
+
+ fireEvent.keyDown(radioGroup, { key: 'ArrowUp', code: 'ArrowUp' });
+ secondRadioButton = getByLabelText('Option 2');
+ expect(secondRadioButton).toBeChecked();
+
+ fireEvent.keyDown(radioGroup, { key: 'ArrowDown', code: 'ArrowDown' });
+ firstRadioButton = getByLabelText('Option 1');
+ expect(firstRadioButton).toBeChecked();
+ });
+
+ it('changes selection on arrow keys', () => {
+ const { getByLabelText } = render();
+
+ const radioGroup = getByLabelText('Option 1');
+ fireEvent.keyDown(radioGroup, { key: 'ArrowRight', code: 'ArrowRight' });
+ fireEvent.keyDown(radioGroup, { key: 'Enter', code: 'Enter' });
+ const secondRadioButton = getByLabelText('Option 2');
+ expect(secondRadioButton).toBeChecked();
+ });
+
+ it('changes selection on clicking radio buttons', () => {
+ const { getByLabelText } = render();
+
+ const secondRadioButton = getByLabelText('Option 2');
+ fireEvent.click(secondRadioButton);
+ expect(secondRadioButton).toBeChecked();
+ });
+});
+
+describe('Radio', () => {
+ it('renders radio button correctly with label', () => {
+ const { getByLabelText } = render();
+
+ const radioButton = getByLabelText('Option 1');
+ expect(radioButton).toBeInTheDocument();
+ expect(radioButton).toHaveAttribute('type', 'radio');
+ expect(radioButton).not.toBeChecked();
+
+ fireEvent.click(radioButton);
+ expect(radioButton).toBeChecked();
+ });
+
+ it('renders disabled radio button', () => {
+ const { getByLabelText } = render(
+ ,
+ );
+
+ const radioButton = getByLabelText('Option 1');
+ expect(radioButton).toBeDisabled();
+ });
+});
diff --git a/packages/extension/src/components/__tests__/switch.test.tsx b/packages/extension/src/components/__tests__/switch.test.tsx
new file mode 100644
index 00000000..259a0d72
--- /dev/null
+++ b/packages/extension/src/components/__tests__/switch.test.tsx
@@ -0,0 +1,69 @@
+import '@testing-library/jest-dom';
+
+import { fireEvent, render, screen } from '@testing-library/react';
+import React from 'react';
+
+import { Switch } from '@/components/switch';
+
+describe('Switch', () => {
+ it('renders without crashing', () => {
+ render();
+ expect(screen.getByRole('switch')).toBeInTheDocument();
+ });
+
+ it('applies default classes', () => {
+ render();
+ const switchElement = screen.getByRole('switch').nextSibling;
+ expect(switchElement).toHaveClass('w-[42px] h-[26px] before:h-5 before:w-5');
+ });
+
+ it('applies small size classes when size prop is small', () => {
+ render();
+ const switchElement = screen.getByRole('switch').nextSibling;
+ expect(switchElement).toHaveClass('w-9 h-[22px] before:h-4 before:w-4 before:left-[3px]');
+ });
+
+ it('forwards ref to input element', () => {
+ const ref = React.createRef();
+ render();
+ expect(ref.current).toBeInstanceOf(HTMLInputElement);
+ });
+
+ it('forwards checked prop to input element', () => {
+ render();
+ const inputElement = screen.getByRole('switch');
+ expect(inputElement).toBeChecked();
+ });
+
+ it('handles additional props', () => {
+ render();
+ const inputElement = screen.getByRole('switch');
+ expect(inputElement).toHaveAttribute('aria-label', 'Custom Switch');
+ });
+
+ it('applies custom class names', () => {
+ const customClass = 'custom-class';
+ render();
+ const switchElement = screen.getByRole('switch').nextSibling;
+ expect(switchElement).toHaveClass(customClass);
+ });
+
+ it('toggles switch state when clicked', () => {
+ render();
+ const inputElement = screen.getByRole('switch');
+ expect(inputElement).not.toBeChecked();
+
+ fireEvent.click(inputElement);
+ expect(inputElement).toBeChecked();
+
+ fireEvent.click(inputElement);
+ expect(inputElement).not.toBeChecked();
+ });
+
+ it('handles additional HTML attributes', () => {
+ const testId = 'switch-test';
+ render();
+ const switchElement = screen.getByTestId(testId);
+ expect(switchElement).toBeInTheDocument();
+ });
+});
diff --git a/src/components/button.tsx b/packages/extension/src/components/button.tsx
similarity index 86%
rename from src/components/button.tsx
rename to packages/extension/src/components/button.tsx
index 4566fc04..6167d342 100644
--- a/src/components/button.tsx
+++ b/packages/extension/src/components/button.tsx
@@ -1,8 +1,8 @@
-import { type VariantProps, cva } from 'class-variance-authority'
-import React, { forwardRef } from 'react'
+import { type VariantProps, cva } from 'class-variance-authority';
+import React, { forwardRef } from 'react';
-import { LoadingSpinner } from '@/components/loading-spinner'
-import { cn } from '@/utils/cn'
+import { LoadingSpinner } from '@/components/loading-spinner';
+import { cn } from '@/utils/cn';
const buttonVariants = cva(
[
@@ -32,13 +32,13 @@ const buttonVariants = cva(
size: 'default',
},
},
-)
+);
export interface ButtonProps
extends VariantProps,
React.ButtonHTMLAttributes {
- loading?: boolean
- ['aria-label']: string
+ loading?: boolean;
+ ['aria-label']: string;
}
export const Button = forwardRef(function Button(
@@ -52,8 +52,9 @@ export const Button = forwardRef(function Button
className={cn(buttonVariants({ variant, size, fullWidth, loading }), className)}
disabled={props.disabled ?? loading ?? false}
aria-disabled={props.disabled ?? loading ?? false}
- {...props}>
+ {...props}
+ >
{loading ? : children}
- )
-})
+ );
+});
diff --git a/src/components/icons.tsx b/packages/extension/src/components/icons.tsx
similarity index 96%
rename from src/components/icons.tsx
rename to packages/extension/src/components/icons.tsx
index 11950a58..ddcbf0d0 100644
--- a/src/components/icons.tsx
+++ b/packages/extension/src/components/icons.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import React from 'react';
export const Spinner = (props: React.SVGProps) => {
return (
@@ -7,7 +7,8 @@ export const Spinner = (props: React.SVGProps) => {
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 16 16"
- {...props}>
+ {...props}
+ >
) => {
d="M2.204 6.447A6 6 0 108 2"
/>
- )
-}
+ );
+};
export const ArrowBack = (props: React.SVGProps) => {
return (
@@ -26,7 +27,8 @@ export const ArrowBack = (props: React.SVGProps) => {
viewBox="0 0 25 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
- {...props}>
+ {...props}
+ >
@@ -37,8 +39,8 @@ export const ArrowBack = (props: React.SVGProps) => {
/>
- )
-}
+ );
+};
export const Settings = (props: React.SVGProps) => {
return (
@@ -48,7 +50,8 @@ export const Settings = (props: React.SVGProps) => {
height="25"
viewBox="0 0 25 25"
fill="none"
- {...props}>
+ {...props}
+ >
@@ -59,8 +62,8 @@ export const Settings = (props: React.SVGProps) => {
/>
- )
-}
+ );
+};
export const DollarSign = (props: React.SVGProps) => {
return (
@@ -70,7 +73,8 @@ export const DollarSign = (props: React.SVGProps) => {
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
- {...props}>
+ {...props}
+ >
) => {
x="0"
y="0"
width="24"
- height="24">
+ height="24"
+ >
@@ -91,5 +96,5 @@ export const DollarSign = (props: React.SVGProps) => {
- )
-}
+ );
+};
diff --git a/src/components/input.tsx b/packages/extension/src/components/input.tsx
similarity index 88%
rename from src/components/input.tsx
rename to packages/extension/src/components/input.tsx
index c98ec04e..b25789d0 100644
--- a/src/components/input.tsx
+++ b/packages/extension/src/components/input.tsx
@@ -1,7 +1,7 @@
-import { type VariantProps, cva } from 'class-variance-authority'
-import React, { forwardRef } from 'react'
+import { type VariantProps, cva } from 'class-variance-authority';
+import React, { forwardRef } from 'react';
-import { cn } from '@/utils/cn'
+import { cn } from '@/utils/cn';
const inputVariants = cva(
[
@@ -23,14 +23,14 @@ const inputVariants = cva(
variant: 'default',
},
},
-)
+);
export interface InputProps
extends VariantProps,
React.InputHTMLAttributes {
- errorMessage?: string
- disabled?: boolean
- icon?: React.ReactNode
+ errorMessage?: string;
+ disabled?: boolean;
+ icon?: React.ReactNode;
}
export const Input = forwardRef(function Input(
@@ -52,5 +52,5 @@ export const Input = forwardRef(function Input(
/>
{errorMessage && {errorMessage}
}
- )
-})
+ );
+});
diff --git a/src/components/label.tsx b/packages/extension/src/components/label.tsx
similarity index 77%
rename from src/components/label.tsx
rename to packages/extension/src/components/label.tsx
index 94f6063f..b3621174 100644
--- a/src/components/label.tsx
+++ b/packages/extension/src/components/label.tsx
@@ -1,14 +1,14 @@
-import { type VariantProps, cva } from 'class-variance-authority'
-import React, { forwardRef } from 'react'
+import { type VariantProps, cva } from 'class-variance-authority';
+import React, { forwardRef } from 'react';
-import { cn } from '@/utils/cn'
+import { cn } from '@/utils/cn';
-const labelVariants = cva('text-medium font-medium leading-6 px-2 flex items-center gap-2')
+const labelVariants = cva('text-medium font-medium leading-6 px-2 flex items-center gap-2');
export interface LabelProps
extends VariantProps,
React.LabelHTMLAttributes {
- children: React.ReactNode
+ children: React.ReactNode;
}
export const Label = forwardRef(function Label(
@@ -19,5 +19,5 @@ export const Label = forwardRef(function Label(
- )
-})
+ );
+});
diff --git a/src/components/layout/header.tsx b/packages/extension/src/components/layout/header.tsx
similarity index 68%
rename from src/components/layout/header.tsx
rename to packages/extension/src/components/layout/header.tsx
index 77b84fb5..3b9ce42f 100644
--- a/src/components/layout/header.tsx
+++ b/packages/extension/src/components/layout/header.tsx
@@ -1,14 +1,14 @@
-import React, { useMemo } from 'react'
-import { Link, useLocation } from 'react-router-dom'
-import { runtime } from 'webextension-polyfill'
+import React, { useMemo } from 'react';
+import { Link, useLocation } from 'react-router-dom';
+import { runtime } from 'webextension-polyfill';
-import { ArrowBack, Settings } from '../icons'
-import { ROUTES } from '../router-provider'
+import { ArrowBack, Settings } from '../icons';
+import { ROUTES } from '../router-provider';
-const Logo = runtime.getURL('assets/images/logo.svg')
+const Logo = runtime.getURL('assets/images/logo.svg');
const NavigationButton = () => {
- const location = useLocation()
+ const location = useLocation();
const component = useMemo(
() =>
@@ -23,10 +23,10 @@ const NavigationButton = () => {
),
[location],
- )
+ );
- return component
-}
+ return component;
+};
export const Header = () => {
return (
@@ -39,5 +39,5 @@ export const Header = () => {
- )
-}
+ );
+};
diff --git a/src/components/layout/main-layout.tsx b/packages/extension/src/components/layout/main-layout.tsx
similarity index 56%
rename from src/components/layout/main-layout.tsx
rename to packages/extension/src/components/layout/main-layout.tsx
index 92fabac9..7c53174a 100644
--- a/src/components/layout/main-layout.tsx
+++ b/packages/extension/src/components/layout/main-layout.tsx
@@ -1,11 +1,11 @@
-import React from 'react'
-import { Outlet } from 'react-router-dom'
+import React from 'react';
+import { Outlet } from 'react-router-dom';
-import { Header } from './header'
+import { Header } from './header';
const Divider = () => {
- return
-}
+ return ;
+};
export const MainLayout = () => {
return (
@@ -16,5 +16,5 @@ export const MainLayout = () => {
- )
-}
+ );
+};
diff --git a/src/components/loading-spinner.tsx b/packages/extension/src/components/loading-spinner.tsx
similarity index 81%
rename from src/components/loading-spinner.tsx
rename to packages/extension/src/components/loading-spinner.tsx
index c42a723c..d0d6f4b9 100644
--- a/src/components/loading-spinner.tsx
+++ b/packages/extension/src/components/loading-spinner.tsx
@@ -1,7 +1,7 @@
-import { type VariantProps, cva } from 'class-variance-authority'
-import React from 'react'
+import { type VariantProps, cva } from 'class-variance-authority';
+import React from 'react';
-import { Spinner } from '@/components/icons'
+import { Spinner } from '@/components/icons';
const loadingSpinnerStyles = cva('animate-spin text-white', {
variants: {
@@ -13,10 +13,10 @@ const loadingSpinnerStyles = cva('animate-spin text-white', {
defaultVariants: {
variant: 'lg',
},
-})
+});
-export type LoadingIndicatorProps = VariantProps
+export type LoadingIndicatorProps = VariantProps;
export const LoadingSpinner = ({ variant }: LoadingIndicatorProps) => {
- return
-}
+ return ;
+};
diff --git a/src/components/radio-group.tsx b/packages/extension/src/components/radio-group.tsx
similarity index 74%
rename from src/components/radio-group.tsx
rename to packages/extension/src/components/radio-group.tsx
index 862cc0a4..b6db2594 100644
--- a/src/components/radio-group.tsx
+++ b/packages/extension/src/components/radio-group.tsx
@@ -1,17 +1,17 @@
-import { type VariantProps, cva } from 'class-variance-authority'
-import React, { useEffect, useMemo, useState } from 'react'
+import { type VariantProps, cva } from 'class-variance-authority';
+import React, { useEffect, useMemo, useState } from 'react';
-import { cn } from '@/utils/cn'
+import { cn } from '@/utils/cn';
export interface RadioProps {
- checked?: boolean
- label?: string
- value: string
- name: string
- id?: string
- disabled?: boolean
- onChange?: any
- noSelected?: boolean
+ checked?: boolean;
+ label?: string;
+ value: string;
+ name: string;
+ id?: string;
+ disabled?: boolean;
+ onChange?: any;
+ noSelected?: boolean;
}
export const Radio = ({
@@ -24,12 +24,12 @@ export const Radio = ({
checked,
noSelected,
}: RadioProps): JSX.Element => {
- const inputId = id || `id-${name}-${value}`
- const divId = `div-${inputId}`
+ const inputId = id || `id-${name}-${value}`;
+ const divId = `div-${inputId}`;
useEffect(() => {
- if (checked) document.getElementById(divId)?.focus()
- }, [checked, divId])
+ if (checked) document.getElementById(divId)?.focus();
+ }, [checked, divId]);
return (
+ role="radio"
+ >
{label} : ''}
- )
-}
+ );
+};
const radioGroupVariants = cva(['flex gap-3'], {
variants: {
@@ -78,14 +79,14 @@ const radioGroupVariants = cva(['flex gap-3'], {
defaultVariants: {
variant: 'default',
},
-})
+});
export interface RadioGroupProps
extends VariantProps,
React.InputHTMLAttributes {
- disabled?: boolean
- items: Omit[]
- name: string
+ disabled?: boolean;
+ items: Omit[];
+ name: string;
}
export const RadioGroup = ({
@@ -96,42 +97,43 @@ export const RadioGroup = ({
disabled,
className,
}: RadioGroupProps) => {
- const checkedItem = useMemo(() => items.findIndex(item => item.checked), [items])
- const [selected, setSelected] = useState(checkedItem)
+ const checkedItem = useMemo(() => items.findIndex(item => item.checked), [items]);
+ const [selected, setSelected] = useState(checkedItem);
const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.code === 'ArrowRight' || event.code === 'ArrowDown') {
- event.preventDefault()
+ event.preventDefault();
- const nextIndex = (selected >= 0 ? selected + 1 : 1) % items.length
- setSelected(nextIndex)
+ const nextIndex = (selected >= 0 ? selected + 1 : 1) % items.length;
+ setSelected(nextIndex);
} else if (event.code === 'ArrowLeft' || event.code === 'ArrowUp') {
- event.preventDefault()
+ event.preventDefault();
- const prevIndex = selected > 0 ? selected - 1 : items.length - 1
- setSelected(prevIndex)
+ const prevIndex = selected > 0 ? selected - 1 : items.length - 1;
+ setSelected(prevIndex);
}
- }
+ };
useEffect(() => {
const handleKeyPress = (event: KeyboardEvent) => {
if (selected === -1 && (event.code === 'Enter' || event.code === 'Space')) {
- setSelected(0)
+ setSelected(0);
}
- }
+ };
- document.addEventListener('keypress', handleKeyPress)
+ document.addEventListener('keypress', handleKeyPress);
return () => {
- document.removeEventListener('keypress', handleKeyPress)
- }
- }, [selected])
+ document.removeEventListener('keypress', handleKeyPress);
+ };
+ }, [selected]);
return (
+ role="radiogroup"
+ >
{items.map((item, index) => (
))}
- )
-}
+ );
+};
diff --git a/src/components/router-provider.tsx b/packages/extension/src/components/router-provider.tsx
similarity index 65%
rename from src/components/router-provider.tsx
rename to packages/extension/src/components/router-provider.tsx
index 076bcd74..7f6a083e 100644
--- a/src/components/router-provider.tsx
+++ b/packages/extension/src/components/router-provider.tsx
@@ -1,14 +1,14 @@
/* eslint-disable simple-import-sort/imports */
-import React from 'react'
-import { MemoryRouter, Routes, Route } from 'react-router-dom'
-import { Home } from '@/popup/pages/Home'
-import { Settings } from '@/popup/pages/Settings'
-import { MainLayout } from './layout/main-layout'
+import React from 'react';
+import { MemoryRouter, Routes, Route } from 'react-router-dom';
+import { Home } from '@/popup/pages/Home';
+import { Settings } from '@/popup/pages/Settings';
+import { MainLayout } from './layout/main-layout';
export const ROUTES = {
INDEX: 'index',
SETTINGS: 'settings',
-}
+};
export const RouterProvider = () => (
@@ -19,4 +19,4 @@ export const RouterProvider = () => (
-)
+);
diff --git a/src/components/slider.tsx b/packages/extension/src/components/slider.tsx
similarity index 85%
rename from src/components/slider.tsx
rename to packages/extension/src/components/slider.tsx
index 2e8b454f..65b99203 100644
--- a/src/components/slider.tsx
+++ b/packages/extension/src/components/slider.tsx
@@ -1,22 +1,22 @@
-import React, { forwardRef } from 'react'
+import React, { forwardRef } from 'react';
-import { cn } from '@/utils/cn'
+import { cn } from '@/utils/cn';
export interface SliderProps extends React.InputHTMLAttributes {
- errorMessage?: string
- disabled?: boolean
- icon?: React.ReactNode
- min?: number
- max?: number
- value?: number
- defaultValue?: number
+ errorMessage?: string;
+ disabled?: boolean;
+ icon?: React.ReactNode;
+ min?: number;
+ max?: number;
+ value?: number;
+ defaultValue?: number;
}
export const Slider = forwardRef(function Slider(
{ errorMessage, defaultValue, value, className, disabled, ...props },
ref,
) {
- const [innerValue, setInnerValue] = React.useState(value || defaultValue || 0)
+ const [innerValue, setInnerValue] = React.useState(value || defaultValue || 0);
return (
@@ -53,5 +53,5 @@ export const Slider = forwardRef
(function Slider(
{errorMessage && {errorMessage}
}
- )
-})
+ );
+});
diff --git a/src/components/switch.tsx b/packages/extension/src/components/switch.tsx
similarity index 92%
rename from src/components/switch.tsx
rename to packages/extension/src/components/switch.tsx
index 5771072c..65a52dbf 100644
--- a/src/components/switch.tsx
+++ b/packages/extension/src/components/switch.tsx
@@ -1,7 +1,7 @@
-import { type VariantProps, cva } from 'class-variance-authority'
-import React, { forwardRef } from 'react'
+import { type VariantProps, cva } from 'class-variance-authority';
+import React, { forwardRef } from 'react';
-import { cn } from '@/utils/cn'
+import { cn } from '@/utils/cn';
const switchVariants = cva(
[
@@ -27,12 +27,12 @@ const switchVariants = cva(
size: 'default',
},
},
-)
+);
export interface SwitchProps
extends VariantProps,
React.HTMLAttributes {
- checked?: boolean
+ checked?: boolean;
}
export const Switch = forwardRef(function Switch(
@@ -50,5 +50,5 @@ export const Switch = forwardRef(function Switch(
/>
- )
-})
+ );
+});
diff --git a/packages/extension/src/content/index.tsx b/packages/extension/src/content/index.tsx
new file mode 100755
index 00000000..fe96294f
--- /dev/null
+++ b/packages/extension/src/content/index.tsx
@@ -0,0 +1,26 @@
+import { runtime } from 'webextension-polyfill';
+
+import { initMonetizationTagManager } from '@/utils/monetizationTagManager';
+import { wm2Polyfill } from '@/utils/polyfill';
+
+import { loadObserver } from './linksObserver';
+import MessageListener from './messageListener';
+
+// import "./content.css";
+
+runtime.onMessage.addListener(MessageListener);
+
+function inject(configure: (_script: HTMLScriptElement) => void) {
+ const script = document.createElement('script');
+ configure(script);
+ document.documentElement.appendChild(script);
+ // document.documentElement.removeChild(script)
+}
+
+// eslint-disable-next-line @typescript-eslint/no-extra-semi
+(function injectCode(code: string) {
+ inject(script => (script.innerHTML = code));
+})(wm2Polyfill);
+
+loadObserver();
+initMonetizationTagManager();
diff --git a/src/content/linksObserver.ts b/packages/extension/src/content/linksObserver.ts
similarity index 74%
rename from src/content/linksObserver.ts
rename to packages/extension/src/content/linksObserver.ts
index 727f871c..fadf6f2b 100644
--- a/src/content/linksObserver.ts
+++ b/packages/extension/src/content/linksObserver.ts
@@ -7,38 +7,38 @@ const listenForLinkChange = (mutationsList: MutationRecord[]) => {
link.getAttribute('href')?.match(/^http/) &&
link.getAttribute('rel')?.match(/monetization/)
) {
- acc.push(link)
+ acc.push(link);
}
- return acc
+ return acc;
},
[],
- )
+ );
if (monetizationLinks.length) {
- console.log(monetizationLinks)
+ console.log(monetizationLinks);
}
}
if (mutationsList[0].type === 'attributes') {
- const target = mutationsList[0].target as HTMLElement
+ const target = mutationsList[0].target as HTMLElement;
if (
target.tagName?.toLowerCase() === 'link' &&
target.getAttribute('href')?.match(/^http/) &&
target.getAttribute('rel')?.match(/monetization/)
) {
- console.log('LINK ATTR CHANGED', target)
+ console.log('LINK ATTR CHANGED', target);
}
}
-}
+};
export const loadObserver = () => {
- const observer = new MutationObserver(listenForLinkChange)
+ const observer = new MutationObserver(listenForLinkChange);
const observeOptions = {
attributes: true,
childList: true,
subtree: true,
- }
+ };
- observer.observe(document, observeOptions)
-}
+ observer.observe(document, observeOptions);
+};
diff --git a/src/content/messageListener.ts b/packages/extension/src/content/messageListener.ts
similarity index 81%
rename from src/content/messageListener.ts
rename to packages/extension/src/content/messageListener.ts
index e61fed35..1142ea89 100644
--- a/src/content/messageListener.ts
+++ b/packages/extension/src/content/messageListener.ts
@@ -1,8 +1,8 @@
// import { Runtime } from 'webextension-polyfill'
-import { PaymentSender } from '@/content/monetization'
+import { PaymentSender } from '@/content/monetization';
-const paymentSender = new PaymentSender()
+const paymentSender = new PaymentSender();
export const onRequest = async (
msg: EXTMessage,
@@ -12,7 +12,7 @@ export const onRequest = async (
switch (msg.type) {
case 'IS_MONETIZATION_READY': {
- const monetizationTag = document.querySelector('link[rel="monetization"]')
+ const monetizationTag = document.querySelector('link[rel="monetization"]');
return {
type: 'SUCCESS',
@@ -20,21 +20,21 @@ export const onRequest = async (
monetization: !!monetizationTag,
paymentPointer: monetizationTag?.getAttribute('href'),
},
- }
+ };
}
case 'START_PAYMENTS': {
- paymentSender.start()
- break
+ paymentSender.start();
+ break;
}
case 'STOP_PAYMENTS': {
- paymentSender.stop()
- break
+ paymentSender.stop();
+ break;
}
case 'PAYMENT_SUCCESS': {
- const { receiveAmount, incomingPayment, paymentPointer } = msg.data
+ const { receiveAmount, incomingPayment, paymentPointer } = msg.data;
window.dispatchEvent(
new CustomEvent('monetization-v2', {
@@ -51,14 +51,14 @@ export const onRequest = async (
receipt: null,
},
}),
- )
+ );
- break
+ break;
}
default:
- return { type: 'SUCCESS' }
+ return { type: 'SUCCESS' };
}
-}
+};
-export default onRequest
+export default onRequest;
diff --git a/src/content/monetization.tsx b/packages/extension/src/content/monetization.tsx
similarity index 51%
rename from src/content/monetization.tsx
rename to packages/extension/src/content/monetization.tsx
index c7043939..fb256556 100644
--- a/src/content/monetization.tsx
+++ b/packages/extension/src/content/monetization.tsx
@@ -1,12 +1,12 @@
-import { sendMessage } from '@/utils/sendMessages'
+import { sendMessage } from '@/utils/sendMessages';
export class PaymentSender {
- sender: any
+ sender: any;
start() {
this.sender = setInterval(() => {
- this.send()
- }, 1000)
+ this.send();
+ }, 1000);
// stop payments manually after 3 seconds
// setTimeout(() => {
@@ -15,11 +15,11 @@ export class PaymentSender {
}
stop() {
- sendMessage({ type: 'PAUSE_PAYMENTS' })
- clearInterval(this.sender)
+ sendMessage({ type: 'PAUSE_PAYMENTS' });
+ clearInterval(this.sender);
}
send() {
- sendMessage({ type: 'RUN_PAYMENT' })
+ sendMessage({ type: 'RUN_PAYMENT' });
}
}
diff --git a/packages/extension/src/manifest/chrome-v3.json b/packages/extension/src/manifest/chrome-v3.json
new file mode 100644
index 00000000..60aa11ea
--- /dev/null
+++ b/packages/extension/src/manifest/chrome-v3.json
@@ -0,0 +1,39 @@
+{
+ "name": "__MSG_appName__",
+ "version": "1.0.1",
+ "manifest_version": 3,
+ "description": "__MSG_appDescription__",
+ "icons": {
+ "34": "assets/icons/icon-34.png",
+ "128": "assets/icons/icon-128.png"
+ },
+ "default_locale": "en",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*", "https://*/*"],
+ "js": ["content/content.js"]
+ }
+ ],
+ "background": {
+ "service_worker": "background/background.js"
+ },
+ "permissions": ["tabs", "storage"],
+ "host_permissions": ["http://*/*", "https://*/*"],
+ "options_ui": {
+ "page": "options/index.html"
+ },
+ "action": {
+ "default_icon": {
+ "16": "assets/icons/icon-16.png",
+ "48": "assets/icons/icon-48.png"
+ },
+ "default_title": "Web Monetization",
+ "default_popup": "popup/index.html"
+ },
+ "web_accessible_resources": [
+ {
+ "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"],
+ "matches": [""]
+ }
+ ]
+}
diff --git a/src/manifest/chrome.json b/packages/extension/src/manifest/chrome.json
similarity index 100%
rename from src/manifest/chrome.json
rename to packages/extension/src/manifest/chrome.json
diff --git a/packages/extension/src/manifest/edge.json b/packages/extension/src/manifest/edge.json
new file mode 100644
index 00000000..631cd6eb
--- /dev/null
+++ b/packages/extension/src/manifest/edge.json
@@ -0,0 +1,27 @@
+{
+ "name": "__MSG_appName__",
+ "version": "1.0.1",
+ "manifest_version": 3,
+ "description": "__MSG_appDescription__",
+ "icons": {
+ "34": "assets/icons/icon-34.png",
+ "128": "assets/icons/icon-128.png"
+ },
+ "default_locale": "en",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*", "https://*/*"],
+ "js": ["content/content.js"]
+ }
+ ],
+ "background": {
+ "service_worker": "background/background.js"
+ },
+ "host_permissions": ["http://*/*", "https://*/*"],
+ "web_accessible_resources": [
+ {
+ "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"],
+ "matches": [""]
+ }
+ ]
+}
diff --git a/packages/extension/src/manifest/firefox-v3.json b/packages/extension/src/manifest/firefox-v3.json
new file mode 100644
index 00000000..a7b770aa
--- /dev/null
+++ b/packages/extension/src/manifest/firefox-v3.json
@@ -0,0 +1,47 @@
+{
+ "name": "__MSG_appName__",
+ "version": "1.0.1",
+ "manifest_version": 3,
+ "description": "__MSG_appDescription__",
+ "icons": {
+ "34": "assets/icons/icon-34.png",
+ "128": "assets/icons/icon-128.png"
+ },
+ "default_locale": "en",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*", "https://*/*"],
+ "js": ["content/content.js"]
+ }
+ ],
+ "background": {
+ "scripts": ["background/background.js"],
+ "type": "module"
+ },
+ "permissions": ["tabs", "storage"],
+ "host_permissions": ["http://*/*", "https://*/*"],
+ "options_ui": {
+ "page": "options/index.html",
+ "browser_style": false
+ },
+ "action": {
+ "default_icon": {
+ "16": "assets/icons/icon-16.png",
+ "48": "assets/icons/icon-48.png"
+ },
+ "default_title": "Web Monetization",
+ "default_popup": "popup/index.html",
+ "browser_style": false
+ },
+ "web_accessible_resources": [
+ {
+ "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"],
+ "matches": [""]
+ }
+ ],
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "web-extension@interledger.com"
+ }
+ }
+}
diff --git a/packages/extension/src/manifest/firefox.json b/packages/extension/src/manifest/firefox.json
new file mode 100644
index 00000000..275b1555
--- /dev/null
+++ b/packages/extension/src/manifest/firefox.json
@@ -0,0 +1,34 @@
+{
+ "name": "__MSG_appName__",
+ "version": "1.0.1",
+ "manifest_version": 2,
+ "description": "__MSG_appDescription__",
+ "icons": {
+ "34": "assets/icons/icon-34.png",
+ "128": "assets/icons/icon-128.png"
+ },
+ "default_locale": "en",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*", "https://*/*", ""],
+ "js": ["content/content.js"]
+ }
+ ],
+ "background": {
+ "scripts": ["background/background.js"],
+ "type": "module"
+ },
+ "permissions": ["tabs", "storage"],
+ "browser_action": {
+ "default_icon": "assets/icons/icon-34.png",
+ "default_title": "Web Monetization",
+ "default_popup": "popup/index.html",
+ "browser_style": false
+ },
+ "web_accessible_resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"],
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "web-extension@interledger.com"
+ }
+ }
+}
diff --git a/packages/extension/src/manifest/opera.json b/packages/extension/src/manifest/opera.json
new file mode 100644
index 00000000..631cd6eb
--- /dev/null
+++ b/packages/extension/src/manifest/opera.json
@@ -0,0 +1,27 @@
+{
+ "name": "__MSG_appName__",
+ "version": "1.0.1",
+ "manifest_version": 3,
+ "description": "__MSG_appDescription__",
+ "icons": {
+ "34": "assets/icons/icon-34.png",
+ "128": "assets/icons/icon-128.png"
+ },
+ "default_locale": "en",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*", "https://*/*"],
+ "js": ["content/content.js"]
+ }
+ ],
+ "background": {
+ "service_worker": "background/background.js"
+ },
+ "host_permissions": ["http://*/*", "https://*/*"],
+ "web_accessible_resources": [
+ {
+ "resources": ["assets/*", "content/*", "options/*", "popup/*", "background/*"],
+ "matches": [""]
+ }
+ ]
+}
diff --git a/src/popup/Popup.scss b/packages/extension/src/popup/Popup.scss
similarity index 100%
rename from src/popup/Popup.scss
rename to packages/extension/src/popup/Popup.scss
diff --git a/packages/extension/src/popup/Popup.tsx b/packages/extension/src/popup/Popup.tsx
new file mode 100644
index 00000000..895fb064
--- /dev/null
+++ b/packages/extension/src/popup/Popup.tsx
@@ -0,0 +1,11 @@
+import './Popup.scss';
+
+import React from 'react';
+
+import { RouterProvider } from '@/components/router-provider';
+
+const Popup = () => {
+ return ;
+};
+
+export default Popup;
diff --git a/src/popup/index.css b/packages/extension/src/popup/index.css
similarity index 100%
rename from src/popup/index.css
rename to packages/extension/src/popup/index.css
diff --git a/packages/extension/src/popup/index.html b/packages/extension/src/popup/index.html
new file mode 100755
index 00000000..42644b2c
--- /dev/null
+++ b/packages/extension/src/popup/index.html
@@ -0,0 +1,10 @@
+
+
+
+ <%= htmlWebpackPlugin.options.title %>
+
+
+
+
+
+
diff --git a/packages/extension/src/popup/index.tsx b/packages/extension/src/popup/index.tsx
new file mode 100755
index 00000000..26d956bc
--- /dev/null
+++ b/packages/extension/src/popup/index.tsx
@@ -0,0 +1,9 @@
+import './index.css';
+
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+
+import Popup from './Popup';
+
+const root = ReactDOM.createRoot(document.getElementById('popup-container')!);
+root.render();
diff --git a/src/popup/pages/Home.tsx b/packages/extension/src/popup/pages/Home.tsx
similarity index 75%
rename from src/popup/pages/Home.tsx
rename to packages/extension/src/popup/pages/Home.tsx
index 46a81f0e..9098249e 100644
--- a/src/popup/pages/Home.tsx
+++ b/packages/extension/src/popup/pages/Home.tsx
@@ -1,18 +1,18 @@
-import React, { useEffect, useState } from 'react'
-import { runtime } from 'webextension-polyfill'
+import React, { useEffect, useState } from 'react';
+import { runtime } from 'webextension-polyfill';
-import { sendMessage, sendMessageToActiveTab } from '@/utils/sendMessages'
+import { sendMessage, sendMessageToActiveTab } from '@/utils/sendMessages';
-const Success = runtime.getURL('assets/images/web-monetization-success.svg')
-const Fail = runtime.getURL('assets/images/web-monetization-fail.svg')
-const CheckIcon = runtime.getURL('assets/images/check.svg')
-const DollarIcon = runtime.getURL('assets/images/dollar.svg')
-const CloseIcon = runtime.getURL('assets/images/close.svg')
+const Success = runtime.getURL('assets/images/web-monetization-success.svg');
+const Fail = runtime.getURL('assets/images/web-monetization-fail.svg');
+const CheckIcon = runtime.getURL('assets/images/check.svg');
+const DollarIcon = runtime.getURL('assets/images/dollar.svg');
+const CloseIcon = runtime.getURL('assets/images/close.svg');
// --- Temporary code until real UI implemented ---
interface IProps {
- isMonetizationReady: boolean
+ isMonetizationReady: boolean;
}
const PopupFooter: React.FC = ({ isMonetizationReady }) => (
@@ -23,93 +23,93 @@ const PopupFooter: React.FC = ({ isMonetizationReady }) => (
This site isn't Web Monetization ready
)}
-)
+);
// --- End of Temporary code until real UI implemented ---
export const Home = () => {
- const [loading, setLoading] = useState(false)
- const [paymentStarted, setPaymentStarted] = useState(false)
- const [spent, setSpent] = useState(0)
- const [sendingPaymentPointer, setSendingPaymentPointer] = useState('')
- const [isMonetizationReady, setIsMonetizationReady] = useState(false)
- const [receivingPaymentPointer, setReceivingPaymentPointer] = useState('')
+ const [loading, setLoading] = useState(false);
+ const [paymentStarted, setPaymentStarted] = useState(false);
+ const [spent, setSpent] = useState(0);
+ const [sendingPaymentPointer, setSendingPaymentPointer] = useState('');
+ const [isMonetizationReady, setIsMonetizationReady] = useState(false);
+ const [receivingPaymentPointer, setReceivingPaymentPointer] = useState('');
const [formData, setFormData] = useState({
paymentPointer: sendingPaymentPointer || '',
amount: 20,
- })
+ });
useEffect(() => {
- checkMonetizationReady()
- getSendingPaymentPointer()
- listenForIncomingPayment()
+ checkMonetizationReady();
+ getSendingPaymentPointer();
+ listenForIncomingPayment();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [])
+ }, []);
const checkMonetizationReady = async () => {
- const response = await sendMessageToActiveTab({ type: 'IS_MONETIZATION_READY' })
- setIsMonetizationReady(response.data.monetization)
- setReceivingPaymentPointer(response.data.paymentPointer)
- }
+ const response = await sendMessageToActiveTab({ type: 'IS_MONETIZATION_READY' });
+ setIsMonetizationReady(response.data.monetization);
+ setReceivingPaymentPointer(response.data.paymentPointer);
+ };
const handleChange = (event: React.ChangeEvent) => {
- setFormData(prevState => ({ ...prevState, [event.target.name]: event.target.value }))
- }
+ setFormData(prevState => ({ ...prevState, [event.target.name]: event.target.value }));
+ };
const handleSubmit = async (event: React.FormEvent) => {
- event.preventDefault()
+ event.preventDefault();
- setLoading(true)
+ setLoading(true);
const data = {
amount: formData.amount,
paymentPointer: formData.paymentPointer,
incomingPayment: receivingPaymentPointer,
- }
+ };
- await sendMessage({ type: 'SET_INCOMING_POINTER', data })
- }
+ await sendMessage({ type: 'SET_INCOMING_POINTER', data });
+ };
const getSendingPaymentPointer = async () => {
- const response = await sendMessage({ type: 'GET_SENDING_PAYMENT_POINTER' })
- setSendingPaymentPointer(response.data.sendingPaymentPointerUrl)
+ const response = await sendMessage({ type: 'GET_SENDING_PAYMENT_POINTER' });
+ setSendingPaymentPointer(response.data.sendingPaymentPointerUrl);
- const { sendingPaymentPointerUrl: paymentPointer, amount } = response.data
+ const { sendingPaymentPointerUrl: paymentPointer, amount } = response.data;
if (paymentPointer && amount) {
setFormData({
paymentPointer: response.data.sendingPaymentPointerUrl,
amount: response.data.amount,
- })
+ });
}
- }
+ };
const listenForIncomingPayment = async () => {
const listener = (message: any) => {
if (message.type === 'SPENT_AMOUNT') {
- setSpent(message.data.spentAmount)
- setPaymentStarted(true)
+ setSpent(message.data.spentAmount);
+ setPaymentStarted(true);
}
if (loading) {
- setLoading(false)
+ setLoading(false);
}
- }
+ };
- runtime.onMessage.addListener(listener)
+ runtime.onMessage.addListener(listener);
return () => {
- runtime.onMessage.removeListener(listener)
- }
- }
+ runtime.onMessage.removeListener(listener);
+ };
+ };
const stopPayments = async (e: React.MouseEvent) => {
- e.preventDefault()
- setPaymentStarted(false)
+ e.preventDefault();
+ setPaymentStarted(false);
setTimeout(() => {
if (loading) {
- setLoading(false)
+ setLoading(false);
}
- }, 1000)
- await sendMessageToActiveTab({ type: 'STOP_PAYMENTS' })
- }
+ }, 1000);
+ await sendMessageToActiveTab({ type: 'STOP_PAYMENTS' });
+ };
return (
<>
@@ -125,7 +125,8 @@ export const Home = () => {