Skip to content

Commit

Permalink
🚀 Release 1.8.0 (#4572)
Browse files Browse the repository at this point in the history
  • Loading branch information
thesan authored Oct 13, 2023
2 parents fd518a3 + 16b2eb1 commit b143966
Show file tree
Hide file tree
Showing 50 changed files with 1,460 additions and 195 deletions.
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.8.0] - 2023-10-13

### Added
- Custom network settings.
- Opening creation modal.
- Grace period or exact execution block on proposal preview pages.

### Fixed
- Markdown list color and indentation.
- Speed-up forum category pages.
- Past council budget statistics.

## [1.7.0] - 2023-09-15

### Added
Expand Down Expand Up @@ -193,7 +205,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/v1.7.0...HEAD
[unreleased]: https://github.com/Joystream/pioneer/compare/v1.8.0...HEAD
[1.8.0]: https://github.com/Joystream/pioneer/compare/v1.7.0...v1.8.0
[1.7.0]: https://github.com/Joystream/pioneer/compare/v1.6.0...v1.7.0
[1.6.0]: https://github.com/Joystream/pioneer/compare/v1.5.1...v1.6.0
[1.5.1]: https://github.com/Joystream/pioneer/compare/v1.5.0...v1.5.1
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@joystream/pioneer",
"version": "1.7.0",
"version": "1.8.0",
"license": "GPL-3.0-only",
"scripts": {
"build": "node --max_old_space_size=4096 ./build.js",
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/app/GlobalModals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { VoteRationale } from '@/proposals/modals/VoteRationale/VoteRationale'
import { ApplicationDetailsModal, ApplicationDetailsModalCall } from '@/working-groups/modals/ApplicationDetailsModal'
import { ApplyForRoleModal, ApplyForRoleModalCall } from '@/working-groups/modals/ApplyForRoleModal'
import { ChangeAccountModal, ChangeAccountModalCall } from '@/working-groups/modals/ChangeAccountModal'
import { CreateOpeningModal, CreateOpeningModalCall } from '@/working-groups/modals/CreateOpening'
import {
IncreaseWorkerStakeModal,
IncreaseWorkerStakeModalCall,
Expand All @@ -87,6 +88,7 @@ export type ModalNames =
| ModalName<MoveFundsModalCall>
| ModalName<AddNewProposalModalCall>
| ModalName<VoteRationaleModalCall>
| ModalName<CreateOpeningModalCall>
| ModalName<CreateThreadModalCall>
| ModalName<DeleteThreadModalCall>
| ModalName<DeletePostModalCall>
Expand Down Expand Up @@ -131,6 +133,7 @@ const modals: Record<ModalNames, ReactElement> = {
ApplyForRoleModal: <ApplyForRoleModal />,
ApplicationDetails: <ApplicationDetailsModal />,
SwitchMember: <SwitchMemberModal />,
CreateOpening: <CreateOpeningModal />,
LeaveRole: <LeaveRoleModal />,
ChangeAccountModal: <ChangeAccountModal />,
MoveFundsModal: <MoveFundsModal />,
Expand Down Expand Up @@ -193,6 +196,7 @@ const GUEST_ACCESSIBLE_MODALS: ModalNames[] = [
export const MODAL_WITH_CLOSE_CONFIRMATION: ModalNames[] = [
'AddNewProposalModal',
'AnnounceCandidateModal',
'CreateOpening',
'CreatePost',
'CreateThreadModal',
'ApplyForRoleModal',
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/app/config/network.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type NetworkType = 'mainnet' | 'local' | 'testnet' | 'auto-conf' | 'local-mocks'
export type NetworkType = 'mainnet' | 'local' | 'testnet' | 'auto-conf' | 'local-mocks' | 'custom'

export interface NetworkEndpoints {
nodeRpcEndpoint: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Args = {
isInDiscussionWhitelist: boolean
type: ProposalDetailsType
constitutionality: number
exactExecutionBlock: number
vote1: VoteArg
vote2: VoteArg
vote3: VoteArg
Expand Down Expand Up @@ -73,6 +74,7 @@ export default {
isDiscussionOpen: true,
type: 'SignalProposalDetails',
constitutionality: 1,
exactExecutionBlock: 0,
vote1: 'None',
vote2: 'None',
vote3: 'None',
Expand Down Expand Up @@ -155,6 +157,7 @@ export default {
status,
type: args.type,
creator: args.isProposer ? alice : bob,
exactExecutionBlock: args.exactExecutionBlock || null,

discussionThread: {
posts: proposalDiscussionPosts,
Expand Down
7 changes: 6 additions & 1 deletion packages/ui/src/app/pages/Proposals/ProposalPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,12 @@ export const ProposalPreview = () => {
{/* Proposal-specific dashboard */}
<h3>{camelCaseToText(proposal.type)}</h3>

<ProposalDetails proposalDetails={proposal.details} />
<ProposalDetails
proposalDetails={proposal.details}
gracePeriod={constants?.gracePeriod}
exactExecutionBlock={proposal.exactExecutionBlock}
createdInBlock={proposal.createdInBlock}
/>

<RationalePreview rationale={proposal.rationale} />
<ProposalDiscussions thread={proposal.discussionThread} proposalId={id} />
Expand Down
191 changes: 188 additions & 3 deletions packages/ui/src/app/pages/Settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import { useForm, FormProvider } from 'react-hook-form'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

import { useApi } from '@/api/hooks/useApi'
import { PageHeaderWrapper, PageLayout } from '@/app/components/PageLayout'
import { NetworkType } from '@/app/config'
import { NetworkType, NetworkEndpoints } from '@/app/config'
import { ButtonPrimary } from '@/common/components/buttons'
import { InputComponent, InputText } from '@/common/components/forms'
import { WarnedIcon } from '@/common/components/icons/activities'
// import { LanguageSelect } from '@/common/components/LanguageSelect'
import { Loading } from '@/common/components/Loading'
import NetworkInfo from '@/common/components/NetworkInfo/NetworkInfo'
import { ColumnGapBlock, MainPanel, RowGapBlock } from '@/common/components/page/PageContent'
import { PageTitle } from '@/common/components/page/PageTitle'
import { PolkadotAppInfo } from '@/common/components/PolkadotAppInfo'
import { SimpleSelect } from '@/common/components/selects'
import { SettingsInformation } from '@/common/components/SettingsInformation'
import { SettingsInformation, SettingsWarningInformation } from '@/common/components/SettingsInformation'
import { Tabs } from '@/common/components/Tabs'
import { TextMedium } from '@/common/components/typography'
import { useLocalStorage } from '@/common/hooks/useLocalStorage'
import { useNetwork } from '@/common/hooks/useNetwork'
import { useNetworkEndpoints } from '@/common/hooks/useNetworkEndpoints'
import { useObservable } from '@/common/hooks/useObservable'
Expand All @@ -36,13 +40,118 @@ export const Settings = () => {
{ title: t('network'), active: currentTab === 'SETTINGS', onClick: () => setCurrentTab('SETTINGS') },
//{ title: t('language'), active: currentTab === 'LANGUAGE', onClick: () => setCurrentTab('LANGUAGE') },
]

const form = useForm()
const [customFaucetEndpoint, customRpcEndpoint, customQueryEndpoint] = form.watch([
'settings.customFaucetEndpoint',
'settings.customRpcEndpoint',
'settings.customQueryEndpoint',
])
const [, storeCustomEndpoints] = useLocalStorage<NetworkEndpoints>('custom_endpoint')
const [isValidFaucetEndpoint, setIsValidFaucetEndpoint] = useState(true)
const [isValidRpcEndpoint, setIsValidRpcEndpoint] = useState(true)
const [isValidQueryEndpoint, setIsValidQueryEndpoint] = useState(true)
const [customSaveStatus, setCustomSaveStatus] = useState<'Init' | 'Saving' | 'Done'>('Init')

const switchNetwork = (network: NetworkType | null) => {
if (network) {
setNetwork(network)
window.location.reload()
}
}

useEffect(() => {
if (network === 'custom') {
form.setValue('settings.customRpcEndpoint', endpoints.nodeRpcEndpoint)
form.setValue('settings.customQueryEndpoint', endpoints.queryNodeEndpoint)
form.setValue('settings.customFaucetEndpoint', endpoints.membershipFaucetEndpoint)
}
}, [network, endpoints])

const checkFaucetEndpoint = async () => {
// check faucet endpoint
try {
const faucetStatusEndpoint = customFaucetEndpoint.replace(new RegExp('register$'), 'status')
const response = await fetch(faucetStatusEndpoint)
const succeeded = response.status < 400
setIsValidFaucetEndpoint(succeeded)
return succeeded
} catch {
setIsValidFaucetEndpoint(false)
return false
}
}

const checkRpcEndpoint = async () => {
// check RPC endpoint
try {
return await new Promise<boolean>((resolve) => {
const ws = new WebSocket(customRpcEndpoint)
const willResolveTo = (succeeded: boolean, timeout?: any) => () => {
if (timeout) clearTimeout(timeout)

ws.close()
setIsValidRpcEndpoint(succeeded)
resolve(succeeded)
}

const timeout = setTimeout(willResolveTo(false), 3000)
ws.onopen = willResolveTo(true, timeout)
ws.onerror = willResolveTo(false, timeout)
})
} catch {
setIsValidRpcEndpoint(false)
return false
}
}

const checkQueryEndpoint = async () => {
// check GraphQL endpoint
try {
const response = await fetch(customQueryEndpoint + '?query=%7B__typename%7D')
const succeeded = response.status < 400 && (await response.json()).data['__typename'] === 'Query'
setIsValidQueryEndpoint(succeeded)
return succeeded
} catch {
setIsValidQueryEndpoint(false)
return false
}
}

useEffect(() => {
if (
isValidFaucetEndpoint === true &&
isValidRpcEndpoint === true &&
isValidQueryEndpoint === true &&
customSaveStatus === 'Done'
) {
storeCustomEndpoints({
nodeRpcEndpoint: customRpcEndpoint,
queryNodeEndpoint: customQueryEndpoint,
membershipFaucetEndpoint: customFaucetEndpoint,
queryNodeEndpointSubscription: customQueryEndpoint.replace(/^http?/, 'ws'),
configEndpoint: undefined,
})
window.location.reload()
}
}, [isValidFaucetEndpoint, isValidRpcEndpoint, isValidQueryEndpoint, customSaveStatus])

const saveSettings = async () => {
if (
/^(http|https):\/\//i.test(customFaucetEndpoint) === false ||
/^(ws|wss):\/\//i.test(customRpcEndpoint) === false ||
/^(http|https):\/\//i.test(customQueryEndpoint) === false
) {
return
}

setCustomSaveStatus('Saving')

await Promise.all([checkFaucetEndpoint(), checkRpcEndpoint(), checkQueryEndpoint()])

setCustomSaveStatus('Done')
}

return (
<Container>
<PageLayout
Expand All @@ -65,6 +174,82 @@ export const Settings = () => {
onChange={switchNetwork}
selectSize="l"
/>
{network == 'custom' && (
<FormProvider {...form}>
<SettingsWarningInformation icon={<WarnedIcon />} title="Attention">
<ColumnGapBlock gap={5}>
<TextMedium lighter>
Please beware that using 3rd party QN may result in fraud. Only use trusted providers
</TextMedium>
</ColumnGapBlock>
</SettingsWarningInformation>
<InputComponent
label={t('customFaucet')}
validation={
/^(http|https):\/\//i.test(customFaucetEndpoint) && isValidFaucetEndpoint
? undefined
: 'invalid'
}
message={
/^(http|https):\/\//i.test(customFaucetEndpoint)
? isValidFaucetEndpoint
? undefined
: 'Connection Error'
: 'This Faucet endpoint must start with http or https'
}
>
<InputText
id="field-custom-faucet"
placeholder="Paste faucet URL address"
name="settings.customFaucetEndpoint"
/>
</InputComponent>
<InputComponent
label={t('customRPCNode')}
validation={
/^(ws|wss):\/\//i.test(customRpcEndpoint) && isValidRpcEndpoint ? undefined : 'invalid'
}
message={
/^(ws|wss):\/\//i.test(customRpcEndpoint)
? isValidRpcEndpoint
? undefined
: 'Connection Error. Sometimes it fails due to network speed. Please try to check once more'
: 'This RPC endpoint must start with ws or wss'
}
>
<InputText
id="field-custom-rpcnode"
placeholder="Paste RPC node"
name="settings.customRpcEndpoint"
/>
</InputComponent>
<InputComponent
label={t('customQueryNode')}
validation={
/^(http|https):\/\//i.test(customQueryEndpoint) && isValidQueryEndpoint
? undefined
: 'invalid'
}
message={
/^(http|https):\/\//i.test(customQueryEndpoint)
? isValidQueryEndpoint
? undefined
: 'Connection Error'
: 'This Query endpoint must start with http or https'
}
>
<InputText
id="field-custom-querynode"
placeholder="Paste Query node"
name="settings.customQueryEndpoint"
/>
</InputComponent>
<ButtonPrimary onClick={saveSettings} size="medium">
Save settings
{customSaveStatus === 'Saving' && <Loading />}
</ButtonPrimary>
</FormProvider>
)}
{endpoints?.configEndpoint && (
<ButtonPrimary
onClick={() => fetchNetworkEndpoints(endpoints.configEndpoint as string)}
Expand Down
Loading

1 comment on commit b143966

@vercel
Copy link

@vercel vercel bot commented on b143966 Oct 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

dao – ./

dao.joystream.org
pioneerapp.xyz
dao-git-main-joystream.vercel.app
dao-joystream.vercel.app

Please sign in to comment.