From a10223465bbd721ce1c5a8e24206172c3635834b Mon Sep 17 00:00:00 2001
From: Victor Emmanuel <33874323+vrrayz@users.noreply.github.com>
Date: Tue, 30 Jan 2024 12:33:19 +0100
Subject: [PATCH 1/3] Change openings reward display (#4745)
* change reward per blocks display
* update rewaeds display
---
.../ui/src/app/pages/WorkingGroups/UpcomingOpening.tsx | 8 ++------
.../src/app/pages/WorkingGroups/WorkingGroupsOpening.tsx | 8 ++------
.../components/OpeningsList/Opening/OpeningDetails.tsx | 7 +++----
.../components/OpeningsList/Opening/OpeningListItem.tsx | 7 +++----
packages/ui/src/working-groups/model/asWeeklyRewards.ts | 9 +++++++++
5 files changed, 19 insertions(+), 20 deletions(-)
create mode 100644 packages/ui/src/working-groups/model/asWeeklyRewards.ts
diff --git a/packages/ui/src/app/pages/WorkingGroups/UpcomingOpening.tsx b/packages/ui/src/app/pages/WorkingGroups/UpcomingOpening.tsx
index c0588c9bf6..48821af58a 100644
--- a/packages/ui/src/app/pages/WorkingGroups/UpcomingOpening.tsx
+++ b/packages/ui/src/app/pages/WorkingGroups/UpcomingOpening.tsx
@@ -17,13 +17,12 @@ import { NumericValueStat } from '@/common/components/statistics/NumericValueSta
import { TextSmall } from '@/common/components/typography'
import { ApplicationStatusWrapper } from '@/working-groups/components/ApplicationStatusWrapper'
import { OpeningIcon } from '@/working-groups/components/OpeningIcon'
-import { useRewardPeriod } from '@/working-groups/hooks/useRewardPeriod'
import { useUpcomingOpening } from '@/working-groups/hooks/useUpcomingOpening'
+import { asWeeklyRewards } from '@/working-groups/model/asWeeklyRewards'
export const UpcomingOpening = () => {
const { id } = useParams<{ id: string }>()
const { isLoading, opening } = useUpcomingOpening(id)
- const rewardPeriod = useRewardPeriod(opening?.groupId)
if (isLoading || !opening) {
return (
@@ -82,10 +81,7 @@ export const UpcomingOpening = () => {
value={opening.expectedEnding}
from={opening.expectedStart}
/>
-
+
{opening.hiringLimit ? (
) : (
diff --git a/packages/ui/src/app/pages/WorkingGroups/WorkingGroupsOpening.tsx b/packages/ui/src/app/pages/WorkingGroups/WorkingGroupsOpening.tsx
index 57e32ec0b4..80cff8fc40 100644
--- a/packages/ui/src/app/pages/WorkingGroups/WorkingGroupsOpening.tsx
+++ b/packages/ui/src/app/pages/WorkingGroups/WorkingGroupsOpening.tsx
@@ -35,8 +35,8 @@ import { ApplicationStatusWrapper } from '@/working-groups/components/Applicatio
import { OpeningIcon } from '@/working-groups/components/OpeningIcon'
import { MappedStatuses, OpeningStatuses, WorkingGroupsRoutes } from '@/working-groups/constants'
import { useOpening } from '@/working-groups/hooks/useOpening'
-import { useRewardPeriod } from '@/working-groups/hooks/useRewardPeriod'
import { ApplyForRoleModalCall } from '@/working-groups/modals/ApplyForRoleModal'
+import { asWeeklyRewards } from '@/working-groups/model/asWeeklyRewards'
import { urlParamToOpeningId } from '@/working-groups/model/workingGroupName'
import { WorkingGroupOpening as WorkingGroupOpeningType } from '@/working-groups/types'
@@ -62,7 +62,6 @@ export const WorkingGroupOpening = () => {
return activeApplications.find(({ id }) => id === activeMembership?.id)
}
}, [opening?.id, activeMembership?.id])
- const rewardPeriod = useRewardPeriod(opening?.groupId)
if (isLoading || !opening) {
return (
@@ -150,10 +149,7 @@ export const WorkingGroupOpening = () => {
-
+
{
const { showModal } = useModal()
- const rewardPeriod = useRewardPeriod(opening.groupId)
const groupName = groupNameToURLParam(nameMapping(opening.groupName))
const openingRoute = `/working-groups/openings/${groupName}-${opening.runtimeId}`
@@ -42,9 +41,9 @@ export const OpeningDetails = ({ opening, onClick, past }: OpeningListItemProps)
-
+
- Reward per {rewardPeriod?.toString()} blocks
+ Reward per week
diff --git a/packages/ui/src/working-groups/components/OpeningsList/Opening/OpeningListItem.tsx b/packages/ui/src/working-groups/components/OpeningsList/Opening/OpeningListItem.tsx
index 05272778f4..f1172ac654 100644
--- a/packages/ui/src/working-groups/components/OpeningsList/Opening/OpeningListItem.tsx
+++ b/packages/ui/src/working-groups/components/OpeningsList/Opening/OpeningListItem.tsx
@@ -15,7 +15,7 @@ import {
ToggleableItemWrap,
OpenItemSummaryColumn,
} from '@/working-groups/components/ToggleableItemStyledComponents'
-import { useRewardPeriod } from '@/working-groups/hooks/useRewardPeriod'
+import { asWeeklyRewards } from '@/working-groups/model/asWeeklyRewards'
import { WorkingGroupOpening } from '@/working-groups/types'
export type OpeningListItemProps = {
@@ -25,7 +25,6 @@ export type OpeningListItemProps = {
}
export const OpeningListItem = ({ opening, past, onClick }: OpeningListItemProps) => {
- const rewardPeriod = useRewardPeriod(opening.groupId)
const hiringTarget = opening.hiring.limit || 1
return (
@@ -44,9 +43,9 @@ export const OpeningListItem = ({ opening, past, onClick }: OpeningListItemProps
-
+
- Reward per {rewardPeriod?.toString()} blocks.
+ Reward per week.
{opening.applicants}
diff --git a/packages/ui/src/working-groups/model/asWeeklyRewards.ts b/packages/ui/src/working-groups/model/asWeeklyRewards.ts
new file mode 100644
index 0000000000..bd9cb6ddb6
--- /dev/null
+++ b/packages/ui/src/working-groups/model/asWeeklyRewards.ts
@@ -0,0 +1,9 @@
+import BN from 'bn.js'
+
+import { A_WEEK } from '@/common/constants'
+import { MILLISECONDS_PER_BLOCK } from '@/common/model/formatters'
+
+export const asWeeklyRewards = (rewardPerBlock: BN) => {
+ const BLOCKS_PER_WEEK = A_WEEK / MILLISECONDS_PER_BLOCK
+ return rewardPerBlock.muln(BLOCKS_PER_WEEK)
+}
From 3c166ec350530b58df15c8bc6fbeadc600734074 Mon Sep 17 00:00:00 2001
From: Victor Emmanuel <33874323+vrrayz@users.noreply.github.com>
Date: Tue, 30 Jan 2024 16:54:49 +0100
Subject: [PATCH 2/3] Cancel proposal feature (#4750)
* cancel proposal feature
* cancel proposal story
* Update packages/ui/src/app/pages/Proposals/ProposalPreview.tsx
Co-authored-by: Theophile Sandoz
---------
Co-authored-by: Theophile Sandoz
---
packages/ui/src/app/GlobalModals.tsx | 3 +
.../Proposals/ProposalPreview.stories.tsx | 37 ++++++++++
.../app/pages/Proposals/ProposalPreview.tsx | 6 ++
.../components/CancelProposalButton.tsx | 27 +++++++
.../CancelProposal/CancelProposalModal.tsx | 70 +++++++++++++++++++
.../proposals/modals/CancelProposal/index.ts | 2 +
.../proposals/modals/CancelProposal/types.ts | 4 ++
7 files changed, 149 insertions(+)
create mode 100644 packages/ui/src/proposals/components/CancelProposalButton.tsx
create mode 100644 packages/ui/src/proposals/modals/CancelProposal/CancelProposalModal.tsx
create mode 100644 packages/ui/src/proposals/modals/CancelProposal/index.ts
create mode 100644 packages/ui/src/proposals/modals/CancelProposal/types.ts
diff --git a/packages/ui/src/app/GlobalModals.tsx b/packages/ui/src/app/GlobalModals.tsx
index 8b86bbf08d..f3bfdc6a4d 100644
--- a/packages/ui/src/app/GlobalModals.tsx
+++ b/packages/ui/src/app/GlobalModals.tsx
@@ -67,6 +67,7 @@ import { SwitchMemberModal, SwitchMemberModalCall } from '@/memberships/modals/S
import { TransferInviteModal, TransferInvitesModalCall } from '@/memberships/modals/TransferInviteModal'
import { UpdateMembershipModal, UpdateMembershipModalCall } from '@/memberships/modals/UpdateMembershipModal'
import { AddNewProposalModal, AddNewProposalModalCall } from '@/proposals/modals/AddNewProposal'
+import { CancelProposalModal, CancelProposalModalCall } from '@/proposals/modals/CancelProposal'
import { VoteForProposalModal, VoteForProposalModalCall } from '@/proposals/modals/VoteForProposal'
import { VoteRationaleModalCall } from '@/proposals/modals/VoteRationale/types'
import { VoteRationale } from '@/proposals/modals/VoteRationale/VoteRationale'
@@ -133,6 +134,7 @@ export type ModalNames =
| ModalName
| ModalName
| ModalName
+ | ModalName
const modals: Record = {
Member: ,
@@ -186,6 +188,7 @@ const modals: Record = {
EmailSubscriptionModal: ,
EmailConfirmationModal: ,
NominatingRedirect: ,
+ CancelProposalModal: ,
}
const GUEST_ACCESSIBLE_MODALS: ModalNames[] = [
diff --git a/packages/ui/src/app/pages/Proposals/ProposalPreview.stories.tsx b/packages/ui/src/app/pages/Proposals/ProposalPreview.stories.tsx
index e8244641a6..0ba815ce41 100644
--- a/packages/ui/src/app/pages/Proposals/ProposalPreview.stories.tsx
+++ b/packages/ui/src/app/pages/Proposals/ProposalPreview.stories.tsx
@@ -51,6 +51,7 @@ type Args = {
vote2: VoteArg
vote3: VoteArg
onVote: jest.Mock
+ onCancel: jest.Mock
}
type Story = StoryObj>
@@ -65,6 +66,7 @@ export default {
vote2: { control: { type: 'inline-radio' }, options: voteArgs },
vote3: { control: { type: 'inline-radio' }, options: voteArgs },
onVote: { action: 'ProposalsEngine.Voted' },
+ onCancel: { action: 'ProposalsEngine.Cancelled' },
},
args: {
@@ -142,6 +144,11 @@ export default {
onSend: args.onVote,
failure: parameters.txFailure,
},
+ cancelProposal: {
+ event: 'Cancelled',
+ onSend: args.onCancel,
+ failure: parameters.txFailure,
+ },
},
},
},
@@ -608,3 +615,33 @@ export const TestVoteTxFailure: Story = {
expect(await modal.findByText('Some error message'))
},
}
+
+export const TestCancelProposalHappy: Story = {
+ args: { type: 'SignalProposalDetails', isCouncilMember: false, isProposer: true },
+
+ name: 'Test CancelProposal Happy',
+
+ play: async ({ canvasElement, step, args: { onCancel } }) => {
+ const activeMember = member('alice')
+
+ const screen = within(canvasElement)
+ const modal = withinModal(canvasElement)
+
+ await step('Cancel', async () => {
+ await userEvent.click(screen.getByText('Cancel Proposal'))
+
+ await step('Sign', async () => {
+ expect(await modal.findByText('Authorize transaction'))
+ expect(modal.getByText('You intend to cancel your proposal.'))
+
+ await userEvent.click(modal.getByText(/^Sign And Cancel Proposal/))
+ })
+
+ await step('Confirm', async () => {
+ expect(await modal.findByText('Your propsal has been cancelled.'))
+
+ expect(onCancel).toHaveBeenLastCalledWith(activeMember.id, PROPOSAL_DATA.id)
+ })
+ })
+ },
+}
diff --git a/packages/ui/src/app/pages/Proposals/ProposalPreview.tsx b/packages/ui/src/app/pages/Proposals/ProposalPreview.tsx
index d2714fe570..3f084c52cc 100644
--- a/packages/ui/src/app/pages/Proposals/ProposalPreview.tsx
+++ b/packages/ui/src/app/pages/Proposals/ProposalPreview.tsx
@@ -23,6 +23,7 @@ import { getUrl } from '@/common/utils/getUrl'
import { useElectedCouncil } from '@/council/hooks/useElectedCouncil'
import { MemberInfo } from '@/memberships/components'
import { useMyMemberships } from '@/memberships/hooks/useMyMemberships'
+import { CancelProposalButton } from '@/proposals/components/CancelProposalButton'
import { ProposalDetails } from '@/proposals/components/ProposalDetails/ProposalDetails'
import { ProposalDiscussions } from '@/proposals/components/ProposalDiscussions'
import { ProposalHistory } from '@/proposals/components/ProposalHistory'
@@ -117,6 +118,11 @@ export const ProposalPreview = () => {
{proposal.title}
+ {active?.id === proposal.proposer.id &&
+ proposal.votes.length === 0 &&
+ (proposal.status === 'deciding' || proposal.status === 'dormant') && (
+
+ )}
{active?.isCouncilMember &&
proposal.status === 'deciding' &&
(!hasVoted ? (
diff --git a/packages/ui/src/proposals/components/CancelProposalButton.tsx b/packages/ui/src/proposals/components/CancelProposalButton.tsx
new file mode 100644
index 0000000000..6f38d32a13
--- /dev/null
+++ b/packages/ui/src/proposals/components/CancelProposalButton.tsx
@@ -0,0 +1,27 @@
+import React, { useCallback } from 'react'
+
+import { ButtonSecondary } from '@/common/components/buttons'
+import { useModal } from '@/common/hooks/useModal'
+import { Member } from '@/memberships/types'
+
+import { CancelProposalModalCall } from '../modals/CancelProposal'
+
+interface Props {
+ member: Member
+ proposalId: string
+}
+
+export const CancelProposalButton = ({ member, proposalId }: Props) => {
+ const { showModal } = useModal()
+ const cancelProposalModal = useCallback(() => {
+ showModal({
+ modal: 'CancelProposalModal',
+ data: { member, proposalId },
+ })
+ }, [])
+ return (
+
+ Cancel Proposal
+
+ )
+}
diff --git a/packages/ui/src/proposals/modals/CancelProposal/CancelProposalModal.tsx b/packages/ui/src/proposals/modals/CancelProposal/CancelProposalModal.tsx
new file mode 100644
index 0000000000..17c0d682ce
--- /dev/null
+++ b/packages/ui/src/proposals/modals/CancelProposal/CancelProposalModal.tsx
@@ -0,0 +1,70 @@
+import React, { useEffect, useMemo } from 'react'
+
+import { useTransactionFee } from '@/accounts/hooks/useTransactionFee'
+import { InsufficientFundsModal } from '@/accounts/modals/InsufficientFundsModal'
+import { useApi } from '@/api/hooks/useApi'
+import { TextMedium } from '@/common/components/typography'
+import { useMachine } from '@/common/hooks/useMachine'
+import { useModal } from '@/common/hooks/useModal'
+import { SignTransactionModal } from '@/common/modals/SignTransactionModal/SignTransactionModal'
+import { defaultTransactionModalMachine } from '@/common/model/machines/defaultTransactionModalMachine'
+
+import { CancelProposalModalCall } from './types'
+
+export const CancelProposalModal = () => {
+ const { hideModal, modalData } = useModal()
+ const { member, proposalId } = modalData
+ const machine = useMemo(
+ () =>
+ defaultTransactionModalMachine(
+ 'There was a problem cancelling your proposal.',
+ 'Your propsal has been cancelled.'
+ ),
+ []
+ )
+ const [state, send] = useMachine(machine, { context: { validateBeforeTransaction: true } })
+ const { api, isConnected } = useApi()
+
+ const { transaction, feeInfo } = useTransactionFee(
+ member.controllerAccount,
+ () => {
+ if (api && isConnected) {
+ return api.tx.proposalsEngine.cancelProposal(member.id, proposalId)
+ }
+ },
+ [modalData, isConnected]
+ )
+
+ useEffect(() => {
+ if (state.matches('requirementsVerification')) {
+ if (transaction && feeInfo) {
+ feeInfo.canAfford && send('PASS')
+ !feeInfo.canAfford && send('FAIL')
+ }
+ }
+
+ if (state.matches('beforeTransaction')) {
+ send(feeInfo?.canAfford ? 'PASS' : 'FAIL')
+ }
+ }, [state.value, member, transaction, feeInfo?.canAfford])
+
+ if (state.matches('transaction') && transaction && member) {
+ return (
+
+ You intend to cancel your proposal.
+
+ )
+ }
+
+ if (state.matches('requirementsFailed') && member && feeInfo) {
+ return (
+
+ )
+ }
+ return null
+}
diff --git a/packages/ui/src/proposals/modals/CancelProposal/index.ts b/packages/ui/src/proposals/modals/CancelProposal/index.ts
new file mode 100644
index 0000000000..3ae4dd6f55
--- /dev/null
+++ b/packages/ui/src/proposals/modals/CancelProposal/index.ts
@@ -0,0 +1,2 @@
+export type { CancelProposalModalCall } from './types'
+export * from './CancelProposalModal'
diff --git a/packages/ui/src/proposals/modals/CancelProposal/types.ts b/packages/ui/src/proposals/modals/CancelProposal/types.ts
new file mode 100644
index 0000000000..f506ea0e0c
--- /dev/null
+++ b/packages/ui/src/proposals/modals/CancelProposal/types.ts
@@ -0,0 +1,4 @@
+import { ModalWithDataCall } from '@/common/providers/modal/types'
+import { Member } from '@/memberships/types'
+
+export type CancelProposalModalCall = ModalWithDataCall<'CancelProposalModal', { member: Member; proposalId: string }>
From 42b99837f393904eeebfe4f15c92a53db6b14a3c Mon Sep 17 00:00:00 2001
From: Theophile Sandoz
Date: Mon, 5 Feb 2024 18:43:23 +0100
Subject: [PATCH 3/3] Update version to `2.5.0`
---
CHANGELOG.md | 17 ++++++++++++++++-
packages/ui/package.json | 2 +-
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 010c1aca5a..fb4efdc772 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [2.5.0] - 2024-02-05
+
+### Added
+- Cancel proposal button.
+
+### Changed
+- Display weekly opening rewards instead of daily.
+
+## [2.4.2] - 2024-01-29
+
+### Fixed
+- Fix infinite proposal page reload for CMs.
+
## [2.4.1] - 2024-01-29
### Fixed
@@ -292,7 +305,9 @@ 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/v2.4.1...HEAD
+[unreleased]: https://github.com/Joystream/pioneer/compare/v2.5.0...HEAD
+[2.5.0]: https://github.com/Joystream/pioneer/compare/v2.4.2...v2.5.0
+[2.4.2]: https://github.com/Joystream/pioneer/compare/v2.4.1...v2.4.2
[2.4.1]: https://github.com/Joystream/pioneer/compare/v2.4.0...v2.4.1
[2.4.0]: https://github.com/Joystream/pioneer/compare/v2.3.1...v2.4.0
[2.3.1]: https://github.com/Joystream/pioneer/compare/v2.3.0...v2.3.1
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 1a3482abd6..6f51f38871 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@joystream/pioneer",
- "version": "2.4.1",
+ "version": "2.5.0",
"license": "GPL-3.0-only",
"scripts": {
"build": "node --max_old_space_size=4096 ./build.js",