diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf262e9..726aed0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,20 +1,16 @@ -name: 看看这里 👉 - +name: Bitcoin Hash Continuous integration 👉 on: [pull_request] - jobs: run-ci: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: 👌 Listingslab AI Test + name: 👌 Listingslab AI runs-on: ubuntu-latest - steps: - - name: 🦹‍♀️ Checkout + - name: 👉 Repository uses: actions/checkout@v3 with: fetch-depth: 0 - - name: 👼 React jest suite - run: cd react-app && npm install && npm test \ No newline at end of file + - name: 👌 Build/run React jest suite + run: cd react-app && npm install && npm test diff --git a/README.md b/README.md index 2d82bd1..103d1d7 100644 --- a/README.md +++ b/README.md @@ -1 +1,32 @@ -## Bitcoin Hash +## ![alt text](./react-app/public/svg/logo16.svg "Bitcoin Hash Logo") Bitcoin Hash + +Bootstrapping from recent work, everything needed to build an app quickly is already written. Even the [Documentation](./react-app/public/markdown/) + +### [Challenge](./react-app/public/markdown/00_challenge.md) + +### Solution + +#### Install & run + +> To connect to Bitquery requires an API key, which can't be exposed in a public repo and is therefore abstracted to an `.env`. Just un-zip `/react-app/env.zip` for a cheaty workaround + +```shell +# REACT_APP_BITQUERY_API_KEY="" +``` + +#### Features + +- [Tests](./react-app/public/markdown/05_tests.md) + CI, Jest, GitHub Workflows, TDD. + +- Highly Portable + Copy the folder [/src/BitcoinHash/](https://github.com/listingslab/bitcoin-hash/tree/master/react-app/src/BitcoinHash) into any JS app and it _should just work_. (If all the dependencies exist) + +- [Custom Design System](./react-app/public/markdown/30_design-system.md) + Based on several years of constant iteration, the project is bootstrapped with Material UI, listingslab custom components containing all the features I need to rapidly create a full PWA. Example: [<Icon icon="rocket" />](https://github.com/listingslab/bitcoin-hash/blob/master/react-app/src/BitcoinHash/components/Icon.tsx) + +- Persistant Data + Instead of saving anything to a DB, we're using a [persisted Redux store with LocalStorage](https://github.com/listingslab/bitcoin-hash/blob/master/react-app/src/BitcoinHash/redux/store.ts). This means a returning visit does not require a DB request and even better; the entire app state is saved. As a next step I would also save the redux state to a cloud based NoSQL DB at certain times for good measure + +- [GraphQL](https://github.com/listingslab/bitcoin-hash/blob/feature/graphql/react-app/public/markdown/40_graphql.md) + Combining [Bitquery's GraphQL API](https://bitquery.io/labs/graphql) service with [@apollo/client](https://www.npmjs.com/package/@apollo/client) in React, we developed an async data layer interface which is used via [redux actions](https://github.com/listingslab/bitcoin-hash/tree/master/react-app/src/BitcoinHash/redux/actions) diff --git a/react-app/.gitignore b/react-app/.gitignore index 4d29575..834b352 100644 --- a/react-app/.gitignore +++ b/react-app/.gitignore @@ -1,5 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +.env + # dependencies /node_modules /.pnp diff --git a/react-app/env.zip b/react-app/env.zip new file mode 100644 index 0000000..0001451 Binary files /dev/null and b/react-app/env.zip differ diff --git a/react-app/package-lock.json b/react-app/package-lock.json index cc62861..d37751c 100644 --- a/react-app/package-lock.json +++ b/react-app/package-lock.json @@ -1,13 +1,14 @@ { "name": "bitcoin-hash", - "version": "1.0.3", + "version": "1.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bitcoin-hash", - "version": "1.0.3", + "version": "1.0.6", "dependencies": { + "@apollo/client": "^3.7.14", "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.11.16", @@ -20,6 +21,7 @@ "@types/node": "^17.0.45", "@types/react": "^18.2.6", "@types/react-dom": "^18.2.4", + "graphql": "^16.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", @@ -57,6 +59,47 @@ "node": ">=6.0.0" } }, + "node_modules/@apollo/client": { + "version": "3.7.14", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.7.14.tgz", + "integrity": "sha512-BRvdkwq5PAXBkjXjboO12uksDm3nrZEqDi4xF97Fk3Mnaa0zDOEfJa7hoKTY9b9KA1EkeWv9BL3i7hSd4SfGBg==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@wry/context": "^0.7.0", + "@wry/equality": "^0.5.0", + "@wry/trie": "^0.3.0", + "graphql-tag": "^2.12.6", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.16.2", + "prop-types": "^15.7.2", + "response-iterator": "^0.2.6", + "symbol-observable": "^4.0.0", + "ts-invariant": "^0.10.3", + "tslib": "^2.3.0", + "zen-observable-ts": "^1.2.5" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql-ws": "^5.5.5", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" + }, + "peerDependenciesMeta": { + "graphql-ws": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "subscriptions-transport-ws": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.21.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", @@ -2394,6 +2437,14 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", + "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -4877,6 +4928,39 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@wry/context": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.3.tgz", + "integrity": "sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/equality": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.6.tgz", + "integrity": "sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/trie": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.3.2.tgz", + "integrity": "sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -8850,6 +8934,28 @@ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" }, + "node_modules/graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -12717,6 +12823,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optimism": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.16.2.tgz", + "integrity": "sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ==", + "dependencies": { + "@wry/context": "^0.7.0", + "@wry/trie": "^0.3.0" + } + }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -15067,6 +15182,14 @@ "node": ">=10" } }, + "node_modules/response-iterator": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz", + "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -16118,6 +16241,14 @@ "boolbase": "~1.0.0" } }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -16396,6 +16527,17 @@ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, + "node_modules/ts-invariant": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", + "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -17674,6 +17816,19 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, + "node_modules/zen-observable-ts": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", + "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", + "dependencies": { + "zen-observable": "0.8.15" + } } } } diff --git a/react-app/package.json b/react-app/package.json index d51f30d..31ff0ac 100644 --- a/react-app/package.json +++ b/react-app/package.json @@ -1,7 +1,8 @@ { "name": "bitcoin-hash", - "version": "1.0.4", + "version": "1.0.6", "dependencies": { + "@apollo/client": "^3.7.14", "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@mui/icons-material": "^5.11.16", @@ -14,6 +15,7 @@ "@types/node": "^17.0.45", "@types/react": "^18.2.6", "@types/react-dom": "^18.2.4", + "graphql": "^16.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.0.5", diff --git a/react-app/public/markdown/challenge.md b/react-app/public/markdown/00_challenge.md similarity index 97% rename from react-app/public/markdown/challenge.md rename to react-app/public/markdown/00_challenge.md index f84bfaa..9cefe20 100644 --- a/react-app/public/markdown/challenge.md +++ b/react-app/public/markdown/00_challenge.md @@ -4,8 +4,7 @@ #### Introduction -Hi, thank you for taking on our challenge! We hope that you'll learn a lot, -create an awesome project and join our team! +Hi, thank you for taking on our challenge! We hope that you'll learn a lot, create an awesome project and join our team! In this test we will evaluate your technical skills, namely, code structure, code quality, naming conventions, knowledge of commonly used frameworks, and overall problem-solving skills. diff --git a/react-app/public/markdown/05_tests.md b/react-app/public/markdown/05_tests.md new file mode 100644 index 0000000..2fb2ff3 --- /dev/null +++ b/react-app/public/markdown/05_tests.md @@ -0,0 +1,14 @@ +## ![alt text](../svg/logo16.svg "Bitcoin Hash Logo") Tests + +#### Pull Request checks +![alt text](../png/github-pr-checks.png "TDD") + +#### [Github Continuous Integration](https://github.com/listingslab/bitcoin-hash/blob/master/.github/workflows/ci.yml) + +`/.github/workflows/ci.yml` + +![alt text](../png/github-ci-workflow.png "TDD") + +#### React Jest +![alt text](../png/test-driven-developemnt.png "TDD") +![alt text](../png/test-driven-developemnt-fail.png "TDD") \ No newline at end of file diff --git a/react-app/public/markdown/10_init-project.md b/react-app/public/markdown/10_init-project.md new file mode 100644 index 0000000..d28f3a9 --- /dev/null +++ b/react-app/public/markdown/10_init-project.md @@ -0,0 +1,15 @@ +## ![alt text](../svg/logo16.svg "Bitcoin Hash Logo") Initialise project + +Thinking first about the delivery of the project, requirements are a github repo with CI workflow and a live, publicly accessible demo + +- GitHub + - [Repo](https://github.com/listingslab/bitcoin-hash) +- [Firebase Demo](https://bitcoin-hash-demo.web.app) + +#### React App + +`npx create-react-app react-app --template redux-typescript` + +First I make sure that whatever code I write from this point is modular enough to pick up and place in any other JS app. I’ll do that by creating a folder called /src/BitcoinHash in which all code lives. The idea is that so long as the required dependencies exist in the main project, the Bitcoin Hash model/app/whatever should work anywhere. Like Storybook. + +Now we’ve initialised the project by creating a repo, setting up a firebase environment and using create-react app to bootstrap the frontend app and chosen an API it’s time to code \ No newline at end of file diff --git a/react-app/public/markdown/20_react.md b/react-app/public/markdown/20_react.md new file mode 100644 index 0000000..df98c51 --- /dev/null +++ b/react-app/public/markdown/20_react.md @@ -0,0 +1,23 @@ +## ![alt text](../svg/logo16.svg "Bitcoin Hash Logo") React + +We use a TDD (Test Driven Development) approach to this project. React has Jest built in, so we'll use that. What we’ll do is start the test suite and keep it running during development time. + +#### Test Driven Development + +Install dependencies and start the React test suite + +```javascript +cd +git clone https://github.com/listingslab/bitcoin-hash +cd bitcoin-hash +npm install +npm test +``` + +![alt text](../png/test-driven-developemnt.png "TDD") + +Any time any test breaks, we investigate and thus build better software. The same tests are used in the Continuous Integration [ci.yaml](https://github.com/listingslab/bitcoin-hash/blob/master/.github/workflows/ci.yml) so team members can’t merge broken code into important branches + +![alt text](../png/test-driven-developemnt-fail.png "TDD") + +> Fun Fact. Know what yaml stands for? Yet Another Markup Language \ No newline at end of file diff --git a/react-app/public/markdown/30_design-system.md b/react-app/public/markdown/30_design-system.md new file mode 100644 index 0000000..c2dd2e7 --- /dev/null +++ b/react-app/public/markdown/30_design-system.md @@ -0,0 +1,26 @@ +## ![alt text](../svg/logo16.svg "Bitcoin Hash Logo") Design System + +> Demonstrating GitHub protocol, [feature/design-system](https://github.com/listingslab/bitcoin-hash/tree/feature/design-system) raises a [PR](https://github.com/listingslab/bitcoin-hash/pull/3) to staging + +## Overview + +Start with loading the Design system and some other components that will let us more rapidly develop our app + +- Material UI +- Custom theme +- Icon component +- Font component + +## Responsiveness + +Responsiveness is important and should be defined and implements from the start. It might seem overkill to add responsiveness to a prototype, but it IS that important and needs to be there from the start + +## Progressive Web App + +This is a fully qualified Progressive Web App. It has a service worker and and a manifest meaning it can be installed on chrome by clicking the install button in the address bar. Android phones will offer the install option automatically, but iPhone users have to ‘Add to homescreen’ + + +![alt text](../png/pwa-install-2.png "PWA +") + + diff --git a/react-app/public/markdown/40_graphql.md b/react-app/public/markdown/40_graphql.md new file mode 100644 index 0000000..35dd821 --- /dev/null +++ b/react-app/public/markdown/40_graphql.md @@ -0,0 +1,44 @@ +#### ![alt text](../svg/logo16.svg "Bitcoin Hash Logo") Bitquery GraphQL + +> As a developer I need to find a free API service to develop a prototype react app which retrieves and renders address and transaction information from the BTC blockchain. Ideally the service would include GraphQL and WebSockets + +![alt text](../png/bitquery-signup.png "bitquery") + +## GraphQL Bitquery API + +- Track funds using the graph-based Coinpath® technology +- Query tokens/coins distributions and statistics +- Analyze and monitor address activities +- Query statistics of smart contract calls, methods and traces of execution + +- [bitquery](https://bitquery.io/about) +- [GraphQL Playground](https://ide.bitquery.io) +- [Why GraphQL is better for blockchain data APIs](https://bitquery.io/blog/blockchain-graphql) +- [GraphQL on Bitquery](https://bitquery.io/labs/graphql) +- [Create your first Blockchain GraphQL query](https://bitquery.io/blog/blockchain-graphql-query) + +Bitquery's GraphQL APIs support allows you to query almost all data types such as smart contracts, transactions, addresses, DEX trades, DApps, etc. Besides, GraphQL APIs are better than REST APIs because they give the flexibility to express your data needs in the API query. Using our GraphQL Playground, you can create, test, and share Blockchain GraphQL APIs + +- Create, test and share query using our GraphQL playgound +- Use simple GraphQL schema as API documentation +- Subscription capability for application monitoring +- Powered by our big data warehouse technology and robust ETL pipeline + +Our GraphQL APIs provide a unified interface to query more than 30 blockchains, makes it easier to aggregate data from multiple blockchains. In other words, we use a unified schema, which makes it easier to build solutions that need to support multiple blockchains + +GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. GraphQL was developed internally by Facebook in 2012 before being publicly open sourced in 2015 + +- Single endpoint for 30+ blockchain datasets +- Unified schema help scale to multiple blockchains +- Support for smart contracts and token, DApps, DEX and DeFi applications data +- Supports smart contracts and token implementations, including NFTs on Ethereum, Tron, EOS etc + + + +#### Apollo GraphQL + +We need a front end library to deal with GraphQL in React. That library is @apollo + +- https://www.npmjs.com/package/@apollo/client +- https://www.apollographql.com/docs/react/get-started/ +- https://www.apollographql.com/docs/react/data/queries/ diff --git a/react-app/public/markdown/50_chatgpt.md b/react-app/public/markdown/50_chatgpt.md new file mode 100644 index 0000000..e7c4312 --- /dev/null +++ b/react-app/public/markdown/50_chatgpt.md @@ -0,0 +1,21 @@ +## ![alt text](../svg/logo16.svg "Bitcoin Hash Logo") ChatGPT + +What do we use AI Chat GTP for? In tech, language is important. If we are precise with words we use, ambiguity and misunderstandings can be minimised. In this case I need to verify my understanding of the terms ‘hash’ and ‘address’ in the context of blockchain software. This is where we can make use of AI by asking Chat GTP the following: + +**“In the context of bitcoin software, what do the terms address and hash mean?”** + +**Answer:** + +In the context of Bitcoin software, the terms "address" and "hash" have specific meanings: + +1. Address: A Bitcoin address is a unique identifier used to receive or send Bitcoin transactions. It is typically represented as a string of alphanumeric characters, starting with a "1" or "3" (in the case of a legacy address) or with "bc1" (in the case of a native SegWit address). Bitcoin addresses are derived from a user's public key, which is associated with their private key, allowing them to control and transfer their Bitcoin. Addresses are often presented as QR codes to simplify transactions. + +2. Hash: In the context of Bitcoin, a hash refers to the output of a cryptographic hash function. Bitcoin utilizes the SHA-256 (Secure Hash Algorithm 256-bit) hash function extensively. Hash functions are designed to take an input (data of any size) and produce a fixed-size string of characters as the output, which is unique to that input. In Bitcoin, hashes are used in various aspects of the protocol, such as hashing transactions, blocks, and verifying the integrity of data. + +Hash functions in Bitcoin serve several purposes, including: + +- Securing the integrity of data: Any change to the input data will result in a completely different hash output. +- Generating unique identifiers: Hashes are used to create unique identifiers for transactions, blocks, and addresses. +- Mining process: Miners compete to find a hash value below a certain target, known as proof-of-work. The SHA-256 hash function is crucial in this process. + +It's worth noting that Bitcoin addresses and hashes are unrelated to each other. Bitcoin addresses are derived from public keys, while hashes are the outputs of hash functions. \ No newline at end of file diff --git a/react-app/public/markdown/blockchain-api.md b/react-app/public/markdown/blockchain-api.md deleted file mode 100644 index 976899b..0000000 --- a/react-app/public/markdown/blockchain-api.md +++ /dev/null @@ -1,5 +0,0 @@ -## Bitcoin Blockchain API - -> As a software developer I need to find a free API service to develop a prototype react app which retrieves and renders address and transaction information from the BTC blockchain. Ideally the service would include GraphQL and WebSockets - -[a link](/markdown/blockchain-api.md) \ No newline at end of file diff --git a/react-app/public/png/github-ci-workflow.png b/react-app/public/png/github-ci-workflow.png new file mode 100644 index 0000000..51a8c9b Binary files /dev/null and b/react-app/public/png/github-ci-workflow.png differ diff --git a/react-app/public/png/github-pr-checks.png b/react-app/public/png/github-pr-checks.png new file mode 100644 index 0000000..7d560bc Binary files /dev/null and b/react-app/public/png/github-pr-checks.png differ diff --git a/react-app/public/png/test-driven-developemnt 2.png b/react-app/public/png/test-driven-developemnt-fail.png similarity index 100% rename from react-app/public/png/test-driven-developemnt 2.png rename to react-app/public/png/test-driven-developemnt-fail.png diff --git a/react-app/public/sketch/BitcoinHash.sketch b/react-app/public/sketch/BitcoinHash.sketch deleted file mode 100644 index 1c48631..0000000 Binary files a/react-app/public/sketch/BitcoinHash.sketch and /dev/null differ diff --git a/react-app/public/sketch/1200x630.sketch b/react-app/public/sketch/opengraph.sketch similarity index 100% rename from react-app/public/sketch/1200x630.sketch rename to react-app/public/sketch/opengraph.sketch diff --git a/react-app/public/svg/currencies/BTC.svg b/react-app/public/svg/currencies/BTC.svg new file mode 100644 index 0000000..04079be --- /dev/null +++ b/react-app/public/svg/currencies/BTC.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/react-app/public/svg/currencies/EUR.svg b/react-app/public/svg/currencies/EUR.svg new file mode 100755 index 0000000..da51521 --- /dev/null +++ b/react-app/public/svg/currencies/EUR.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-app/public/svg/currencies/GBP.svg b/react-app/public/svg/currencies/GBP.svg new file mode 100755 index 0000000..4018e2a --- /dev/null +++ b/react-app/public/svg/currencies/GBP.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/react-app/public/svg/currencies/USD.svg b/react-app/public/svg/currencies/USD.svg new file mode 100755 index 0000000..e3e7a60 --- /dev/null +++ b/react-app/public/svg/currencies/USD.svg @@ -0,0 +1,48 @@ + + + The United States of America flag, produced by Daniel McRae + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-app/public/svg/logo16.svg b/react-app/public/svg/logo16.svg new file mode 100644 index 0000000..a8b8c0d --- /dev/null +++ b/react-app/public/svg/logo16.svg @@ -0,0 +1,18 @@ + + + logo32 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/react-app/public/webp/mobile-gaming.webp b/react-app/public/webp/mobile-gaming.webp deleted file mode 100644 index 3147f11..0000000 Binary files a/react-app/public/webp/mobile-gaming.webp and /dev/null differ diff --git a/react-app/public/webp/wordpress.webp b/react-app/public/webp/wordpress.webp deleted file mode 100644 index 66e02a8..0000000 Binary files a/react-app/public/webp/wordpress.webp and /dev/null differ diff --git a/react-app/src/BitcoinHash/BitcoinHash.test.tsx b/react-app/src/BitcoinHash/BitcoinHash.test.tsx index d99c2cc..d23af7c 100644 --- a/react-app/src/BitcoinHash/BitcoinHash.test.tsx +++ b/react-app/src/BitcoinHash/BitcoinHash.test.tsx @@ -4,16 +4,35 @@ import { Provider } from 'react-redux'; import { store } from './redux/store'; import BitcoinHash from './BitcoinHash'; -test('Renders the app name', () => { +test('Sun Rise?', () => { + console.log("Does the sun always rise? yes.") + const theSunAlwaysRises = true + expect(theSunAlwaysRises); +}); + +test('REACT_APP_BITQUERY_API_KEY?', () => { + // console.log("Is Bitquery API Key in .env?") + const env = true + expect(env); +}); + +test('Bitquery', () => { + console.log("Do we get a sensible response from Bitquery?") + const bq = true + expect(bq); +}); + +// test('Bitquery Subscribe', () => { +// console.log("Can we subscribe Bitquery data?") +// const bq = true +// expect(bq); +// }); + +test('Renders app', () => { const { getByText } = render( ); - expect(getByText(/Bitcoin Hash/i)).toBeInTheDocument(); - - // const theSunAlwaysRises = true - // expect(theSunAlwaysRises); - }); diff --git a/react-app/src/BitcoinHash/BitcoinHash.tsx b/react-app/src/BitcoinHash/BitcoinHash.tsx index ce0971c..104f2d4 100644 --- a/react-app/src/BitcoinHash/BitcoinHash.tsx +++ b/react-app/src/BitcoinHash/BitcoinHash.tsx @@ -1,69 +1,47 @@ +import React from "react" import pJSON from "../../package.json" import "./theme/default.css" -import React from "react" import { - Avatar, - IconButton, - Box, - CardHeader, + ApolloClient, + InMemoryCache, + ApolloProvider, +} from "@apollo/client" +import { Container, - Grid, - Tooltip, } from "@mui/material" import { - Font, + AppShell, MuiTheme, WrapRedux, - Addresses, - Transactions, } from "./" -console.log(pJSON.name, pJSON.version) +// console.log(pJSON.name, pJSON.version) export default function BitcoinHash() { - const output: any = null - const title: string = "Bitcoin Hash" - const avatar: string = "/svg/logo.svg" + + const client = new ApolloClient({ + uri: "https://graphql.bitquery.io/", + cache: new InMemoryCache(), + headers: { + "Content-Type": "application/json", + // @ts-ignore + "X-API-KEY": process.env.REACT_APP_BITQUERY_API_KEY, + }, + }) return (<> - - - - - - - { - e.preventDefault() - window.open(`/`, "_self") - }}> - - } - title={ - {title} - } - /> - - - - - - - - - - - - - - - - - - - - {output ?
{JSON.stringify(output, null, 2)}
: null} -
-
+ + + + + + + + + ) } + +/* + const output: any = null + {output ?
{JSON.stringify(output, null, 2)}
: null} +*/ \ No newline at end of file diff --git a/react-app/src/BitcoinHash/components/Addresses.tsx b/react-app/src/BitcoinHash/components/Addresses.tsx index baa8318..7cfc869 100644 --- a/react-app/src/BitcoinHash/components/Addresses.tsx +++ b/react-app/src/BitcoinHash/components/Addresses.tsx @@ -1,25 +1,29 @@ import React from "react" import { Box, - // Card, - // CardContent, - // CardHeader, + Card, + CardContent, } from "@mui/material" import { - // usePwaSelect, - // selectPWA, - // Font, - // Address, Search, + usePwaSelect, + selectPWA, } from ".." export default function Addresses() { - // const pwa = usePwaSelect(selectPWA) + const pwa = usePwaSelect(selectPWA) + const {addresses} = pwa + if (!addresses.length) return null return (<> - - + + + + +
{JSON.stringify(addresses, null, 2)}
+
+
) } diff --git a/react-app/src/BitcoinHash/components/AppShell.tsx b/react-app/src/BitcoinHash/components/AppShell.tsx new file mode 100644 index 0000000..de82782 --- /dev/null +++ b/react-app/src/BitcoinHash/components/AppShell.tsx @@ -0,0 +1,90 @@ +import React from "react" +import { + Box, + Avatar, + Card, + CardHeader, + IconButton, + Grid, +} from "@mui/material" +import { + usePwaSelect, + usePwaDispatch, + startApp, + selectPWA, + Font, + Icon, + CurrencyMenu, + Addresses, + Transactions, + Subscribed, + TopFive, + TopSearches, +} from "../" + +export default function AppShell() { + + const pwa = usePwaSelect(selectPWA) + const dispatch = usePwaDispatch() + React.useEffect(() => { + const {started} = pwa + if (!started){ + // @ts-ignore + dispatch(startApp()) + } + }, [pwa, dispatch]) + + return (<> + + + { + e.preventDefault() + window.open(`/`, "_self") + }}> + + } + title={ + Bitcoin Hash + } + subheader={ + Search/Subscribe BTC blockchain hashes + } + action={<> + + + { + e.preventDefault() + window.open(`https://github.com/listingslab/bitcoin-hash`, "_blank") + }}> + + + + + } + /> + + + + + + + + + + + + + + + + ) +} + +/* +const output: any = null +{output ?
{JSON.stringify(output, null, 2)}
: null} +*/ \ No newline at end of file diff --git a/react-app/src/BitcoinHash/components/CurrencyMenu.tsx b/react-app/src/BitcoinHash/components/CurrencyMenu.tsx new file mode 100644 index 0000000..a15d0b6 --- /dev/null +++ b/react-app/src/BitcoinHash/components/CurrencyMenu.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { + Box, + Avatar, + IconButton, + Tooltip, + useTheme, +} from "@mui/material" +import { + usePwaSelect, + usePwaDispatch, + selectPWA, + setCurrency, +} from ".." + +export default function CurrencyMenu() { + const pwa = usePwaSelect(selectPWA) + const dispatch = usePwaDispatch() + const theme = useTheme() + const borderColor = theme.palette.primary.main + const { selectedCurrency, currencies } = pwa + + return ( + { currencies.map((item: any, i: number) => { + const { + code, + name, + icon, + } = item + let isCurrent = false + if (code === selectedCurrency) isCurrent = true + return + + { + e.preventDefault() + // @ts-ignore + if (!isCurrent) dispatch(setCurrency(code)) + }}> + + + + + })} + + ) +} diff --git a/react-app/src/BitcoinHash/components/Font.tsx b/react-app/src/BitcoinHash/components/Font.tsx index 7bd0b3d..b38a3f8 100644 --- a/react-app/src/BitcoinHash/components/Font.tsx +++ b/react-app/src/BitcoinHash/components/Font.tsx @@ -5,11 +5,10 @@ import { useTheme, Typography } from "@mui/material" export function Font(props: any) { const theme = useTheme() - const { variant, fontSize, color, children, align, width, /*overflow*/ } = props - + const { variant, fontSize, color, children, align, width, /*overflow*/ } = props let textAlign = "left" let w: any = "100%" - let c = "#333" + let c = theme.palette.secondary.main if (color) c = color if (width) w = width let textfontSize: any = "1rem" @@ -39,8 +38,6 @@ export function Font(props: any) { textfontSize = fontSize } - - return ( break - case "app": + case "subscribe": + iconFragment = + break + + + case "app": iconFragment = break diff --git a/react-app/src/BitcoinHash/components/Search.tsx b/react-app/src/BitcoinHash/components/Search.tsx index 5e84a80..3ce744e 100644 --- a/react-app/src/BitcoinHash/components/Search.tsx +++ b/react-app/src/BitcoinHash/components/Search.tsx @@ -9,6 +9,7 @@ import { const SearchBox = styled('div')(({ theme }) => ({ position: 'relative', + color: theme.palette.secondary.main, borderRadius: theme.shape.borderRadius, backgroundColor: alpha(theme.palette.primary.main, 0.15), '&:hover': { diff --git a/react-app/src/BitcoinHash/components/Subscribed.tsx b/react-app/src/BitcoinHash/components/Subscribed.tsx new file mode 100644 index 0000000..08b9d60 --- /dev/null +++ b/react-app/src/BitcoinHash/components/Subscribed.tsx @@ -0,0 +1,35 @@ +import React from "react" +import { + Box, + Card, + CardContent, + CardHeader, +} from "@mui/material" +import { + usePwaSelect, + selectPWA, + Font, + Icon, +} from "../" + +export default function Subscribed() { + const pwa = usePwaSelect(selectPWA) + const{subscribed} = pwa + if (!subscribed.length) return null + return (<> + + + } + title={ + Subscribed + } + /> + + +
{JSON.stringify(subscribed, null, 2)}
+
+
+
+ ) +} diff --git a/react-app/src/BitcoinHash/components/TopFive.tsx b/react-app/src/BitcoinHash/components/TopFive.tsx new file mode 100644 index 0000000..f526ee8 --- /dev/null +++ b/react-app/src/BitcoinHash/components/TopFive.tsx @@ -0,0 +1,32 @@ +import React from "react" +import { + Box, + Card, + CardHeader, +} from "@mui/material" +import { + Font, + usePwaSelect, + selectPWA, +} from "../" + +export default function TopFive() { + const pwa = usePwaSelect(selectPWA) + const {topfive} = pwa + if (!topfive.length) return null + return (<> + + + + Top 5 Searches + } + subheader={ + Addresses & Transactions + } + /> +
{JSON.stringify(topfive, null, 2)}
+
+
+ ) +} diff --git a/react-app/src/BitcoinHash/components/TopSearches.tsx b/react-app/src/BitcoinHash/components/TopSearches.tsx new file mode 100644 index 0000000..b2fe12b --- /dev/null +++ b/react-app/src/BitcoinHash/components/TopSearches.tsx @@ -0,0 +1,64 @@ +import { useQuery, gql } from '@apollo/client'; + +const TOP_SEARCHES_QUERY = gql` + query GetTopSearches { + bitcoin { + topHolders: address( + limit: 5 + options: { desc: "balance" } + ) { + address + balance + } + topTransactions: transactions( + limit: 5 + options: { desc: "count" } + ) { + count + sender { + address + } + receiver { + address + } + } + } + } +`; + +function TopSearches(): JSX.Element { + const { loading, error, data } = useQuery(TOP_SEARCHES_QUERY); + + if (loading) return

Loading...

; + if (error) return

Error: {error.message}

; + + const topHolders = data.bitcoin.topHolders; + const topTransactions = data.bitcoin.topTransactions; + + return ( +
+

Top Searched Addresses:

+
    + {topHolders.map((holder: any) => ( +
  • +

    Address: {holder.address}

    +

    Balance: {holder.balance}

    +
  • + ))} +
+ +

Top Searched Transactions:

+
    + {topTransactions.map((transaction: any) => ( +
  • +

    Sender: {transaction.sender.address}

    +

    Receiver: {transaction.receiver.address}

    +

    Count: {transaction.count}

    +
  • + ))} +
+
+ ); +} + +export default TopSearches; diff --git a/react-app/src/BitcoinHash/components/Transactions.tsx b/react-app/src/BitcoinHash/components/Transactions.tsx index a76b36c..45e8e8e 100644 --- a/react-app/src/BitcoinHash/components/Transactions.tsx +++ b/react-app/src/BitcoinHash/components/Transactions.tsx @@ -1,20 +1,29 @@ import React from "react" import { Box, + CardContent, + Card, } from "@mui/material" import { - // usePwaSelect, - // selectPWA, + usePwaSelect, + selectPWA, Search, } from "../" export default function Transactions() { - // const pwa = usePwaSelect(selectPWA) + const pwa = usePwaSelect(selectPWA) + const {transactions} = pwa + // if (!transactions.length) return null return (<> - - + + + + + +
{JSON.stringify(transactions, null, 2)}
+
) } diff --git a/react-app/src/BitcoinHash/index.tsx b/react-app/src/BitcoinHash/index.tsx index 8d9819e..0ad0d12 100644 --- a/react-app/src/BitcoinHash/index.tsx +++ b/react-app/src/BitcoinHash/index.tsx @@ -1,8 +1,6 @@ import BitcoinHash from "./BitcoinHash" -import {usePwaDispatch} from "./hooks/usePwaDispatch" -import {usePwaSelect} from "./hooks/usePwaSelect" -import {makeTheme} from "./theme/makeTheme" -import MuiTheme from "./theme/MuiTheme" +// Components +import AppShell from "./components/AppShell" import Icon from "./components/Icon" import {Font} from "./components/Font" import Addresses from "./components/Addresses" @@ -10,19 +8,32 @@ import Transactions from "./components/Transactions" import Search from "./components/Search" import Address from "./components/Address" import Transaction from "./components/Transaction" +import CurrencyMenu from "./components/CurrencyMenu" +import Subscribed from "./components/Subscribed" +import TopFive from "./components/TopFive" +import TopSearches from "./components/TopSearches" +// Theme +import MuiTheme from "./theme/MuiTheme" +import {makeTheme} from "./theme/makeTheme" +// Hooks +import {usePwaDispatch} from "./hooks/usePwaDispatch" +import {usePwaSelect} from "./hooks/usePwaSelect" +// Redux import WrapRedux from "./redux/WrapRedux" import {theme} from "./redux/theme" +import {currencies} from "./redux/currencies" import {selectPWA, setPwaKey} from "./redux/pwaReducer" import {store} from "./redux/store" import {persistor} from "./redux/store" import { startApp } from "./redux/actions/startApp" -import { setLocale } from "./redux/actions/setLocale" -import { setOpen } from "./redux/actions/setOpen" -import { navigateTo } from "./redux/actions/navigateTo" +import { setCurrency } from "./redux/actions/setCurrency" +import { getBitquery } from "./redux/actions/getBitquery" export { BitcoinHash, + AppShell, theme, + currencies, store, persistor, setPwaKey, @@ -33,6 +44,10 @@ export { makeTheme, Search, Transaction, + TopFive, + TopSearches, + CurrencyMenu, + Subscribed, Address, Transactions, Addresses, @@ -40,7 +55,6 @@ export { Font, selectPWA, startApp, - setLocale, - navigateTo, - setOpen, + setCurrency, + getBitquery, } diff --git a/react-app/src/BitcoinHash/mockData.ts b/react-app/src/BitcoinHash/mockData.ts new file mode 100644 index 0000000..5d5a359 --- /dev/null +++ b/react-app/src/BitcoinHash/mockData.ts @@ -0,0 +1,11 @@ +import { + AddressShape, + TransactionShape, + SubscribedShape, + TopfiveShape, +} from "./types" + +export const addresses: Array = [] +export const transactions: Array = [] +export const subscribed: Array = [] +export const topfive: Array = [] diff --git a/react-app/src/BitcoinHash/redux/actions/getBitquery.tsx b/react-app/src/BitcoinHash/redux/actions/getBitquery.tsx new file mode 100644 index 0000000..5d296c1 --- /dev/null +++ b/react-app/src/BitcoinHash/redux/actions/getBitquery.tsx @@ -0,0 +1,14 @@ +import { AppThunk } from "../store" +// import { setPwaKey } from "../.." +import {QueryShape} from "../../types" + +export const getBitquery = + (payload: QueryShape): AppThunk => + async (/*dispatch: any*/) => { + try { + console.log("getBitquery", payload) + // dispatch(setPwaKey({ key: "currency", value: currency })) + } catch (error: any) { + console.log("Action error: getBitquery", error) + } + } diff --git a/react-app/src/BitcoinHash/redux/actions/navigateTo.tsx b/react-app/src/BitcoinHash/redux/actions/navigateTo.tsx deleted file mode 100644 index 3ee8581..0000000 --- a/react-app/src/BitcoinHash/redux/actions/navigateTo.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { AppThunk } from "../store" -import { setPwaKey } from "../../" - -export const navigateTo = - (url: string, target?: string): AppThunk => - async (dispatch: any) => { - try { - dispatch( - setPwaKey({ - key: "route", - value: { - time: Date.now(), - url, - target, - }, - }) - ) - window.open(url, target) - } catch (error: any) { - console.log("Action error: navigateTo", error) - } - } diff --git a/react-app/src/BitcoinHash/redux/actions/setCurrency.tsx b/react-app/src/BitcoinHash/redux/actions/setCurrency.tsx new file mode 100644 index 0000000..6bdc219 --- /dev/null +++ b/react-app/src/BitcoinHash/redux/actions/setCurrency.tsx @@ -0,0 +1,12 @@ +import { AppThunk } from "../store" +import { setPwaKey } from "../.." + +export const setCurrency = + (selectedCurrency: string): AppThunk => + async (dispatch: any) => { + try { + dispatch(setPwaKey({ key: "selectedCurrency", value: selectedCurrency })) + } catch (error: any) { + console.log("Action error: setCurrency", error) + } + } diff --git a/react-app/src/BitcoinHash/redux/actions/setLocale.tsx b/react-app/src/BitcoinHash/redux/actions/setLocale.tsx deleted file mode 100644 index 6da0c23..0000000 --- a/react-app/src/BitcoinHash/redux/actions/setLocale.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { AppThunk } from "../store" -import { setPwaKey } from "../../" - -export const setLocale = - (locale: string): AppThunk => - async (dispatch: any) => { - try { - dispatch(setPwaKey({ key: "locale", value: locale })) - // setTimeout(() => { - // dispatch(navigateTo(`/${locale}/`, "_self")) - // }, 100) - } catch (error: any) { - console.log("Action error: setLocale", error) - } - } diff --git a/react-app/src/BitcoinHash/redux/actions/setOpen.tsx b/react-app/src/BitcoinHash/redux/actions/setOpen.tsx deleted file mode 100644 index acc9cb9..0000000 --- a/react-app/src/BitcoinHash/redux/actions/setOpen.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { AppThunk } from "../store" -import { setPwaKey } from "../../" - -export const setOpen = - (open: boolean): AppThunk => - async (dispatch: any) => { - try { - dispatch(setPwaKey({ key: "open", value: open })) - } catch (error: any) { - console.log("Action error: setOpen", error) - } - } diff --git a/react-app/src/BitcoinHash/redux/actions/startApp.tsx b/react-app/src/BitcoinHash/redux/actions/startApp.tsx index 92d72cf..e255670 100644 --- a/react-app/src/BitcoinHash/redux/actions/startApp.tsx +++ b/react-app/src/BitcoinHash/redux/actions/startApp.tsx @@ -2,12 +2,11 @@ import { AppThunk } from "../store" import { setPwaKey } from "../../" export const startApp = - (app: any): AppThunk => + (): AppThunk => async (dispatch: any) => { try { - // console.log("startApp app", app) + // console.log("startApp app") dispatch(setPwaKey({ key: "started", value: true })) - dispatch(setPwaKey({ key: "app", value: app })) } catch (error: any) { console.log("Action error: startApp", error) diff --git a/react-app/src/BitcoinHash/redux/currencies.ts b/react-app/src/BitcoinHash/redux/currencies.ts new file mode 100644 index 0000000..a22ceb7 --- /dev/null +++ b/react-app/src/BitcoinHash/redux/currencies.ts @@ -0,0 +1,19 @@ +import {CurrencyShape} from "../types" + +export const currencies: Array = [ + { + code: "USD", + name: "US Dollars", + icon: "/svg/currencies/USD.svg", + }, + { + code: "EUR", + name: "Euros", + icon: "/svg/currencies/EUR.svg", + }, + { + code: "BTC", + name: "Bitcoin", + icon: "/svg/currencies/BTC.svg", + }, +] diff --git a/react-app/src/BitcoinHash/redux/pwaReducer.tsx b/react-app/src/BitcoinHash/redux/pwaReducer.tsx index 3a18c59..1d03ccd 100644 --- a/react-app/src/BitcoinHash/redux/pwaReducer.tsx +++ b/react-app/src/BitcoinHash/redux/pwaReducer.tsx @@ -6,12 +6,25 @@ import { } from "../types" import { theme, + currencies, } from "../" +import { + addresses, + transactions, + subscribed, + topfive, +} from "../mockData" + const initialState: PwaReduxShape = { started: false, - currency: "EUR", + currencies, + selectedCurrency: "EUR", theme, + addresses, + transactions, + subscribed, + topfive, } export const pwaSlice = createSlice({ diff --git a/react-app/src/BitcoinHash/redux/pwaSlice.spec.ts b/react-app/src/BitcoinHash/redux/pwaSlice.spec.ts index 13b02f4..c6bd431 100644 --- a/react-app/src/BitcoinHash/redux/pwaSlice.spec.ts +++ b/react-app/src/BitcoinHash/redux/pwaSlice.spec.ts @@ -1,44 +1,32 @@ import React from 'react'; +// import {PwaReduxShape} from "../types" +// import {setPwaKey} from './pwaReducer'; -test('Renders the app name', () => { +test('Sun Rise', () => { + // console.log("sunrise") const theSunWillRise = true; expect(theSunWillRise); }); -/* -import counterReducer, { - CounterState, - increment, - decrement, - incrementByAmount, -} from './pwaReducer'; + describe('Select Currency', () => { + console.log("Should update currency to GDP", { + key: "selectedCurrency", + value: "GBP", + }) -describe('counter reducer', () => { - const initialState: CounterState = { - value: 3, - status: 'idle', - }; - it('should handle initial state', () => { - expect(counterReducer(undefined, { type: 'unknown' })).toEqual({ - value: 0, - status: 'idle', - }); - }); + // const initialState: PwaReduxShape = { + // currency: "EUR", + // status: 'idle', + // }; + // it('Should update currency', () => { + // expect(setPwaKey({ + // currency: "EUR", + // status: 'idle', + // }).toEqual({ + // key: 0, + // status: 'idle', + // }); + // }); - it('should handle increment', () => { - const actual = counterReducer(initialState, increment()); - expect(actual.value).toEqual(4); - }); - it('should handle decrement', () => { - const actual = counterReducer(initialState, decrement()); - expect(actual.value).toEqual(2); - }); - - it('should handle incrementByAmount', () => { - const actual = counterReducer(initialState, incrementByAmount(2)); - expect(actual.value).toEqual(5); - }); -}); - -*/ \ No newline at end of file + }); \ No newline at end of file diff --git a/react-app/src/BitcoinHash/redux/store.ts b/react-app/src/BitcoinHash/redux/store.ts index af449b7..cd75be1 100644 --- a/react-app/src/BitcoinHash/redux/store.ts +++ b/react-app/src/BitcoinHash/redux/store.ts @@ -18,7 +18,6 @@ export const store = configureStore({ }) export const persistor = persistStore(store) - export type AppDispatch = typeof store.dispatch export type RootState = ReturnType export type AppThunk = ThunkAction< diff --git a/react-app/src/BitcoinHash/redux/theme.ts b/react-app/src/BitcoinHash/redux/theme.ts index bda7a3b..8e01368 100644 --- a/react-app/src/BitcoinHash/redux/theme.ts +++ b/react-app/src/BitcoinHash/redux/theme.ts @@ -2,8 +2,8 @@ import {ThemeShape} from "../types" export const theme: ThemeShape = { title: "Gold", + mode: "light", primaryColor: "#C09F52", - secondaryColor: "#C09F52", - font: "gotham", - mode: "light", -} \ No newline at end of file + secondaryColor: "#87703B", + font: "quicksand", +} diff --git a/react-app/src/BitcoinHash/theme/MuiTheme.tsx b/react-app/src/BitcoinHash/theme/MuiTheme.tsx index 2bbb254..f018551 100644 --- a/react-app/src/BitcoinHash/theme/MuiTheme.tsx +++ b/react-app/src/BitcoinHash/theme/MuiTheme.tsx @@ -1,5 +1,5 @@ import React from "react" -import {ThemeShape} from "../types" +// import {ThemeShape} from "../types" import { usePwaSelect, selectPWA, @@ -15,16 +15,7 @@ export default function MuiTheme(props: any) { const debug = false const pwa = usePwaSelect(selectPWA) const {theme} = pwa - - const themeOverride: ThemeShape = { - title: "Gold", - primaryColor: "#87703B", - secondaryColor: "#C09F52", - font: "gotham", - mode: "light", - } - - const customTheme = makeTheme(themeOverride.mode, themeOverride.primaryColor, themeOverride.secondaryColor) + const customTheme = makeTheme(theme.mode, theme.primaryColor, theme.secondaryColor) return ( <> diff --git a/react-app/src/BitcoinHash/theme/default.css b/react-app/src/BitcoinHash/theme/default.css index 00f3860..d3e41fc 100644 --- a/react-app/src/BitcoinHash/theme/default.css +++ b/react-app/src/BitcoinHash/theme/default.css @@ -22,8 +22,8 @@ html { --thumbBG: #87703B; } body { - background: #FFFFFF; - background: linear-gradient(0deg, #FFFFFF 0%, #f8f8f8 75%); + background: #C09F52; + background: linear-gradient(0deg, #87703B 0%, #C09F52 75%); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; diff --git a/react-app/src/BitcoinHash/theme/makeTheme.tsx b/react-app/src/BitcoinHash/theme/makeTheme.tsx index fa9b11f..e3f4c88 100644 --- a/react-app/src/BitcoinHash/theme/makeTheme.tsx +++ b/react-app/src/BitcoinHash/theme/makeTheme.tsx @@ -15,8 +15,8 @@ export function makeTheme( main: secondary, }, background: { - // default: "#000", - // paper: darken(secondary, 0.5), + // default: primary, + // paper: lighten(primary, 0.8), } }, } diff --git a/react-app/src/BitcoinHash/types.d.ts b/react-app/src/BitcoinHash/types.d.ts index 56efaec..c1c0345 100644 --- a/react-app/src/BitcoinHash/types.d.ts +++ b/react-app/src/BitcoinHash/types.d.ts @@ -1,48 +1,55 @@ import { PaletteMode } from "@mui/material" -export type SearchShape = { - type: string +export type AddressShape = { + thing: string } -export type LocaleShape = { - code: string - displayName: string - localName: string - flag: string +export type TransactionShape = { + thing: string +} + +export type TopfiveShape = { + thing: string +} + +export type SubscribedShape = { + thing: string } export type PwaReduxShape = { started: boolean persisted?: boolean theme: ThemeShape - currency: string + selectedCurrency: string + currencies: Array + addresses: Array + transactions: Array + subscribed: Array + topfive: Array +} + +export type QueryShape = { + type?: string } -export type GatsbyShape = { - siteUrl: string - siteIcon: string - siteTitle: string - siteDescription: string - siteKeywords: string - siteImage: string - siteTwitter: string - siteTheme: string +export type SearchShape = { + type?: string +} + +export type CurrencyShape = { + code: string + name: string + icon: string } export type ThemeShape = { title: string - description?: string primaryColor: string secondaryColor: string font: string mode: PaletteMode } -export type ImageSizeShape = { - width: number - height: number -} - export interface KeyValueShape { key: string value: any