diff --git a/.github/workflows/on-push.yml b/.github/workflows/on-push.yml index 592a8fdae..99e1a2f40 100644 --- a/.github/workflows/on-push.yml +++ b/.github/workflows/on-push.yml @@ -49,120 +49,119 @@ jobs: name: lib-schema path: packages/lib/**/schema/ backend-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-backend' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-backend' + package-pat: ${{ secrets.PACKAGE_PAT }} integration-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-integration-tests' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-integration-tests' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-crypto-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-crypto' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-crypto' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-delivery-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-delivery' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-delivery' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-messaging-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-messaging' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-messaging' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-profile-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-profile' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-profile' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-shared-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-shared' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-shared' + package-pat: ${{ secrets.PACKAGE_PAT }} lib-storage-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-lib-storage' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-lib-storage' + package-pat: ${{ secrets.PACKAGE_PAT }} offchain-resolver-test: - runs-on: ubuntu-latest - needs: build - defaults: - run: - working-directory: 'packages/lib' - steps: - - uses: actions/checkout@v1 - - id: workspace-test - uses: ./test-action - with: - workspace-name: 'dm3-offchain-resolver' - package-pat: ${{ secrets.PACKAGE_PAT }} + runs-on: ubuntu-latest + needs: build + defaults: + run: + working-directory: 'packages/lib' + steps: + - uses: actions/checkout@v1 + - id: workspace-test + uses: ./test-action + with: + workspace-name: 'dm3-offchain-resolver' + package-pat: ${{ secrets.PACKAGE_PAT }} diff --git a/.yarn/versions/3dae3775.yml b/.yarn/versions/3dae3775.yml index 854760c12..7c3817114 100644 --- a/.yarn/versions/3dae3775.yml +++ b/.yarn/versions/3dae3775.yml @@ -1,16 +1,16 @@ undecided: - - dm3-backend - - dm3-billboard-client - - dm3-billboard-widget - - dm3-integration-tests - - dm3-lib-billboard-client-api - - dm3-lib-crypto - - dm3-lib-delivery - - dm3-lib-delivery-api - - dm3-lib-messaging - - dm3-lib-offchain-resolver-api - - dm3-lib-profile - - dm3-lib-shared - - dm3-lib-storage - - dm3-offchain-resolver - - dm3-react + - dm3-backend + - dm3-billboard-client + - dm3-billboard-widget + - dm3-integration-tests + - dm3-lib-billboard-client-api + - dm3-lib-crypto + - dm3-lib-delivery + - dm3-lib-delivery-api + - dm3-lib-messaging + - dm3-lib-offchain-resolver-api + - dm3-lib-profile + - dm3-lib-shared + - dm3-lib-storage + - dm3-offchain-resolver + - dm3-react diff --git a/.yarnrc.yml b/.yarnrc.yml index b29a1efeb..774c08021 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,17 +1,17 @@ nodeLinker: node-modules npmScopes: - testscope: - npmPublishRegistry: "http://localhost:4873" - npmRegistryServer: "http://localhost:4873" + testscope: + npmPublishRegistry: 'http://localhost:4873' + npmRegistryServer: 'http://localhost:4873' plugins: - - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs - spec: "@yarnpkg/plugin-workspace-tools" - - path: .yarn/plugins/@yarnpkg/plugin-version.cjs - spec: "@yarnpkg/plugin-version" + - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs + spec: '@yarnpkg/plugin-workspace-tools' + - path: .yarn/plugins/@yarnpkg/plugin-version.cjs + spec: '@yarnpkg/plugin-version' unsafeHttpWhitelist: - - localhost + - localhost yarnPath: .yarn/releases/yarn-3.5.1.cjs diff --git a/README.md b/README.md index f638a297a..82fe14f10 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,47 @@ # Packages -* [dm3-react](packages/react): dm3 UI components -* [dm3-web](packages/web): dm3 web app -* [dm3-backend](packages/backend): The delivery and storage service -* [dm3-lib](packages/lib): Basic dm3 functionality + +- [dm3-react](packages/react): dm3 UI components +- [dm3-web](packages/web): dm3 web app +- [dm3-backend](packages/backend): The delivery and storage service +- [dm3-lib](packages/lib): Basic dm3 functionality # Protocol + ## Simple Summary + dm3 Protocol enables decentral, open, and secure messaging based on established web3 services like ENS and IPFS. ## Specification + https://dm3.readthedocs.io/en/doc-latest/ -## Principles -* **Decentral**: An dm3 client must be realizable as a real decentral application and the messages must also be stored in a decentral way. -* **Open**: All parts of dm3 are open source and the protocol is permissionless. Everyone should be able to write an dm3 client. -* **Secure**: All messages are end-to-end encrypted and the encryption keys are only under the control of the user. +## Principles + +- **Decentral**: An dm3 client must be realizable as a real decentral application and the messages must also be stored in a decentral way. +- **Open**: All parts of dm3 are open source and the protocol is permissionless. Everyone should be able to write an dm3 client. +- **Secure**: All messages are end-to-end encrypted and the encryption keys are only under the control of the user. ## Terminology -* **Ethereum Account Key**: The private key linked to an [Externally Owned Account](https://ethereum.org/en/whitepaper/#ethereum-accounts). -* **Message Encryption Key Pair**: The key pair used to encrypt/decrypt messages. -* **Signing Key Pair**: The key pair used to sign/verify messages. -* **Storage Encryption Key**: Synchronous key to encrypt the user storage. `keccak256(personalSignWithEthAccount(salt))` -* **Delivery Service**: The service that buffers messages until they are delivered. -* **Registry**: A decentral service (e.g. ENS) mapping Ethereum accounts to profile registry entry URLs (e.g. using text records). -* **Profile Registry Entry**: A resource containing properties linked to an Ethereum account that is using dm3. E.g. public keys, the delivery service URL and spam filter configuration. + +- **Ethereum Account Key**: The private key linked to an [Externally Owned Account](https://ethereum.org/en/whitepaper/#ethereum-accounts). +- **Message Encryption Key Pair**: The key pair used to encrypt/decrypt messages. +- **Signing Key Pair**: The key pair used to sign/verify messages. +- **Storage Encryption Key**: Synchronous key to encrypt the user storage. `keccak256(personalSignWithEthAccount(salt))` +- **Delivery Service**: The service that buffers messages until they are delivered. +- **Registry**: A decentral service (e.g. ENS) mapping Ethereum accounts to profile registry entry URLs (e.g. using text records). +- **Profile Registry Entry**: A resource containing properties linked to an Ethereum account that is using dm3. E.g. public keys, the delivery service URL and spam filter configuration. ## Integration Guide -This guide explains how to make a messenger dm3 compatible. + +This guide explains how to make a messenger dm3 compatible. The integration test package contains executable integration examples: -- [Basic example](packages/integration-tests/src/lib/CreateAndSend.test.ts) -- [Example with a message proxy](packages/integration-tests/src/lib/CreateAndSendOverProxy.test.ts) + +- [Basic example](packages/integration-tests/src/lib/CreateAndSend.test.ts) +- [Example with a message proxy](packages/integration-tests/src/lib/CreateAndSendOverProxy.test.ts) + ### Delivery Service Setup + The following function creates a delivery service profile and the corresponding keys. `http://a` is the URL pointing to the delivery service endpoint. ```Typescript @@ -50,10 +60,12 @@ const profileJsonDataUri = createJsonDataUri(deliveryServiceProfileA); The data URI of the profile must be published on-chain as ENS `network.dm3.deliveryService` text record on the delivery service ENS domain. ### Creating and Publishing User Profiles + Every dm3 user needs to create and publish a profile containing: -- the public signing key, -- the public encryption key, -- and a list of the ENS names referencing the delivery service the user subscribed to + +- the public signing key, +- the public encryption key, +- and a list of the ENS names referencing the delivery service the user subscribed to The following function call will return the user profile, the keys, and the nonce. @@ -69,45 +81,50 @@ The user profiles need to be transformed into a data URI before they can be publ ```Typescript const profileJsonDataUriAlice = createJsonDataUri(aliceProfile); - ``` +``` The profiles must be published on-chain or made available via CCIP. Therefore the ENS `network.dm3.profile` text record needs to be set to the data URI containing the user profile. ### Sending and Receiving Messages + ```Mermaid flowchart LR s(Sender App) --> rds[Receiver Delivery Service] --> ra(Receiver App) ``` + To receive messages the dm3 clients can subscribe to a "new message" event via a WebSocket that is provided by the delivery service. The messages could also be fetched via a REST request. In the following example, Bob subscribes to a push service. The callback function is called by the delivery service if there is a message for Bob. The message has to be decrypted and the message signature must be checked. - ```Typescript -onMessage('bob.eth', async (encryptedEnvelop: EncryptionEnvelop) => { - - // The client will decrypt a received message - const envelop = await decryptEnvelop( - encryptedEnvelop, - bobKeys.encryptionKeyPair, - ); - // The client must check the signature of a received message - const valid = await checkMessageSignature( - envelop.message, - aliceProfile.profile.publicSigningKey, - 'alice.eth', - ); +```Typescript +onMessage('bob.eth', async (encryptedEnvelop: EncryptionEnvelop) => { - if (!valid) { - throw Error('Signature check failed'); - } + // The client will decrypt a received message + const envelop = await decryptEnvelop( + encryptedEnvelop, + bobKeys.encryptionKeyPair, + ); + + // The client must check the signature of a received message + const valid = await checkMessageSignature( + envelop.message, + aliceProfile.profile.publicSigningKey, + 'alice.eth', + ); + + if (!valid) { + throw Error('Signature check failed'); + } }); ``` + **Note:** The communication between the receiving delivery service and receiving client app is not part of the dm3 Message Transport Protocol. The following code example will create a message with the content 'Test Message'. + ```Typescript const messageAliceToBob = await createMessage( 'bob.eth', @@ -116,22 +133,25 @@ const messageAliceToBob = await createMessage( aliceKeys.signingKeyPair.privateKey, ); ``` + The message must be encrypted and put into an envelope containing the meta information needed for the delivery. The `createEnvelop` function will return an encrypted and unencrypted version of the envelope. The unencrypted version could be used for storing it with a chunk of other received and send messages in a message store. The message store could be encrypted with the generated symmetrical key. **Note**: The storage of sent or received messages is not part of the dm3 Message Transport Protocol. The following function returns a dm3 submit message JSON RPC Request. + ```Typescript const jsonRpcRequest = createJsonRpcCallSubmitMessage( encyptedEnvelopAliceToBob, ); ``` -This request needs to be submitted to the URL specified in the receiver delivery service profile. +This request needs to be submitted to the URL specified in the receiver delivery service profile. #### Message Proxy -Some messenger providers may prefer that their clients interact with a message proxy instead of directly sending messages to the receiver delivery service. +Some messenger providers may prefer that their clients interact with a message proxy instead of directly sending messages to the receiver delivery service. + ```Mermaid flowchart LR s(Sender App) --> m[Message Proxy] --> rds[Receiver Delivery Service] --> ra(Receiver App) @@ -149,6 +169,7 @@ const proxyEnvelope = await createProxyEnvelop( ``` The message proxy forwards the message using the `sendOverMessageProxy` function + ```Typescript await sendOverMessageProxy({ getRessource, @@ -167,6 +188,7 @@ await sendOverMessageProxy({ The function`handleMessageOnDeliveryService` will decrypt the delivery information and add a postmark (incoming timestamp) to the incoming envelope. + ```Typescript const processedEnvelop = await handleMessageOnDeliveryService( encryptedEnvelop, @@ -176,4 +198,3 @@ and add a postmark (incoming timestamp) to the incoming envelope. ``` After processing the envelope, the delivery service forwards the message to the receiver app. - diff --git a/packages/backend/README.md b/packages/backend/README.md index 968443b2e..0a20dc02c 100644 --- a/packages/backend/README.md +++ b/packages/backend/README.md @@ -1,4 +1,5 @@ # dm3-backend + ## Getting Started ### Build @@ -7,16 +8,16 @@ cd ../../ && yarn build ``` - ### Usage - yarn + ``` yarn start ``` -npm +npm + ``` npm start -``` \ No newline at end of file +``` diff --git a/packages/backend/docker-compose.yml b/packages/backend/docker-compose.yml index c0304fae8..c54a08503 100644 --- a/packages/backend/docker-compose.yml +++ b/packages/backend/docker-compose.yml @@ -2,10 +2,9 @@ version: '3.6' # The containers that compose the project services: - db: - image: redis - restart: always - container_name: redis - ports: - - '6379:6379' - + db: + image: redis + restart: always + container_name: redis + ports: + - '6379:6379' diff --git a/packages/billboard-client/README.md b/packages/billboard-client/README.md index baf10cb0b..17aa29b8b 100644 --- a/packages/billboard-client/README.md +++ b/packages/billboard-client/README.md @@ -1,4 +1,5 @@ # dm3-backend + ## Getting Started ### Build @@ -7,16 +8,16 @@ cd ../../ && yarn build ``` - ### Usage - yarn + ``` yarn start ``` -npm +npm + ``` yarn start -``` \ No newline at end of file +``` diff --git a/packages/billboard-client/docker-compose.yml b/packages/billboard-client/docker-compose.yml index 1f12f41bf..c558c6882 100644 --- a/packages/billboard-client/docker-compose.yml +++ b/packages/billboard-client/docker-compose.yml @@ -2,10 +2,9 @@ version: '3.6' # The containers that compose the project services: - db: - image: redis - restart: always - container_name: redis_billboard-client - ports: - - '6368:6368' - + db: + image: redis + restart: always + container_name: redis_billboard-client + ports: + - '6368:6368' diff --git a/packages/billboard-widget/.eslintrc.cjs b/packages/billboard-widget/.eslintrc.cjs index 4020bcbf4..f11621f51 100644 --- a/packages/billboard-widget/.eslintrc.cjs +++ b/packages/billboard-widget/.eslintrc.cjs @@ -1,14 +1,14 @@ module.exports = { - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - ], - parser: '@typescript-eslint/parser', - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': 'warn', - }, -} + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': 'warn', + }, +}; diff --git a/packages/billboard-widget/README.md b/packages/billboard-widget/README.md index 58abbf9c6..4294b70d1 100644 --- a/packages/billboard-widget/README.md +++ b/packages/billboard-widget/README.md @@ -85,17 +85,17 @@ Common billboard widget options: - `className`: A custom CSS class that will be added to the main div of the widget. This can be used for custom styling. -- `avatarSrc`: A custom URL for all user avatars, that takes the user's identifier (hash) and returns a URL. This function will be used to generate avatar images for users. +- `avatarSrc`: A custom URL for all user avatars, that takes the user's identifier (hash) and returns a URL. This function will be used to generate avatar images for users. - `userNameResolver`: A custom user name resolver function that takes the user identifier and returns a new string or a promise resolving to a string. - `dateFormat`: Formatting string for the date of each message, using date-fns formatter function. - - See: https://date-fns.org/v2.30.0/docs/format + - See: https://date-fns.org/v2.30.0/docs/format - - The default is `P` which stands for _Long localized date_. + - The default is `P` which stands for _Long localized date_. -- `relativeDate`: Display a relative date for recent dates. Default is `true`. +- `relativeDate`: Display a relative date for recent dates. Default is `true`. ### clientOptions diff --git a/packages/billboard-widget/index.html b/packages/billboard-widget/index.html index e0d1c8408..81aefabd3 100644 --- a/packages/billboard-widget/index.html +++ b/packages/billboard-widget/index.html @@ -1,13 +1,13 @@ - - - - - Vite + React + TS - - -
- - + + + + + Vite + React + TS + + +
+ + diff --git a/packages/billboard-widget/src/styles/app.pcss b/packages/billboard-widget/src/styles/app.pcss index d4a300496..ff4b6a4c3 100644 --- a/packages/billboard-widget/src/styles/app.pcss +++ b/packages/billboard-widget/src/styles/app.pcss @@ -320,4 +320,4 @@ button { padding: 4px 8px; } } - } +} diff --git a/packages/billboard-widget/src/styles/buttonWithTimer.pcss b/packages/billboard-widget/src/styles/buttonWithTimer.pcss index 253cf712a..14a28559d 100644 --- a/packages/billboard-widget/src/styles/buttonWithTimer.pcss +++ b/packages/billboard-widget/src/styles/buttonWithTimer.pcss @@ -32,11 +32,11 @@ background-color: #e1e2e9; } .CircularProgressbar-background { - fill: #81828d; + fill: #81828d; } .CircularProgressbar-path { - stroke: #ffcada; + stroke: #ffcada; } .CircularProgressbar-trail { - stroke: #81828d; -} \ No newline at end of file + stroke: #81828d; +} diff --git a/packages/billboard-widget/src/styles/classic.pcss b/packages/billboard-widget/src/styles/classic.pcss index b26c65671..e0814ef8a 100644 --- a/packages/billboard-widget/src/styles/classic.pcss +++ b/packages/billboard-widget/src/styles/classic.pcss @@ -70,4 +70,4 @@ } .dm3-send-message-btn:disabled { background-color: #ffcada; -} \ No newline at end of file +} diff --git a/packages/billboard-widget/src/styles/scrollbars.pcss b/packages/billboard-widget/src/styles/scrollbars.pcss index 363beb976..93e4b0b31 100644 --- a/packages/billboard-widget/src/styles/scrollbars.pcss +++ b/packages/billboard-widget/src/styles/scrollbars.pcss @@ -1,15 +1,15 @@ .styled-scrollbars { - --scrollbar-foreground: #4A4C60; - --scrollbar-background: rgba(58, 60, 80, 0.3); - scrollbar-color: var(--scrollbar-foreground) var(--scrollbar-background); + --scrollbar-foreground: #4a4c60; + --scrollbar-background: rgba(58, 60, 80, 0.3); + scrollbar-color: var(--scrollbar-foreground) var(--scrollbar-background); } .styled-scrollbars::-webkit-scrollbar { - width: 12px; + width: 12px; } .styled-scrollbars::-webkit-scrollbar-thumb { - background: var(--scrollbar-foreground); - border-radius: 6px; + background: var(--scrollbar-foreground); + border-radius: 6px; } .styled-scrollbars::-webkit-scrollbar-track { - background: var(--scrollbar-background); + background: var(--scrollbar-background); } diff --git a/packages/messenger-demo/README.md b/packages/messenger-demo/README.md index da2d0d410..3943ab9a4 100644 --- a/packages/messenger-demo/README.md +++ b/packages/messenger-demo/README.md @@ -1,4 +1,5 @@ # dm3-web + ## Getting Started ### Build @@ -7,16 +8,16 @@ cd ../../ && yarn build ``` - ### Usage +yarn: -yarn: ``` yarn start ``` -npm: +npm: + ``` yarn start -``` \ No newline at end of file +``` diff --git a/packages/messenger-demo/public/index.html b/packages/messenger-demo/public/index.html index ba3e8c43c..c1455225a 100644 --- a/packages/messenger-demo/public/index.html +++ b/packages/messenger-demo/public/index.html @@ -1,22 +1,22 @@ - - - - - - - - - - - - dm3 - - - -
- - + --> diff --git a/packages/messenger-demo/src/App.tsx b/packages/messenger-demo/src/App.tsx index 6a2b0ca9a..c636076b0 100644 --- a/packages/messenger-demo/src/App.tsx +++ b/packages/messenger-demo/src/App.tsx @@ -4,9 +4,15 @@ import 'bootstrap/dist/js/bootstrap.bundle.min.js'; import { DM3 } from 'messenger-widget'; function App() { + const props: any = { + defaultContact: 'help.dm3.eth', + defaultServiceUrl: process.env.REACT_APP_DEFAULT_SERVICE, + showAlways: true, + }; + return ( <> - + ); } diff --git a/packages/messenger-demo/src/index.css b/packages/messenger-demo/src/index.css index 24a9eac39..c7150e6c9 100644 --- a/packages/messenger-demo/src/index.css +++ b/packages/messenger-demo/src/index.css @@ -1,60 +1,53 @@ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - background-color: #222222; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: #222222; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; } .filler { - backdrop-filter: blur(1px) !important; - background-color: #0D0D0D !important; - cursor: initial !important; + backdrop-filter: blur(1px) !important; + background-color: #0d0d0d !important; + cursor: initial !important; } - .container { - margin-top: 2rem !important; + margin-top: 2rem !important; } .main-content-row { - - border-style: none !important; - /* box-shadow: 0px 0px 2px #999; */ - + border-style: none !important; + /* box-shadow: 0px 0px 2px #999; */ } .logo { - position: absolute; - filter: brightness(100%) ; - top: 2rem; - left: 2rem; - z-index: 9999; - cursor: pointer; - + position: absolute; + filter: brightness(100%); + top: 2rem; + left: 2rem; + z-index: 9999; + cursor: pointer; } .logo a { - text-decoration: none; - + text-decoration: none; } .logo:hover { - - filter: brightness(110%) ; - + filter: brightness(110%); } .main-content-row { - filter: drop-shadow(0px 0px 5px rgb(0 0 0 / 0.4)) + filter: drop-shadow(0px 0px 5px rgb(0 0 0 / 0.4)); } .legal { - font-size: 0.8rem !important; - text-decoration: none; -} \ No newline at end of file + font-size: 0.8rem !important; + text-decoration: none; +} diff --git a/packages/messenger-widget/README.md b/packages/messenger-widget/README.md index 6015f245f..95af2eb96 100644 --- a/packages/messenger-widget/README.md +++ b/packages/messenger-widget/README.md @@ -1,4 +1,5 @@ # messenger-widget + ## Getting Started ### Build @@ -7,12 +8,12 @@ cd ../../ && yarn build ``` - ### Usage + ``` import DM3 from 'messenger-widget'; ... -``` \ No newline at end of file +``` diff --git a/packages/messenger-widget/src/assets/images/dm3-logo.png b/packages/messenger-widget/src/assets/images/dm3-logo.png new file mode 100644 index 000000000..1e372ebcd Binary files /dev/null and b/packages/messenger-widget/src/assets/images/dm3-logo.png differ diff --git a/packages/messenger-widget/src/common.css b/packages/messenger-widget/src/common.css new file mode 100644 index 000000000..ee4842124 --- /dev/null +++ b/packages/messenger-widget/src/common.css @@ -0,0 +1,55 @@ +.border-radius-8 { + border-radius: 8px; +} + +.border-radius-6 { + border-radius: 6px; +} + +.border-radius-4 { + border-radius: 4px; +} + +.pointer-cursor { + cursor: pointer; +} + +.font-weight-400 { + font-weight: 400; +} + +.font-weight-500 { + font-weight: 500; +} + +.font-weight-800 { + font-weight: 800; +} + +.right-float { + float: right; +} + +.left-float { + float: left; +} + +.height-fill { + height: -webkit-fill-available; +} + +.width-fill { + width: -webkit-fill-available; +} + +.width-fit { + width: fit-content; +} + +.font-size-12 { + font-size: 12px; +} + +.display-none { + display: none; +} diff --git a/packages/messenger-widget/src/contexts/Accounts.ts b/packages/messenger-widget/src/contexts/Accounts.ts new file mode 100644 index 000000000..88dde8468 --- /dev/null +++ b/packages/messenger-widget/src/contexts/Accounts.ts @@ -0,0 +1,63 @@ +import { log } from 'dm3-lib-shared'; +import { + Accounts, + AccountsActions, + AccountsType, +} from '../utils/enum-type-utils'; +import { normalizeEnsName } from 'dm3-lib-profile'; + +export function accountsReducer(state: Accounts, action: AccountsActions) { + switch (action.type) { + case AccountsType.SetSelectedContact: + if (state.selectedContact === action.payload?.account.ensName) { + return state; + } else { + log( + `[Accounts] Set selected account to ${action.payload?.account.ensName}`, + 'info', + ); + + return { + ...state, + selectedContact: action.payload && { + ...action.payload, + account: { + ...action.payload.account, + ensName: normalizeEnsName( + action.payload.account.ensName, + ), + }, + }, + }; + } + + case AccountsType.SetContacts: + log( + `[Accounts] Set ${ + action.payload ? action.payload.length : '0' + } contacts`, + 'info', + ); + return { + ...state, + contacts: action.payload, + }; + + case AccountsType.SetAccountInfoView: + if (state.accountInfoView === action.payload) { + return state; + } else { + log( + `[Accounts] Set account info view ${action.payload}`, + 'info', + ); + return { + ...state, + accountInfoView: action.payload, + }; + } + + default: + return state; + } +} diff --git a/packages/messenger-widget/src/contexts/Auth.ts b/packages/messenger-widget/src/contexts/Auth.ts new file mode 100644 index 000000000..27d47e349 --- /dev/null +++ b/packages/messenger-widget/src/contexts/Auth.ts @@ -0,0 +1,24 @@ +import { AuthState } from '../interfaces/context'; +import { AuthStateActions, AuthStateType } from '../utils/enum-type-utils'; + +export function authReducer( + state: AuthState, + { type, payload }: AuthStateActions, +): AuthState { + switch (type) { + case AuthStateType.AddNewSession: + const allSessions = { + ...state.allSessions, + [payload.ensName]: payload, + }; + return { + ...state, + currentSession: payload, + allSessions, + recentlyUsedSession: payload.ensName, + }; + + default: + return state; + } +} diff --git a/packages/messenger-widget/src/contexts/Cache.ts b/packages/messenger-widget/src/contexts/Cache.ts new file mode 100644 index 000000000..aa94a31bb --- /dev/null +++ b/packages/messenger-widget/src/contexts/Cache.ts @@ -0,0 +1,46 @@ +import { log } from 'dm3-lib-shared'; +import { CacheActions, CacheType } from '../utils/enum-type-utils'; +import { formatAddress } from 'dm3-lib-profile'; +import { Cache } from '../interfaces/context'; + +export function cacheReducer(state: Cache, action: CacheActions): Cache { + switch (action.type) { + case CacheType.AddAvatarUrl: + if (state.avatarUrls.has(action.payload.ensName)) { + return state; + } + + log( + `[Cache] Add avatar url ${action.payload.url} for ${action.payload.ensName}`, + 'info', + ); + + const avatarUrls = new Map(state.avatarUrls); + avatarUrls.set(action.payload.ensName, action.payload.url); + return { + ...state, + avatarUrls, + }; + + case CacheType.AddAbis: + const abis = new Map(state.abis); + + action.payload.forEach((abiContainer) => { + const address = formatAddress(abiContainer.address); + if (state.abis.has(address)) { + log(`[Cache] ABI for ${address} already in cache`, 'info'); + } else { + log(`[Cache] Adding ABI for ${address}`, 'info'); + abis.set(address, abiContainer.abi); + } + }); + + return { + ...state, + abis, + }; + + default: + return state; + } +} diff --git a/packages/messenger-widget/src/contexts/Connection.ts b/packages/messenger-widget/src/contexts/Connection.ts new file mode 100644 index 000000000..4b486b90d --- /dev/null +++ b/packages/messenger-widget/src/contexts/Connection.ts @@ -0,0 +1,97 @@ +import { log } from 'dm3-lib-shared'; +import { Connection } from '../interfaces/web3'; +import { + ConnectionActions, + ConnectionState, + ConnectionType, +} from '../utils/enum-type-utils'; + +export function connectionReducer( + state: Connection, + action: ConnectionActions, +): Connection { + switch (action.type) { + case ConnectionType.ChangeConnectionState: + if (state.connectionState === action.payload) { + return state; + } else { + log( + `[Connection] New connection state ${ + ConnectionState[action.payload] + }`, + 'info', + ); + return { + ...state, + connectionState: action.payload, + }; + } + case ConnectionType.ChangeSocket: + log(`[Connection] New socket`, 'info'); + return { + ...state, + //@ts-ignore + socket: action.payload, + }; + + case ConnectionType.ChangeAccount: + log(`[Connection] Set account ${action.payload.ensName}`, 'info'); + return { + ...state, + account: action.payload, + }; + + case ConnectionType.ChangeEthAddress: + log(`[Connection] Set eth address to ${action.payload}`, 'info'); + return { + ...state, + ethAddress: action.payload, + }; + + case ConnectionType.ChangeStorageLocation: + if (state.storageLocation === action.payload) { + return state; + } else { + log( + `[Connection] Set storage location to ${action.payload}`, + 'info', + ); + + return { + ...state, + storageLocation: action.payload, + }; + } + case ConnectionType.ChangeProvider: + log(`[Connection] Set provider`, 'info'); + return { + ...state, + //@ts-ignore + provider: action.payload, + }; + + case ConnectionType.ChangeStorageToken: + if (state.storageToken === action.payload) { + return state; + } else { + log(`[Connection] Set sorage token`, 'info'); + return { + ...state, + storageToken: action.payload, + }; + } + + case ConnectionType.SetDefaultServiceUrl: + log( + `[Connection] set default service url ${action.payload}`, + 'info', + ); + return { + ...state, + defaultServiceUrl: action.payload, + }; + + default: + return state; + } +} diff --git a/packages/messenger-widget/src/contexts/UiState.ts b/packages/messenger-widget/src/contexts/UiState.ts new file mode 100644 index 000000000..51971a7a6 --- /dev/null +++ b/packages/messenger-widget/src/contexts/UiState.ts @@ -0,0 +1,73 @@ +import { log } from 'dm3-lib-shared'; +import { UiState } from '../interfaces/context'; +import { UiStateActions, UiStateType } from '../utils/enum-type-utils'; + +export function uiStateReducer( + state: UiState, + action: UiStateActions, +): UiState { + switch (action.type) { + case UiStateType.SetShowAddContact: + if (state.showAddContact === action.payload) { + return state; + } else { + log(`[UI] Set show add contact form ${action.payload}`, 'info'); + return { + ...state, + showAddContact: action.payload, + }; + } + + case UiStateType.SetSelectedRightView: + if (state.selectedRightView === action.payload) { + return state; + } else { + log(`[UI] Change right view to ${action.payload}`, 'info'); + return { + ...state, + selectedRightView: action.payload, + }; + } + + case UiStateType.SetMaxLeftView: + log(`[UI] maxLeftView: ${action.payload}`, 'info'); + return { + ...state, + maxLeftView: action.payload, + }; + + case UiStateType.ToggleShow: + log(`[UI] toggle show`, 'info'); + return { + ...state, + show: !state.show, + }; + + case UiStateType.SetLastMessagePull: + log(`[UI] set timestamp of last message pull`, 'info'); + return { + ...state, + lastMessagePull: action.payload, + }; + + case UiStateType.SetProfileExists: + log(`[UI] set profile exists to ${action.payload}`, 'info'); + return { + ...state, + proflieExists: action.payload, + }; + + case UiStateType.SetBrowserStorageBackup: + log( + `[UI] set create browser storage backups to ${action.payload}`, + 'info', + ); + return { + ...state, + browserStorageBackup: action.payload, + }; + + default: + return state; + } +} diff --git a/packages/messenger-widget/src/contexts/UserDB.ts b/packages/messenger-widget/src/contexts/UserDB.ts new file mode 100644 index 000000000..d9bf29264 --- /dev/null +++ b/packages/messenger-widget/src/contexts/UserDB.ts @@ -0,0 +1,232 @@ +import { + StorageEnvelopContainer, + UserDB, + createTimestamp, + getConversation, + sortEnvelops, +} from 'dm3-lib-storage'; +import { UserDbActions, UserDbType } from '../utils/enum-type-utils'; +import { normalizeEnsName } from 'dm3-lib-profile'; +import { getId } from 'dm3-lib-messaging'; +import { log } from 'dm3-lib-shared'; + +export function userDbReducer( + state: UserDB | undefined, + action: UserDbActions, +): UserDB | undefined { + const lastChangeTimestamp = createTimestamp(); + switch (action.type) { + case UserDbType.addMessage: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + + const container = action.payload.container; + const connection = action.payload.connection; + const newConversations = new Map( + state.conversations, + ); + + let hasChanged = false; + + const contactEnsName = normalizeEnsName( + container.envelop.message.metadata.from === + connection.account!.ensName + ? container.envelop.message.metadata.to + : container.envelop.message.metadata.from, + ); + + const prevContainers: StorageEnvelopContainer[] = getConversation( + contactEnsName, + [{ ensName: contactEnsName }], + state, + ); + + if (!container.envelop.id) { + container.envelop.id = getId(container.envelop); + } + + if (prevContainers.length === 0) { + newConversations.set(contactEnsName, [container]); + hasChanged = true; + } else if ( + prevContainers[prevContainers.length - 1].envelop.message + .metadata.timestamp < + container.envelop.message.metadata.timestamp + ) { + newConversations.set(contactEnsName, [ + ...prevContainers, + container, + ]); + hasChanged = true; + } else { + const otherContainer = prevContainers.filter( + (prevContainer) => + prevContainer.envelop.id !== container.envelop.id, + ); + + newConversations.set( + contactEnsName, + sortEnvelops([...otherContainer, container]), + ); + hasChanged = true; + } + + if (!hasChanged) { + return state; + } else { + log( + `[DB] Add message (timestamp: ${lastChangeTimestamp})`, + 'info', + ); + return { + ...state, + conversations: newConversations, + conversationsCount: Array.from(newConversations.keys()) + .length, + synced: false, + lastChangeTimestamp, + }; + } + + case UserDbType.setDB: + log(`[DB] Set db (timestamp: ${lastChangeTimestamp})`, 'info'); + return { + ...action.payload, + lastChangeTimestamp, + }; + + case UserDbType.createEmptyConversation: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + + if (state.conversations.has(normalizeEnsName(action.payload))) { + log( + `[DB] Converation exists already (timestamp: ${lastChangeTimestamp})`, + 'info', + ); + return state; + } + log( + `[DB] Create empty conversation (timestamp: ${lastChangeTimestamp})`, + 'info', + ); + + const conversations = new Map(state.conversations); + conversations.set(action.payload, []); + + return { + ...state, + conversations: conversations, + conversationsCount: Array.from(conversations.keys()).length, + synced: false, + lastChangeTimestamp, + }; + + case UserDbType.setSynced: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + if (state.synced === action.payload) { + return state; + } else { + log( + `[DB] Set synced to ${action.payload} (timestamp: ${lastChangeTimestamp})`, + 'info', + ); + + return { + ...state, + synced: action.payload, + lastChangeTimestamp, + }; + } + + case UserDbType.setConfigViewed: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + + log(`[DB] Set config viewed`, 'info'); + + return { + ...state, + configViewed: action.payload, + lastChangeTimestamp, + }; + + case UserDbType.setSyncProcessState: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + if (state.syncProcessState === action.payload) { + return state; + } else { + log( + `[DB] Set sync process state to ${action.payload} (timestamp: ${lastChangeTimestamp}) `, + 'info', + ); + + return { + ...state, + syncProcessState: action.payload, + lastChangeTimestamp, + }; + } + + case UserDbType.hideContact: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + + if ( + state.hiddenContacts.find( + (contact: { ensName: string; aka?: string }) => + contact === action.payload, + ) + ) { + log(`[DB] Contact ${action.payload} already hidden`, 'info'); + return state; + } else { + log(`[DB] Hide contact ${action.payload} `, 'info'); + + return { + ...state, + hiddenContacts: [...state.hiddenContacts, action.payload], + synced: false, + lastChangeTimestamp, + }; + } + + case UserDbType.unhideContact: + if (!state) { + throw Error(`UserDB hasn't been created.`); + } + + if ( + !state.hiddenContacts.find( + (contact) => contact.ensName === action.payload, + ) + ) { + log(`[DB] Contact ${action.payload} not hidden`, 'info'); + return state; + } else { + log(`[DB] Unhide contact ${action.payload} `, 'info'); + + return { + ...state, + hiddenContacts: state.hiddenContacts.filter( + (contact) => + normalizeEnsName(contact.ensName) !== + normalizeEnsName(action.payload), + ), + synced: false, + lastChangeTimestamp, + }; + } + + default: + return state; + } +} diff --git a/packages/messenger-widget/src/contexts/shared.ts b/packages/messenger-widget/src/contexts/shared.ts new file mode 100644 index 000000000..dc10f97ff --- /dev/null +++ b/packages/messenger-widget/src/contexts/shared.ts @@ -0,0 +1,39 @@ +import { StorageLocation } from 'dm3-lib-storage'; +import { + AccountInfo, + ConnectionState, + GlobalState, + SelectedRightView, +} from '../utils/enum-type-utils'; + +export const initialState: GlobalState = { + connection: { + connectionState: ConnectionState.CollectingSignInData, + storageLocation: StorageLocation.dm3Storage, + defaultServiceUrl: process.env.REACT_APP_BACKEND as string, + }, + accounts: { + contacts: undefined, + selectedContact: undefined, + accountInfoView: AccountInfo.None, + }, + cache: { + abis: new Map(), + avatarUrls: new Map(), + }, + userDb: undefined, + uiState: { + showAddContact: false, + selectedRightView: SelectedRightView.Chat, + maxLeftView: true, + show: false, + lastMessagePull: 0, + proflieExists: false, + browserStorageBackup: false, + }, + auth: { + currentSession: undefined, + recentlyUsedSession: undefined, + allSessions: {}, + }, +}; diff --git a/packages/messenger-widget/src/declaration.d.ts b/packages/messenger-widget/src/declaration.d.ts index 6ae82a0d8..335d8e55b 100644 --- a/packages/messenger-widget/src/declaration.d.ts +++ b/packages/messenger-widget/src/declaration.d.ts @@ -1,8 +1,9 @@ -declare module "*.png"; -declare module "*.gif"; -declare module "*.jpg"; +// To support images with extensions png, jpg and gif +declare module '*.png'; +declare module '*.gif'; +declare module '*.jpg'; -declare module "localforage" { - let localforage: LocalForage; - export = localforage; +declare module 'localforage' { + let localforage: LocalForage; + export = localforage; } diff --git a/packages/messenger-widget/src/index.css b/packages/messenger-widget/src/index.css index d26b88697..e82301865 100644 --- a/packages/messenger-widget/src/index.css +++ b/packages/messenger-widget/src/index.css @@ -1,3 +1,51 @@ .outer-container { min-height: 100vh; -} \ No newline at end of file +} + +.base-background { + background: var(--base-background); +} + +.background-container { + background: var(--background-container); +} + +.normal-btn { + background-color: var(--normal-btn); +} + +.normal-btn-hover { + background-color: var(--normal-btn-hover); +} + +.text-primary-color { + color: var(--text-primary-color); +} + +.normal-btn-inactive { + background-color: var(--normal-btn-inactive); +} + +.text-secondary-color { + color: var(--text-secondary-color); +} + +.background-active-contact { + background: var(--background-active-contact); +} + +.background-config-box { + background: var(--background-config-box); +} + +.background-config-box-border { + border: var(--background-config-box-border); +} + +.normal-btn-border { + border: var(--normal-btn-border); +} + +.config-box-border { + border: var(--config-box-border); +} diff --git a/packages/messenger-widget/src/index.tsx b/packages/messenger-widget/src/index.tsx index 06b123a19..b6dda9409 100644 --- a/packages/messenger-widget/src/index.tsx +++ b/packages/messenger-widget/src/index.tsx @@ -4,12 +4,20 @@ import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/js/bootstrap.bundle.min.js'; import '@fortawesome/fontawesome-free/css/all.min.css'; import { Home } from './views/Home/Home'; +import { setTheme } from './utils/style-utils'; +import { Config } from './interfaces/config'; +import { getConfig } from './utils/config-utils'; +import GlobalContextProvider from './utils/context-utils'; -export function DM3() { +export function DM3(props: Partial) { + const propsData: Config = getConfig(props); + setTheme(propsData.theme); return ( -
- +
+ + +
); } diff --git a/packages/messenger-widget/src/interfaces/config.ts b/packages/messenger-widget/src/interfaces/config.ts index e968008fd..f48f51332 100644 --- a/packages/messenger-widget/src/interfaces/config.ts +++ b/packages/messenger-widget/src/interfaces/config.ts @@ -1,22 +1,22 @@ import { StorageLocation } from 'dm3-lib-storage'; export interface Config { - defaultContact?: string; - showContacts: boolean; - inline: boolean; - defaultStorageLocation: StorageLocation; - hideStorageSelection: boolean; - style: React.CSSProperties; - defaultServiceUrl: string; - showAlways: boolean; - miniSignIn: boolean; - connectionStateChange?: (newState: any) => void; - warnBeforeLeave: boolean; - browserStorageBackup: boolean; - showHelp: boolean; - theme: string | undefined | null; + defaultContact?: string; + showContacts: boolean; + inline: boolean; + defaultStorageLocation: StorageLocation; + hideStorageSelection: boolean; + style: React.CSSProperties; + defaultServiceUrl: string; + showAlways: boolean; + miniSignIn: boolean; + connectionStateChange?: (newState: any) => void; + warnBeforeLeave: boolean; + browserStorageBackup: boolean; + showHelp: boolean; + theme: string | undefined | null; } export interface Dm3Props { - config: Config; -} \ No newline at end of file + config: Config; +} diff --git a/packages/messenger-widget/src/interfaces/context.ts b/packages/messenger-widget/src/interfaces/context.ts index e41f666e0..c23b2fa3b 100644 --- a/packages/messenger-widget/src/interfaces/context.ts +++ b/packages/messenger-widget/src/interfaces/context.ts @@ -1,3 +1,39 @@ +import { Account, DeliveryServiceProfile } from 'dm3-lib-profile'; +import { SelectedRightView } from '../utils/enum-type-utils'; + +export interface Contact { + account: Account; + deliveryServiceProfile?: DeliveryServiceProfile; +} + +export interface AuthState { + allSessions: { [address: string]: AuthSession }; + currentSession?: AuthSession; + recentlyUsedSession?: string; +} + +export interface AuthSession { + storage: string; + token: string; + ensName: string; + storageEncryptionKey?: string; +} + +export interface Cache { + abis: Map; + avatarUrls: Map; +} + +export interface UiState { + showAddContact: boolean; + selectedRightView: SelectedRightView; + maxLeftView: boolean; + show: boolean; + lastMessagePull: number; + proflieExists: boolean; + browserStorageBackup: boolean; +} + export interface GlobalContextProviderProps { - children: JSX.Element; -} \ No newline at end of file + children: JSX.Element; +} diff --git a/packages/messenger-widget/src/interfaces/web3.ts b/packages/messenger-widget/src/interfaces/web3.ts new file mode 100644 index 000000000..26fbc1f9b --- /dev/null +++ b/packages/messenger-widget/src/interfaces/web3.ts @@ -0,0 +1,29 @@ +import { ethers } from 'ethers'; +import { Socket } from 'socket.io-client'; +import { DefaultEventsMap } from 'socket.io/dist/typed-events'; +import { StorageLocation } from 'dm3-lib-storage'; +import { Account } from 'dm3-lib-profile'; +import { ConnectionState } from '../utils/enum-type-utils'; + +declare global { + interface Window { + ethereum?: any; + } +} + +export interface Connection { + connectionState: ConnectionState; + ethAddress?: string; + account?: Account; + provider?: ethers.providers.JsonRpcProvider; + socket?: Socket; + storageToken?: string; + storageLocation: StorageLocation; + defaultServiceUrl: string; +} + +export interface SignInProps { + hideStorageSelection: boolean; + miniSignIn: boolean; + defaultStorageLocation: StorageLocation | undefined; +} diff --git a/packages/messenger-widget/src/utils/config-utils.ts b/packages/messenger-widget/src/utils/config-utils.ts new file mode 100644 index 000000000..063bd16b2 --- /dev/null +++ b/packages/messenger-widget/src/utils/config-utils.ts @@ -0,0 +1,23 @@ +import { StorageLocation } from 'dm3-lib-storage'; +import { Config } from '../interfaces/config'; + +// The default configuration of the app +const DefaultConfig: Config = { + showContacts: true, + inline: false, + hideStorageSelection: false, + defaultStorageLocation: StorageLocation.dm3Storage, + style: {}, + defaultServiceUrl: 'http://localhost:8080' as string, + showAlways: false, + connectionStateChange: undefined, + miniSignIn: false, + warnBeforeLeave: false, + browserStorageBackup: false, + showHelp: false, + theme: 'default', +}; + +export function getConfig(overwrite: Partial): Config { + return { ...DefaultConfig, ...overwrite }; +} diff --git a/packages/messenger-widget/src/utils/context-utils.tsx b/packages/messenger-widget/src/utils/context-utils.tsx new file mode 100644 index 000000000..f393c1803 --- /dev/null +++ b/packages/messenger-widget/src/utils/context-utils.tsx @@ -0,0 +1,52 @@ +import React, { Dispatch } from 'react'; +import { accountsReducer } from '../contexts/Accounts'; +import { authReducer } from '../contexts/Auth'; +import { cacheReducer } from '../contexts/Cache'; +import { connectionReducer } from '../contexts/Connection'; +import { initialState } from '../contexts/shared'; +import { uiStateReducer } from '../contexts/UiState'; +import { userDbReducer } from '../contexts/UserDB'; +import { GlobalContextProviderProps } from '../interfaces/context'; +import { + AccountsActions, + Actions, + AuthStateActions, + CacheActions, + ConnectionActions, + GlobalState, + UiStateActions, + UserDbActions, +} from './enum-type-utils'; + +// custom context +export const GlobalContext = React.createContext<{ + state: GlobalState; + dispatch: Dispatch; +}>({ state: initialState, dispatch: () => null }); + +// combined all reducers in single reducer +const mainReducer = (state: GlobalState, action: Actions): GlobalState => ({ + connection: connectionReducer( + state.connection, + action as ConnectionActions, + ), + cache: cacheReducer(state.cache, action as CacheActions), + accounts: accountsReducer(state.accounts, action as AccountsActions), + userDb: userDbReducer(state.userDb, action as UserDbActions), + uiState: uiStateReducer(state.uiState, action as UiStateActions), + auth: authReducer(state.auth, action as AuthStateActions), +}); + +// global context provider to handle state sharing +function GlobalContextProvider(props: GlobalContextProviderProps) { + const [state, dispatch] = React.useReducer(mainReducer, initialState); + + return ( + /** @ts-ignore */ + + {props.children} + + ); +} + +export default GlobalContextProvider; diff --git a/packages/messenger-widget/src/utils/enum-type-utils.ts b/packages/messenger-widget/src/utils/enum-type-utils.ts new file mode 100644 index 000000000..445db7a84 --- /dev/null +++ b/packages/messenger-widget/src/utils/enum-type-utils.ts @@ -0,0 +1,215 @@ +import { + AuthSession, + AuthState, + Cache, + Contact, + UiState, +} from '../interfaces/context'; +import { ethers } from 'ethers'; +import { Socket } from 'socket.io-client'; +import { DefaultEventsMap } from 'socket.io/dist/typed-events'; +import { + StorageEnvelopContainer, + StorageLocation, + SyncProcessState, + UserDB, +} from 'dm3-lib-storage'; +import { Account } from 'dm3-lib-profile'; +import { Connection } from '../interfaces/web3'; + +export type ActionMap = { + [Key in keyof M]: M[Key] extends undefined + ? { + type: Key; + } + : { + type: Key; + payload: M[Key]; + }; +}; + +export type Accounts = { + contacts: Contact[] | undefined; + selectedContact: Contact | undefined; + accountInfoView: AccountInfo; +}; + +export type AccountsPayload = { + [AccountsType.SetSelectedContact]: Contact | undefined; + [AccountsType.SetContacts]: Contact[] | undefined; + [AccountsType.SetAccountInfoView]: AccountInfo; + [AccountsType.RemoveContact]: string; +}; + +export type AccountsActions = + ActionMap[keyof ActionMap]; + +export type AuthStatePayload = { + [AuthStateType.AddNewSession]: AuthSession; +}; + +export type AuthStateActions = + ActionMap[keyof ActionMap]; + +export type CachePayload = { + [CacheType.AddAbis]: { address: string; abi: string }[]; + [CacheType.AddAvatarUrl]: { ensName: string; url: string }; +}; + +export type CacheActions = + ActionMap[keyof ActionMap]; + +export type ConnectionPayload = { + [ConnectionType.ChangeConnectionState]: ConnectionState; + [ConnectionType.ChangeSocket]: Socket; + [ConnectionType.ChangeAccount]: Account; + [ConnectionType.ChangeEthAddress]: string; + [ConnectionType.ChangeProvider]: ethers.providers.JsonRpcProvider; + [ConnectionType.ChangeStorageToken]: string | undefined; + [ConnectionType.ChangeStorageLocation]: StorageLocation; + [ConnectionType.SetDefaultServiceUrl]: string; +}; + +export type ConnectionActions = + ActionMap[keyof ActionMap]; + +export type GlobalState = { + connection: Connection; + accounts: Accounts; + cache: Cache; + userDb: UserDB | undefined; + uiState: UiState; + auth: AuthState; +}; + +export type UserDbPayload = { + [UserDbType.addMessage]: { + container: StorageEnvelopContainer; + connection: Connection; + }; + [UserDbType.setDB]: UserDB; + [UserDbType.createEmptyConversation]: string; + [UserDbType.setSynced]: boolean; + [UserDbType.setConfigViewed]: boolean; + [UserDbType.setSyncProcessState]: SyncProcessState; + [UserDbType.hideContact]: { ensName: string; aka?: string }; + [UserDbType.unhideContact]: string; +}; + +export type UserDbActions = + ActionMap[keyof ActionMap]; + +export type UiStatePayload = { + [UiStateType.SetShowAddContact]: boolean; + [UiStateType.SetSelectedRightView]: SelectedRightView; + [UiStateType.SetMaxLeftView]: boolean; + [UiStateType.ToggleShow]: undefined; + [UiStateType.SetLastMessagePull]: number; + [UiStateType.SetProfileExists]: boolean; + [UiStateType.SetBrowserStorageBackup]: boolean; +}; + +export type UiStateActions = + ActionMap[keyof ActionMap]; + +export type Actions = + | ConnectionActions + | CacheActions + | AccountsActions + | UserDbActions + | UiStateActions + | AuthStateActions; + +export enum AccountsType { + SetSelectedContact = 'SET_SELECTED_CONTACT', + SetContacts = 'SET_CONTACTS', + SetAccountInfoView = 'ACCOUNT_INFO_VIEW', + RemoveContact = 'REMOVE_CONTACT', +} + +export enum AccountInfo { + None, + Contact, + Account, + DomainConfig, +} + +export enum AuthStateType { + AddNewSession = 'ADD_NEW_SESSION', +} + +export enum CacheType { + AddEnsName = 'ADD_ENS_NAME', + AddAbis = 'ADD_ABIS', + AddAvatarUrl = 'ADD_AVATAR_URL', +} + +export enum ConnectionType { + ChangeConnectionState = 'CHANGE_CONNECTION_STATE', + ChangeSocket = 'CHANGE_SOCKET', + ChangeAccount = 'CHANGE_ACCOUNT', + ChangeEthAddress = 'CHANGE_ETH_ADDRESS', + ChangeProvider = 'CHANGE_PROVIDER', + ChangeStorageToken = 'CHANGE_STORAGE_TOKEN', + ChangeStorageLocation = 'CHANGE_STORAGE_LOCATION', + SetDefaultServiceUrl = 'SET_DEFAULT_SERVICE_URL', +} + +export enum UserDbType { + addMessage = 'ADD_MESSAGE', + setDB = 'SET_DB', + createEmptyConversation = 'CREATE_EMPTY_CONVERSATION', + setSynced = 'SET_SYNCED', + setConfigViewed = 'SET_CONFIG_VIEWED', + setSyncProcessState = 'SET_SYNC_PROCESS_STATE', + hideContact = 'HIDE_CONTACT', + unhideContact = 'UNHIDE_CONTACT', +} + +export enum SelectedRightView { + Error, + Chat, + UserInfo, +} + +export enum UiStateType { + SetShowAddContact = 'SET_SHOW_ADD_CONTACT', + SetSelectedRightView = 'SET_SELECTED_RIGHT_VIEW', + SetMaxLeftView = 'SET_MAX_LEFT_VIEW', + ToggleShow = 'ToggleShow', + SetLastMessagePull = 'SET_LAST_MESSAGE_PULL', + SetProfileExists = 'SET_PROFILE_EXISTS', + SetBrowserStorageBackup = 'SET_BROWSER_STORAGE_BACKUP', +} + +export enum ConnectionState { + CollectingSignInData, + SignInReady, + AccountConnectReady, + WaitingForAccountConnection, + WaitingForSignIn, + ConnectionRejected, + SignInFailed, + SignedIn, +} + +export enum GoogleAuthState { + Ready, + Pending, + Success, + Failed, +} + +export enum ButtonState { + Ideal, + Failed, + Loading, + Success, + Disabled, +} + +export enum SignInBtnValues { + SignIn = 'Sign In', + WaitingForSigature = 'Waiting for signature...', + SigningIn = 'Signing In', +} diff --git a/packages/messenger-widget/src/utils/style-utils.ts b/packages/messenger-widget/src/utils/style-utils.ts new file mode 100644 index 000000000..7e0b4f28b --- /dev/null +++ b/packages/messenger-widget/src/utils/style-utils.ts @@ -0,0 +1,64 @@ +import { themeOne } from './theme-utils'; + +// Method to set styles in classes +export const setTheme = (theme: string | undefined | null) => { + const themeDetails: any = selectTheme(theme); + document.body.style.setProperty( + '--base-background', + themeDetails.baseBackground, + ); + document.body.style.setProperty( + '--background-container', + themeDetails.backgroundContainer, + ); + document.body.style.setProperty( + '--normal-btn-border', + themeDetails.normalBtnBorder, + ); + document.body.style.setProperty( + '--config-box-border', + themeDetails.configBoxBorder, + ); + document.body.style.setProperty('--normal-btn', themeDetails.normalBtn); + document.body.style.setProperty( + '--normal-btn-hover', + themeDetails.normalBtnHover, + ); + document.body.style.setProperty( + '--normal-btn-inactive', + themeDetails.normalBtnInactive, + ); + document.body.style.setProperty( + '--text-primary-color', + themeDetails.textPrimary, + ); + document.body.style.setProperty( + '--text-secondary-color', + themeDetails.textSecondary, + ); + document.body.style.setProperty( + '--background-active-contact', + themeDetails.backgroundActiveContact, + ); + document.body.style.setProperty( + '--background-config-box', + themeDetails.backgroundConfigBox, + ); + document.body.style.setProperty( + '--background-config-box-border', + themeDetails.backgroundConfigBoxBorder, + ); +}; + +// Method to get all css style class based on theme selected +export const selectTheme = (theme: string | undefined | null): object => { + if (!theme) { + return themeOne; + } + switch (theme) { + case 'dark': + return themeOne; + default: + return themeOne; + } +}; diff --git a/packages/messenger-widget/src/utils/theme-utils.ts b/packages/messenger-widget/src/utils/theme-utils.ts new file mode 100644 index 000000000..b43e2d782 --- /dev/null +++ b/packages/messenger-widget/src/utils/theme-utils.ts @@ -0,0 +1,16 @@ +export const themeOne: any = { + baseBackground: + 'linear-gradient(25deg, #9341FE -437%, #0D0D0D 71%, #2CA6FF 145%)', + backgroundContainer: '#1A1B22', + normalBtnBorder: '2px solid #544393', + configBoxBorder: '1px solid white', + normalBtn: '#28204A', + normalBtnHover: '#544393', + normalBtnInactive: '#2A2B38', + textPrimary: '#FFFF', + textSecondary: '#81828D', + backgroundActiveContact: + 'linear-gradient(89.87deg, rgba(84, 67, 147, 0.5) 1.99%, rgba(31, 32, 41, 0.5) 90.93%)', + backgroundConfigBox: '#3A3C50', + backgroundConfigBoxBorder: '1px solid #666876', +}; diff --git a/packages/messenger-widget/src/views/Home/Home.css b/packages/messenger-widget/src/views/Home/Home.css index e69de29bb..ad83000d8 100644 --- a/packages/messenger-widget/src/views/Home/Home.css +++ b/packages/messenger-widget/src/views/Home/Home.css @@ -0,0 +1,17 @@ +.entry { + all: initial; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.logo-container { + padding: 2rem 0rem 0rem 1rem; +} + +.dm3-logo { + height: 2rem; +} diff --git a/packages/messenger-widget/src/views/Home/Home.tsx b/packages/messenger-widget/src/views/Home/Home.tsx index 901a042a2..200783866 100644 --- a/packages/messenger-widget/src/views/Home/Home.tsx +++ b/packages/messenger-widget/src/views/Home/Home.tsx @@ -1,7 +1,13 @@ -import "./Home.css"; +import './Home.css'; +import dm3Logo from '../../assets/images/dm3-logo.png'; +import { Dm3Props } from '../../interfaces/config'; -export function Home() { - return ( -
Home...
- ); -} \ No newline at end of file +export function Home(props: Dm3Props) { + return ( +
+
+ DM3 logo +
+
+ ); +} diff --git a/packages/offchain-resolver/docker-compose.yml b/packages/offchain-resolver/docker-compose.yml index 5a8478a55..31f0b7429 100644 --- a/packages/offchain-resolver/docker-compose.yml +++ b/packages/offchain-resolver/docker-compose.yml @@ -2,12 +2,11 @@ version: '3.6' # The containers that compose the project services: - - db: - image: postgres - restart: always - container_name: offchain_resolver_db - environment: - POSTGRES_PASSWORD: example - ports: - - '5432:5432' + db: + image: postgres + restart: always + container_name: offchain_resolver_db + environment: + POSTGRES_PASSWORD: example + ports: + - '5432:5432' diff --git a/packages/react/README.md b/packages/react/README.md index 1f6cbfa4e..84068186d 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -1,4 +1,5 @@ # dm3-react + ## Getting Started ### Build @@ -7,12 +8,12 @@ cd ../../ && yarn build ``` - ### Usage + ``` import DM3 from 'dm3-react'; ... -``` \ No newline at end of file +``` diff --git a/packages/react/src/Dm3.css b/packages/react/src/Dm3.css index db73e9ccb..4e4d57016 100644 --- a/packages/react/src/Dm3.css +++ b/packages/react/src/Dm3.css @@ -1,214 +1,195 @@ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; } .circle-char { + width: 38px; + height: 38px; + margin-right: 1rem; - width: 38px; - height: 38px; - margin-right: 1rem; + background-color: #b8b8b8; - background-color: #b8b8b8; - - color: #fff; - text-align: center; + color: #fff; + text-align: center; } .header-lock { - color: #b8b8b8; - font-size: 1.5rem; - + color: #b8b8b8; + font-size: 1.5rem; } .row-space-2 { - margin-top: 4rem !important; + margin-top: 4rem !important; } .push-end { - float: right + float: right; } .row-space-lg { - margin-top: 3rem !important; + margin-top: 3rem !important; } .row-space { - margin-top: 2rem !important; + margin-top: 2rem !important; } .row-space-sm { - margin-top: 1rem !important; + margin-top: 1rem !important; } .row-space-xs { - margin-top: 0.5rem !important; + margin-top: 0.5rem !important; } .container { - padding-top: 2rem; - height: 80%; - cursor: initial; + padding-top: 2rem; + height: 80%; + cursor: initial; } .main-content-row { - - overflow: hidden; - /* min-height: 500px !important; */ - height: 100%; - max-height: 100%; - border-radius: 18px; - background-color: var(--mainDark); - border: solid var(--mainBorder) 2px; + overflow: hidden; + /* min-height: 500px !important; */ + height: 100%; + max-height: 100%; + border-radius: 18px; + background-color: var(--mainDark); + border: solid var(--mainBorder) 2px; } .main-content-row-max { - - - min-height: 500px !important; - height: 100%; - max-height: 100%; - border-radius: 18px; + min-height: 500px !important; + height: 100%; + max-height: 100%; + border-radius: 18px; } .content-container { - padding: 0px !important; - max-height: 100%; - background-color: #1F2029; - + padding: 0px !important; + max-height: 100%; + background-color: #1f2029; } - - -html, body { - height: 100%; +html, +body { + height: 100%; } .top-left-radius { - border-top-left-radius: 18px; + border-top-left-radius: 18px; } .top-right-radius { - border-top-right-radius: 18px; + border-top-right-radius: 18px; } .bottom-left-radius { - border-bottom-left-radius: 18px; + border-bottom-left-radius: 18px; } .bottom-right-radius { - border-bottom-right-radius: 18px; + border-bottom-right-radius: 18px; } - - /* .body-row { height: 90%; } */ - .start-space { - margin-top: 2.5rem; + margin-top: 2.5rem; } .error-indication { - color: red; + color: red; } .min-left-view { - background-color: #2f8aff26 ; + background-color: #2f8aff26; } - .left-view { - min-height: inherit; + min-height: inherit; } .left-view .text-muted { - color: #bdbdbd!important; + color: #bdbdbd !important; } .right-btn { - border-width: 2px !important; - border-radius: 0.5rem; - height: 40px !important; - width: 40px !important; - padding: 0px !important; + border-width: 2px !important; + border-radius: 0.5rem; + height: 40px !important; + width: 40px !important; + padding: 0px !important; } .right-state-button { - background-color: var(--mainLight); - color: var(--lightHighlight); - border-radius: 18px; + background-color: var(--mainLight); + color: var(--lightHighlight); + border-radius: 18px; - border-style: none; - line-height: normal; + border-style: none; + line-height: normal; } .right-state-button:hover { - background-color: var(--mainLight); - color: var(--lightHighlight); - filter: brightness(103%); + background-color: var(--mainLight); + color: var(--lightHighlight); + filter: brightness(103%); } ::-webkit-scrollbar-track { - padding: 2px; + padding: 2px; } ::-webkit-scrollbar { - background: #ffffff00 !important; - width: 8px; - - + background: #ffffff00 !important; + width: 8px; } ::-webkit-scrollbar-thumb { - background: #0000003f !important; - - - background-clip: padding-box; - border: 1px solid rgba(0, 0, 0, 0) !important; - border-radius: 0px !important; - - + background: #0000003f !important; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0) !important; + border-radius: 0px !important; } ::-webkit-scrollbar-thumb:hover { - background: #0000005f !important; + background: #0000005f !important; } .filler { - position: fixed; - top: 0px; - left: 0px; - width: 100vw; - height: 100vh; - -webkit-backdrop-filter: blur(5px); - backdrop-filter: blur(5px); - background: rgba(168, 168, 168, 0.1); - z-index: 1001; + position: fixed; + top: 0px; + left: 0px; + width: 100vw; + height: 100vh; + -webkit-backdrop-filter: blur(5px); + backdrop-filter: blur(5px); + background: rgba(168, 168, 168, 0.1); + z-index: 1001; } .filler:hover { - cursor: pointer; + cursor: pointer; } .btn-outline-secondary { - - border-color: var(--lightHighlight); - color: var(--lightHighlight); + border-color: var(--lightHighlight); + color: var(--lightHighlight); } .account-tooltip .tooltip-inner { - - font-size: 0.6rem; -} \ No newline at end of file + font-size: 0.6rem; +} diff --git a/packages/react/src/chat/Chat.css b/packages/react/src/chat/Chat.css index 34e96b1ef..7ba2add4b 100644 --- a/packages/react/src/chat/Chat.css +++ b/packages/react/src/chat/Chat.css @@ -1,5 +1,5 @@ .rcw-widget-container { - position: relative !important; + position: relative !important; margin: 0px !important; height: 100%; max-height: 100%; @@ -8,41 +8,38 @@ .account-header-text { cursor: pointer; - color: #81828D - } + color: #81828d; +} .profile-warning { -color: #81828D; -font-size: 0.8rem !important; -background-color: #fff3cd22; -border-color: #ffecb522; -padding-top: 0.5rem; -padding-bottom: 0.5rem; -border-radius: 10px !important; + color: #81828d; + font-size: 0.8rem !important; + background-color: #fff3cd22; + border-color: #ffecb522; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + border-radius: 10px !important; } - .info-box { margin: 1rem !important; font-size: 1rem; border-radius: 18px; } - + .rcw-messages-container { - flex-grow: 1!important; + flex-grow: 1 !important; padding-top: 5px !important; max-height: 100% !important; - background-color: #1F2029; + background-color: #1f2029; padding: 5px; padding-right: 0px; } .rcw-input { - padding-left: 5px; padding-right: 40px; - } .rcw-input::-webkit-scrollbar-thumb { @@ -52,16 +49,16 @@ border-radius: 10px !important; .rcw-picker-btn { display: none; } - + .rcw-new-message { height: 100%; width: 100%; border: solid var(--mainLight) 2px !important; border-radius: 10px; color: #ffffff; - background-color: #3A3C50 !important; + background-color: #3a3c50 !important; } - + .rcw-message { margin-bottom: 0px !important; margin-top: 0px !important; @@ -70,43 +67,38 @@ border-radius: 10px !important; .rcw-header { display: none !important; } - + .rcw-send { position: absolute; background-color: transparent !important; align-self: center !important; right: 20px; padding: 0px !important; - } - + .rcw-sender { position: relative; height: 100px !important; border-radius: 0px !important; - background-color: #1F2029 !important; + background-color: #1f2029 !important; display: flex; - - } - .rcw-client .rcw-message-text { color: #ffffff; max-width: none !important; margin-left: 3rem !important; border-radius: 6px; - background-color: #6773DD; + background-color: #6773dd; } .rcw-response .rcw-message-text { color: #ffffff; border-radius: 6px; - - background-color: #3A3C50; -} + background-color: #3a3c50; +} .rcw-conversation-container { box-shadow: none !important; @@ -114,14 +106,14 @@ border-radius: 10px !important; max-height: 100%; height: 100%; display: flex; - flex-direction: column!important; + flex-direction: column !important; } - + .msg-container { min-width: 630px; } .message-state { margin-bottom: 15px; - color: #81828D; + color: #81828d; } diff --git a/packages/react/src/contacts/AccountNameHeader.css b/packages/react/src/contacts/AccountNameHeader.css index 13d5489c6..b6bef1129 100644 --- a/packages/react/src/contacts/AccountNameHeader.css +++ b/packages/react/src/contacts/AccountNameHeader.css @@ -1,23 +1,18 @@ .account-header-text { cursor: pointer; - - } - .account-name { +} +.account-name { height: fit-content; font-size: 1.2rem; font-weight: 400; - - } - - - +} .header-buttons > div { - padding: 0px !important + padding: 0px !important; } .show-add-btn { border-radius: 4px !important; height: 30px !important; width: 30px !important; -} \ No newline at end of file +} diff --git a/packages/react/src/contacts/Contacts.css b/packages/react/src/contacts/Contacts.css index 95339c3a4..6776857a4 100644 --- a/packages/react/src/contacts/Contacts.css +++ b/packages/react/src/contacts/Contacts.css @@ -93,4 +93,4 @@ .add-contact-error { background-color: #c2606a !important; -} \ No newline at end of file +} diff --git a/packages/react/src/domain-config/Config.css b/packages/react/src/domain-config/Config.css index 33f4504b7..9d5c96699 100644 --- a/packages/react/src/domain-config/Config.css +++ b/packages/react/src/domain-config/Config.css @@ -1,16 +1,14 @@ .config-banner { - background-color: #3A3C50; + background-color: #3a3c50; color: #ffffff; margin: 10px; border-radius: 10px; - } .config-btn { color: #ffffff; border-color: #ffffff; - } /* .config-btn:hover { @@ -33,14 +31,13 @@ .user-info input { color: #ffffff; - background-color: #3A3C50 !important; + background-color: #3a3c50 !important; border-style: none; height: 3rem; } .user-info input:focus { color: #ffffff; - } .user-info-text-muted { @@ -53,7 +50,7 @@ .user-info .input-group-text { color: #8f8f93; - background-color: #1F2029 !important; + background-color: #1f2029 !important; } .user-info * { @@ -62,9 +59,8 @@ .user-info .form-check-input { width: 3rem; - - background-color: #1F2029 !important; + background-color: #1f2029 !important; } .user-info .explain-text-sm { @@ -74,7 +70,6 @@ .value-text { color: #8f8f93; - } .user-info .explain-text { @@ -84,21 +79,20 @@ } .config-text { - color: #8f8f93 !important; + color: #8f8f93 !important; } .user-info .form-control { border-radius: 0.25rem !important; } - .check-container { padding: 0; width: 3rem; } .submit-btn { - border-color: #6773DD !important; + border-color: #6773dd !important; color: #ffffff; border-style: solid; border-radius: 10px; @@ -106,25 +100,16 @@ width: 300px !important; } +.submit-btn:hover { + background-color: #6773dd !important; +} - - .submit-btn:hover { - - background-color: #6773DD !important; - - - } - - .submit-btn:hover:disabled { - +.submit-btn:hover:disabled { background-color: transparent !important; border-color: #6c757d !important; - - } +} - .submit-btn:disabled { - +.submit-btn:disabled { background-color: transparent !important; border-color: #6c757d !important; - - } \ No newline at end of file +} diff --git a/packages/react/src/header/Header.css b/packages/react/src/header/Header.css index 3ef15b6f6..f6945fae6 100644 --- a/packages/react/src/header/Header.css +++ b/packages/react/src/header/Header.css @@ -1,16 +1,8 @@ - - .header-row-left { - background-color: var(--mainDark);; + background-color: var(--mainDark); color: #ffffffd5; +} - - - } - - .header-row-right { +.header-row-right { background-color: var(--mainLight); - - - - } +} diff --git a/packages/react/src/index.css b/packages/react/src/index.css index 44d182670..ffd4f3a88 100644 --- a/packages/react/src/index.css +++ b/packages/react/src/index.css @@ -1,27 +1,27 @@ .entry { all: initial; margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } :root { - --mainDark: #1A1B22; - --mainLight: #1F2029; - --mainLightSecondary: #E0E5EB80; + --mainDark: #1a1b22; + --mainLight: #1f2029; + --mainLightSecondary: #e0e5eb80; --lightHighlight: #5e6164; --mainBorder: #5e6164; - --mainColorLight: #3173D5; - --mainColorLightSecondary: #3173D530; + --mainColorLight: #3173d5; + --mainColorLightSecondary: #3173d530; --btnColorLeftDisabled: #6d7f9c; --btnColorLeft: #879dc0; - --btnColorLeftSecondary: #9DB1D1; - --btnColorLeftSecondaryFocus: #9DB1D140; - } - - + --btnColorLeftSecondary: #9db1d1; + --btnColorLeftSecondaryFocus: #9db1d140; +} - .h-100 { - min-height: inherit; - } \ No newline at end of file +.h-100 { + min-height: inherit; +} diff --git a/packages/react/src/logos/DarkLogo.css b/packages/react/src/logos/DarkLogo.css index 0ff9eddab..39b30b608 100644 --- a/packages/react/src/logos/DarkLogo.css +++ b/packages/react/src/logos/DarkLogo.css @@ -1,3 +1,3 @@ .logo-link { text-decoration: none; -} \ No newline at end of file +} diff --git a/packages/react/src/sign-in/SignIn.css b/packages/react/src/sign-in/SignIn.css index f72f25479..467a1aa0b 100644 --- a/packages/react/src/sign-in/SignIn.css +++ b/packages/react/src/sign-in/SignIn.css @@ -1,85 +1,78 @@ .help-text { - - color: #6c757d; + color: #6c757d; } .sign-in-main { - overflow: overlay; + overflow: overlay; } - .sign-in-help { - margin-top: 0.2rem !important; + margin-top: 0.2rem !important; } .sign-in-help-1 { margin-top: 2.5rem !important; - } +} .sign-in-help-2 { margin-top: 2.2rem !important; } .store-token { - margin-top: 0.5rem; + margin-top: 0.5rem; } .explanation { - font-size: 0.8rem; + font-size: 0.8rem; } .sign-in { - padding-left: 1rem; + padding-left: 1rem; } .storage-item { - background-color: var(--mainDark); - color: #DCE1E7 !important; - border-style: none; - border-radius: 8px !important; - margin-bottom: 0.5rem; + background-color: var(--mainDark); + color: #dce1e7 !important; + border-style: none; + border-radius: 8px !important; + margin-bottom: 0.5rem; } .storage-item:focus { - background-color: var(--btnColorLeft); + background-color: var(--btnColorLeft); - color: #fff !important; + color: #fff !important; } .storage-item-selected { - background-color: var(--btnColorLeft); + background-color: var(--btnColorLeft); - color: #fff !important; + color: #fff !important; } - .storage-item:hover { - background-color: var(--btnColorLeftSecondary); - - color: #fff !important; + background-color: var(--btnColorLeftSecondary); + color: #fff !important; } .sign-in-btn { - width: 350px !important; - height: 75px; - font-size: 1.7rem; - border-color: #6773DD !important; - color: #ffffff !important; - border-radius: 15px; + width: 350px !important; + height: 75px; + font-size: 1.7rem; + border-color: #6773dd !important; + color: #ffffff !important; + border-radius: 15px; } .sign-in-btn:hover { - - background-color: #6773DD !important; - + background-color: #6773dd !important; } .sign-in-logo { - padding-top: 6rem; - padding-bottom: 6rem; + padding-top: 6rem; + padding-bottom: 6rem; } .miniSignInBtn { - width: 10rem !important; + width: 10rem !important; } - diff --git a/packages/react/src/start/Start.css b/packages/react/src/start/Start.css index f2aafe0cf..d952a5ad4 100644 --- a/packages/react/src/start/Start.css +++ b/packages/react/src/start/Start.css @@ -1,6 +1,6 @@ .start-btn { background-color: #00000000; - color: #DBDFE5 !important; + color: #dbdfe5 !important; font-size: 30px; border: none !important; height: 60px !important; @@ -11,4 +11,3 @@ right: 40px; z-index: 1001; } - diff --git a/packages/react/src/storage/Storage.css b/packages/react/src/storage/Storage.css index 13bfc1ff5..6de7e76ca 100644 --- a/packages/react/src/storage/Storage.css +++ b/packages/react/src/storage/Storage.css @@ -1,21 +1,14 @@ - .storage-view-container { - background-color: var(--mainDark);; - - + background-color: var(--mainDark); } .storage-view { - padding: 10px !important; - - } .sync-state { font-size: 0.8rem; } - /* .storage-view-bottom { @@ -65,4 +58,4 @@ .not-synced { background-color: #dc3545; } -*/ \ No newline at end of file +*/ diff --git a/packages/react/src/ui-shared/Help.css b/packages/react/src/ui-shared/Help.css index a564932af..4db7fa12b 100644 --- a/packages/react/src/ui-shared/Help.css +++ b/packages/react/src/ui-shared/Help.css @@ -1,3 +1,3 @@ .help-row { filter: opacity(0.4); -} \ No newline at end of file +} diff --git a/packages/react/src/ui-shared/StateButton.css b/packages/react/src/ui-shared/StateButton.css index 00e6484d6..3c80c8277 100644 --- a/packages/react/src/ui-shared/StateButton.css +++ b/packages/react/src/ui-shared/StateButton.css @@ -3,4 +3,4 @@ border-style: solid; border-radius: 0.5rem; background-color: transparent; -} \ No newline at end of file +} diff --git a/packages/react/src/user-info/UserInfo.css b/packages/react/src/user-info/UserInfo.css index 5b5adec02..e4938d50a 100644 --- a/packages/react/src/user-info/UserInfo.css +++ b/packages/react/src/user-info/UserInfo.css @@ -6,7 +6,6 @@ .address-info { font-family: monospace; font-size: 1rem; - } .extended-space { @@ -16,12 +15,10 @@ .info-value { color: #ffffff; word-break: break-all; - } .info-value div a { color: #ffffff; - } .user-info-row { @@ -44,4 +41,4 @@ border-radius: 10px; font-size: 1.2rem; width: 300px; -} \ No newline at end of file +} diff --git a/packages/web/README.md b/packages/web/README.md index da2d0d410..3943ab9a4 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -1,4 +1,5 @@ # dm3-web + ## Getting Started ### Build @@ -7,16 +8,16 @@ cd ../../ && yarn build ``` - ### Usage +yarn: -yarn: ``` yarn start ``` -npm: +npm: + ``` yarn start -``` \ No newline at end of file +``` diff --git a/packages/web/public/index.html b/packages/web/public/index.html index ba3e8c43c..c1455225a 100644 --- a/packages/web/public/index.html +++ b/packages/web/public/index.html @@ -1,22 +1,22 @@ - - - - - - - - - - - - dm3 - - - -
- - + --> diff --git a/packages/web/src/index.css b/packages/web/src/index.css index 24a9eac39..c7150e6c9 100644 --- a/packages/web/src/index.css +++ b/packages/web/src/index.css @@ -1,60 +1,53 @@ body { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - background-color: #222222; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: #222222; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', - monospace; + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; } .filler { - backdrop-filter: blur(1px) !important; - background-color: #0D0D0D !important; - cursor: initial !important; + backdrop-filter: blur(1px) !important; + background-color: #0d0d0d !important; + cursor: initial !important; } - .container { - margin-top: 2rem !important; + margin-top: 2rem !important; } .main-content-row { - - border-style: none !important; - /* box-shadow: 0px 0px 2px #999; */ - + border-style: none !important; + /* box-shadow: 0px 0px 2px #999; */ } .logo { - position: absolute; - filter: brightness(100%) ; - top: 2rem; - left: 2rem; - z-index: 9999; - cursor: pointer; - + position: absolute; + filter: brightness(100%); + top: 2rem; + left: 2rem; + z-index: 9999; + cursor: pointer; } .logo a { - text-decoration: none; - + text-decoration: none; } .logo:hover { - - filter: brightness(110%) ; - + filter: brightness(110%); } .main-content-row { - filter: drop-shadow(0px 0px 5px rgb(0 0 0 / 0.4)) + filter: drop-shadow(0px 0px 5px rgb(0 0 0 / 0.4)); } .legal { - font-size: 0.8rem !important; - text-decoration: none; -} \ No newline at end of file + font-size: 0.8rem !important; + text-decoration: none; +} diff --git a/test-action/action.yml b/test-action/action.yml index d2bd9040d..d7a7af9ef 100644 --- a/test-action/action.yml +++ b/test-action/action.yml @@ -2,42 +2,42 @@ name: 'test action' description: 'Yarn workspace test action' inputs: - workspace-name: - description: 'The name of the workspace that should be tested' - required: true - package-pat: - required: true + workspace-name: + description: 'The name of the workspace that should be tested' + required: true + package-pat: + required: true runs-on: ubuntu-latest runs: - using: "composite" - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v3 - with: - registry-url: 'https://npm.pkg.github.com' - node-version: 18.0.0 - cache: 'yarn' - - uses: actions/download-artifact@master - with: - name: lib-builds - path: packages/lib - - uses: actions/download-artifact@master - with: - name: lib-schema - path: packages/lib - - uses: actions/download-artifact@master - with: - name: lib-backend-builds - path: packages/lib - - name: Install - shell: bash - run: yarn install - env: - NODE_AUTH_TOKEN: ${{ inputs.package-pat }} + using: 'composite' + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v3 + with: + registry-url: 'https://npm.pkg.github.com' + node-version: 18.0.0 + cache: 'yarn' + - uses: actions/download-artifact@master + with: + name: lib-builds + path: packages/lib + - uses: actions/download-artifact@master + with: + name: lib-schema + path: packages/lib + - uses: actions/download-artifact@master + with: + name: lib-backend-builds + path: packages/lib + - name: Install + shell: bash + run: yarn install + env: + NODE_AUTH_TOKEN: ${{ inputs.package-pat }} - - name: Unit Tests - shell: bash - run: yarn workspace ${{ inputs.workspace-name }} test - env: - REACT_APP_ADDR_ENS_SUBDOMAIN: 'test.dm3.eth' - DATABASE_URL: 'postgresql://postgres:example@127.0.0.1:5432' \ No newline at end of file + - name: Unit Tests + shell: bash + run: yarn workspace ${{ inputs.workspace-name }} test + env: + REACT_APP_ADDR_ENS_SUBDOMAIN: 'test.dm3.eth' + DATABASE_URL: 'postgresql://postgres:example@127.0.0.1:5432'