Skip to content

Commit

Permalink
🚀 Release 3.7.2 (#4863)
Browse files Browse the repository at this point in the history
  • Loading branch information
thesan committed Aug 10, 2024
2 parents 76697cd + f1718d3 commit e59cf1f
Show file tree
Hide file tree
Showing 18 changed files with 394 additions and 89 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.7.2] - 2024-08-10

### Fixed
- Show onboarding flow when a membership is required and no wallet is connected.

## [3.7.1] - 2024-07-07

### Fixed
Expand Down Expand Up @@ -407,7 +412,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.1.1] - 2022-12-02

[unreleased]: https://github.com/Joystream/pioneer/compare/v3.7.1...HEAD
[unreleased]: https://github.com/Joystream/pioneer/compare/v3.7.2...HEAD
[3.8.0]: https://github.com/Joystream/pioneer/compare/v3.7.1...v3.7.2
[3.7.1]: https://github.com/Joystream/pioneer/compare/v3.7.0...v3.7.1
[3.7.0]: https://github.com/Joystream/pioneer/compare/v3.6.0...v3.7.0
[3.6.0]: https://github.com/Joystream/pioneer/compare/v3.5.2...v3.6.0
Expand Down
5 changes: 5 additions & 0 deletions packages/ui/.env.example
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# TESTNET Endpoints
REACT_APP_TESTNET_NODE_SOCKET=wss://rpc.joystream.org:9944
REACT_APP_TESTNET_NODE_HTTP_RPC=https://rpc.joystream.org
REACT_APP_TESTNET_QUERY_NODE=https://query.joystream.org/graphql
REACT_APP_TESTNET_QUERY_NODE_SOCKET=wss://query.joystream.org/graphql
REACT_APP_TESTNET_MEMBERSHIP_FAUCET_URL=https://faucet.joystream.org/member-faucet/register
REACT_APP_TESTNET_BACKEND=http://localhost:3000

# MAINNET Endpoints
REACT_APP_MAINNET_NODE_SOCKET=wss://rpc.joystream.org:9944
REACT_APP_MAINNET_NODE_HTTP_RPC=https://rpc.joystream.org
REACT_APP_MAINNET_QUERY_NODE=https://query.joystream.org/graphql
REACT_APP_MAINNET_QUERY_NODE_SOCKET=wss://query.joystream.org/graphql
REACT_APP_MAINNET_MEMBERSHIP_FAUCET_URL=https://faucet.joystream.org/member-faucet/register
Expand All @@ -23,6 +25,9 @@ REACT_APP_AVATAR_UPLOAD_URL=https://atlas-services.joystream.org/avatars
# WalletConnect project id
REACT_APP_WALLET_CONNECT_PROJECT_ID="2ea3f3ghubh32b8ie2f2"

# Metamask snap id (`local:http://localhost:8081` for local development)
REACT_APP_METAMASK_SNAP_ID="npm:@chainsafe/polkadot-snap"

# Image reporting

## Manual blacklist:
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@joystream/pioneer",
"version": "3.7.1",
"version": "3.7.2",
"license": "GPL-3.0-only",
"scripts": {
"build": "node --max_old_space_size=4096 ./build.js",
Expand All @@ -25,6 +25,7 @@
},
"dependencies": {
"@apollo/client": "3.5.7",
"@chainsafe/metamask-polkadot-adapter": "^0.6.0",
"@hcaptcha/react-hcaptcha": "^1.4.4",
"@joystream/js": "1.10.0",
"@joystream/markdown-editor": "^0.1.0",
Expand Down Expand Up @@ -97,6 +98,7 @@
"@babel/preset-env": "7",
"@babel/preset-react": "7",
"@babel/preset-typescript": "7",
"@chainsafe/metamask-polkadot-types": "^0.6.0",
"@graphql-codegen/cli": "^2.2.0",
"@graphql-codegen/near-operation-file-preset": "^2.1.4",
"@graphql-codegen/typescript": "^2.2.2",
Expand Down
83 changes: 83 additions & 0 deletions packages/ui/src/accounts/model/metamask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { enablePolkadotSnap } from '@chainsafe/metamask-polkadot-adapter'
import { MetamaskSnapApi } from '@chainsafe/metamask-polkadot-adapter/build/types'
import { SnapNetworks } from '@chainsafe/metamask-polkadot-types'
import { Signer } from '@polkadot/types/types'
import { BaseDotsamaWallet, SubscriptionFn, WalletAccount } from 'injectweb3-connect'

import MetamaskLogo from '@/app/assets/images/logos/Metamask.svg'
import { CHAIN_PROPERTIES } from '@/app/constants/chain'

const networkName = 'joystream' as SnapNetworks
const addressPrefix = CHAIN_PROPERTIES.ss58Format
const unit = { symbol: CHAIN_PROPERTIES.tokenSymbol[0], decimals: CHAIN_PROPERTIES.tokenDecimals[0] }

export class Metamask extends BaseDotsamaWallet {
protected _snapId: string
protected _httpRpcUrl: string
protected _snapApi: MetamaskSnapApi | undefined
protected _accounts: WalletAccount[] | undefined
protected _txId = 0

constructor(snapId: string, httpRpcUrl: string) {
super({
extensionName: 'Metamask',
title: 'Metamask',
logo: { src: MetamaskLogo, alt: 'Metamask Logo' },
})

this._snapId = snapId
this._httpRpcUrl = httpRpcUrl
}

public enable = async (): Promise<void> => {
const snap = await enablePolkadotSnap(
{ networkName, wsRpcUrl: this._httpRpcUrl, addressPrefix, unit },
this._snapId
)

this._snapApi = await snap.getMetamaskSnapApi()
const address = await this._snapApi.getAddress()
this._accounts = [
{
name: 'Metamask account',
address,
source: this.extensionName,
},
]

this._snapApi.signPayloadJSON
}

public getAccounts = async (): Promise<WalletAccount[]> => {
return this._accounts ?? []
}

public subscribeAccounts: (callback: SubscriptionFn) => Promise<() => void> = (callback) => {
callback(this._accounts ?? [])
return Promise.resolve(() => undefined)
}

public get signer(): Signer {
return {
signPayload: async (payload) => {
if (!this._snapApi) {
throw Error('Metamask was accessed before it was enabled')
}

const signature = (await this._snapApi.signPayloadJSON(payload)) as `0x${string}`

return { id: this._txId++, signature }
},

signRaw: async (raw) => {
if (!this._snapApi) {
throw Error('Metamask was accessed before it was enabled')
}

const signature = (await this._snapApi.signPayloadRaw(raw)) as `0x${string}`

return { id: this._txId++, signature }
},
}
}
}
55 changes: 41 additions & 14 deletions packages/ui/src/accounts/providers/accounts/useWallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { groupBy } from 'lodash'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Subject, firstValueFrom } from 'rxjs'

import { Metamask } from '@/accounts/model/metamask'
import { WalletConnect } from '@/accounts/model/walletConnect'
import { RecommendedWallets, RecommendedWalletsNames, asWallet } from '@/accounts/model/wallets'
import { useApi } from '@/api/hooks/useApi'
import { useLocalStorage } from '@/common/hooks/useLocalStorage'
import { useNetworkEndpoints } from '@/common/hooks/useNetworkEndpoints'

type WalletState = undefined | 'ENABLING' | 'READY' | 'APP_REJECTED'

Expand All @@ -21,6 +23,27 @@ const genesisHash$ = new Subject<string>()
const WalletDisconnection$ = new Subject<void>()

export const useWallets = (): UseWallets => {
const walletExtensions = useWalletExtensions()
const walletConnect = useWalletConnect(() => setWallet(undefined))
const metamask = useMetamask()

const allWallets: Wallet[] = useMemo(
() => [
...walletExtensions.installed,
...walletExtensions.unknown,
...metamask,
...walletConnect,
...walletExtensions.recommended,
],
[walletExtensions, walletConnect]
)

const { wallet, setWallet, walletState } = useSelectedWallet(allWallets)

return { allWallets, wallet, setWallet, walletState }
}

const useWalletExtensions = (): { installed: Wallet[]; recommended: Wallet[]; unknown: Wallet[] } => {
const [installedWalletsNames, setInstalledWalletsNames] = useState<string[]>([])

useEffect(() => {
Expand All @@ -43,40 +66,44 @@ export const useWallets = (): UseWallets => {
return () => clearInterval(intervalId)
}, [])

const walletExtensions = useMemo(() => {
return useMemo(() => {
const unknown = installedWalletsNames.filter((name) => !RecommendedWalletsNames.includes(name)).map(asWallet)
const { installed = [], recommended = [] } = groupBy(RecommendedWallets, (wallet) =>
installedWalletsNames.includes(wallet.extensionName) ? 'installed' : 'recommended'
)
return { installed, recommended, unknown }
}, [installedWalletsNames])
}

const useWalletConnect = (disconnect: () => void): WalletConnect[] => {
const { api } = useApi()
useEffect(() => {
if (api) genesisHash$.next(api.genesisHash.toHex())
}, [api?.isConnected])

const walletConnect = useMemo(() => {
const walletConnect: WalletConnect | undefined = useMemo(() => {
const wcProjectId: string | undefined = process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID
if (!wcProjectId) return

const genesisHash = firstValueFrom(genesisHash$)
return new WalletConnect(wcProjectId, genesisHash, WalletDisconnection$, () => setWallet(undefined))
return new WalletConnect(wcProjectId, genesisHash, WalletDisconnection$, disconnect)
}, [])

const allWallets = useMemo(
() => [
...walletExtensions.installed,
...walletExtensions.unknown,
...(walletConnect ? [walletConnect] : []),
...walletExtensions.recommended,
],
[walletExtensions, walletConnect]
)
return useMemo(() => (walletConnect ? [walletConnect] : []), [walletConnect])
}

const { wallet, setWallet, walletState } = useSelectedWallet(allWallets)
const useMetamask = (): Metamask[] => {
const [endpoints] = useNetworkEndpoints()

return { allWallets, wallet, setWallet, walletState }
const metamask: Metamask | undefined = useMemo(() => {
const snapId = process.env.REACT_APP_METAMASK_SNAP_ID
const isMetaMask = !!window.ethereum && '_metamask' in window.ethereum
if (!snapId || !isMetaMask) return

return new Metamask(snapId, endpoints.nodeHttpRpcEndpoint)
}, [endpoints.nodeHttpRpcEndpoint])

return useMemo(() => (metamask ? [metamask] : []), [metamask])
}

const useSelectedWallet = (allWallets: Wallet[]) => {
Expand Down
22 changes: 15 additions & 7 deletions packages/ui/src/app/GlobalModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { memo, ReactElement, useEffect, useMemo, useState } from 'react'
import ReactDOM from 'react-dom'
import styled from 'styled-components'

import { useMyAccounts } from '@/accounts/hooks/useMyAccounts'
import { ClaimVestingModalCall } from '@/accounts/modals/ClaimVestingModal'
import { ClaimVestingModal } from '@/accounts/modals/ClaimVestingModal/ClaimVestingModal'
import { MoveFundsModal, MoveFundsModalCall } from '@/accounts/modals/MoveFundsModal'
Expand Down Expand Up @@ -225,6 +226,7 @@ export const GlobalModals = () => {
const { active: activeMember } = useMyMemberships()
const { status } = useTransactionStatus()
const Modal = useMemo(() => (modal && modal in modals ? memo(() => modals[modal as ModalNames]) : null), [modal])
const { wallet } = useMyAccounts()

const [container, setContainer] = useState(document.body)
useEffect(() => {
Expand All @@ -235,13 +237,19 @@ export const GlobalModals = () => {
const potentialFallback = useGlobalModalHandler(currentModalMachine, hideModal)

if (modal && !GUEST_ACCESSIBLE_MODALS.includes(modal as ModalNames) && !activeMember) {
showModal<SwitchMemberModalCall>({
modal: 'SwitchMember',
data: {
originalModalName: modal as ModalNames,
originalModalData: modalData,
},
})
if (wallet) {
showModal<SwitchMemberModalCall>({
modal: 'SwitchMember',
data: {
originalModalName: modal as ModalNames,
originalModalData: modalData,
},
})
} else {
showModal({
modal: 'OnBoardingModal',
})
}
return null
}

Expand Down
43 changes: 43 additions & 0 deletions packages/ui/src/app/assets/images/logos/Metamask.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions packages/ui/src/app/config/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type NetworkType = 'mainnet' | 'local' | 'testnet' | 'auto-conf' | 'local

export interface NetworkEndpoints {
nodeRpcEndpoint: string
nodeHttpRpcEndpoint: string
queryNodeEndpoint: string
queryNodeEndpointSubscription: string
membershipFaucetEndpoint: string
Expand All @@ -10,6 +11,7 @@ export interface NetworkEndpoints {
}

const TESTNET_NODE_SOCKET = process.env.REACT_APP_TESTNET_NODE_SOCKET
const TESTNET_NODE_HTTP_RPC = process.env.REACT_APP_TESTNET_NODE_HTTP_RPC
const TESTNET_QUERY_NODE = process.env.REACT_APP_TESTNET_QUERY_NODE
const TESTNET_QUERY_NODE_SOCKET = process.env.REACT_APP_TESTNET_QUERY_NODE_SOCKET
const TESTNET_MEMBERSHIP_FAUCET_URL = process.env.REACT_APP_TESTNET_MEMBERSHIP_FAUCET_URL
Expand All @@ -19,6 +21,7 @@ export const IS_TESTNET_DEFINED =
TESTNET_NODE_SOCKET && TESTNET_QUERY_NODE && TESTNET_QUERY_NODE_SOCKET && TESTNET_MEMBERSHIP_FAUCET_URL

const MAINNET_NODE_SOCKET = process.env.REACT_APP_MAINNET_NODE_SOCKET
const MAINNET_NODE_HTTP_RPC = process.env.REACT_APP_MAINNET_NODE_HTTP_RPC
const MAINNET_QUERY_NODE = process.env.REACT_APP_MAINNET_QUERY_NODE
const MAINNET_QUERY_NODE_SOCKET = process.env.REACT_APP_MAINNET_QUERY_NODE_SOCKET
const MAINNET_MEMBERSHIP_FAUCET_URL = process.env.REACT_APP_MAINNET_MEMBERSHIP_FAUCET_URL
Expand Down Expand Up @@ -57,6 +60,13 @@ const NODE_RPC_ENDPOINT: PredefinedEndpoint = {
'local-mocks': 'ws://127.0.0.1:9944',
}

const NODE_HTTP_RPC_ENDPOINT: PredefinedEndpoint = {
mainnet: MAINNET_NODE_HTTP_RPC,
local: 'http://127.0.0.1:9933',
testnet: TESTNET_NODE_HTTP_RPC,
'local-mocks': 'http://127.0.0.1:9933',
}

const BACKEND_ENDPOINT: PredefinedEndpoint = {
mainnet: MAINNET_BACKEND,
local: 'http://localhost:3000',
Expand All @@ -66,6 +76,7 @@ const BACKEND_ENDPOINT: PredefinedEndpoint = {

export const pickEndpoints = (network: NetworkType): Partial<NetworkEndpoints> => ({
nodeRpcEndpoint: NODE_RPC_ENDPOINT[network],
nodeHttpRpcEndpoint: NODE_HTTP_RPC_ENDPOINT[network],
queryNodeEndpoint: QUERY_NODE_ENDPOINT[network],
queryNodeEndpointSubscription: QUERY_NODE_ENDPOINT_SUBSCRIPTION[network],
membershipFaucetEndpoint: MEMBERSHIP_FAUCET_ENDPOINT[network],
Expand Down
Loading

0 comments on commit e59cf1f

Please sign in to comment.