diff --git a/packages/apps-config/src/links/polkassembly.ts b/packages/apps-config/src/links/polkassembly.ts index a381594523..266e4124bd 100644 --- a/packages/apps-config/src/links/polkassembly.ts +++ b/packages/apps-config/src/links/polkassembly.ts @@ -16,6 +16,7 @@ export default { isActive: true, logo: externalLogos.polkassembly as string, paths: { + bounty: 'bounty', council: 'motion', proposal: 'proposal', referendum: 'referendum', diff --git a/packages/apps-config/src/links/types.ts b/packages/apps-config/src/links/types.ts index 84465847d1..9f4471588b 100644 --- a/packages/apps-config/src/links/types.ts +++ b/packages/apps-config/src/links/types.ts @@ -3,7 +3,7 @@ import BN from 'bn.js'; -export type LinkTypes = 'address' | 'block' | 'council' | 'extrinsic' | 'proposal' | 'referendum' | 'techcomm' | 'tip' | 'treasury'; +export type LinkTypes = 'address' | 'block' | 'bounty' | 'council' | 'extrinsic' | 'proposal' | 'referendum' | 'techcomm' | 'tip' | 'treasury'; export interface ExternalDef { chains: Record; diff --git a/packages/apps-routing/src/bounties.ts b/packages/apps-routing/src/bounties.ts new file mode 100644 index 0000000000..b88a968908 --- /dev/null +++ b/packages/apps-routing/src/bounties.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2020 @polkadot/apps-routing authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { TFunction } from 'i18next'; + +import Component from '@polkadot/app-bounties'; + +import type { Route } from './types'; + +export default function create (t: TFunction): Route { + return { + Component, + display: { + needsApi: [ + 'tx.treasury.proposeBounty' + ] + }, + group: 'governance', + icon: 'coins', + name: 'bounties', + text: t('nav.bounties', 'Bounties', { ns: 'apps-routing' }) + }; +} diff --git a/packages/apps-routing/src/index.ts b/packages/apps-routing/src/index.ts index 3b8e31cd55..98f349dd00 100644 --- a/packages/apps-routing/src/index.ts +++ b/packages/apps-routing/src/index.ts @@ -6,6 +6,7 @@ import type { TFunction } from 'i18next'; import type { Routes } from './types'; import accounts from './accounts'; import addresses from './addresses'; +import bounties from './bounties'; import calendar from './calendar'; import claims from './claims'; import contracts from './contracts'; @@ -41,6 +42,7 @@ export default function create (t: TFunction): Routes { democracy(t), council(t), treasury(t), + bounties(t), techcomm(t), parachains(t), society(t), diff --git a/packages/apps/public/locales/en/app-bounties.json b/packages/apps/public/locales/en/app-bounties.json new file mode 100644 index 0000000000..08a6b46739 --- /dev/null +++ b/packages/apps/public/locales/en/app-bounties.json @@ -0,0 +1,10 @@ +{ + "Bounties": "Bounties", + "No open bounties": "No open bounties", + "bounties": "bounties", + "curator": "curator", + "title": "title", + "update due": "update due", + "value": "value", + "{{blocks}} blocks": "{{blocks}} blocks" +} \ No newline at end of file diff --git a/packages/apps/public/locales/en/apps-routing.json b/packages/apps/public/locales/en/apps-routing.json index 45e573e13b..3c668f71eb 100644 --- a/packages/apps/public/locales/en/apps-routing.json +++ b/packages/apps/public/locales/en/apps-routing.json @@ -1,6 +1,7 @@ { "nav.accounts": "Accounts", "nav.addresses": "Address book", + "nav.bounties": "Bounties", "nav.calendar": "Event calendar", "nav.claims": "Claim Tokens", "nav.contracts": "Contracts", diff --git a/packages/apps/public/locales/en/index.json b/packages/apps/public/locales/en/index.json index 7ac619c08f..a568314ed0 100644 --- a/packages/apps/public/locales/en/index.json +++ b/packages/apps/public/locales/en/index.json @@ -1,6 +1,7 @@ [ "app-accounts.json", "app-addresses.json", + "app-bounties.json", "app-calendar.json", "app-claims.json", "app-contracts.json", diff --git a/packages/apps/public/locales/en/translation.json b/packages/apps/public/locales/en/translation.json index ab1731d461..c7e111a5fc 100644 --- a/packages/apps/public/locales/en/translation.json +++ b/packages/apps/public/locales/en/translation.json @@ -154,6 +154,7 @@ "bids": "", "bond": "", "bonded": "", + "bounties": "", "call data for final approval": "", "candidates": "", "clipboard": "", @@ -166,6 +167,7 @@ "contracts": "", "controller": "", "count": "", + "curator": "", "defender": "", "denomination vote": "", "deploy": "", @@ -242,12 +244,14 @@ "the payment account": "", "threshold": "", "tips": "", + "title": "", "total": "", "transactions": "", "turnout": "", "type": "", "unapplied": "", "until block": "", + "update due": "", "using my account": "", "validation": "", "validators": "", diff --git a/packages/page-bounties/.skip-build b/packages/page-bounties/.skip-build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/page-bounties/.skip-npm b/packages/page-bounties/.skip-npm new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/page-bounties/LICENSE b/packages/page-bounties/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/page-bounties/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/page-bounties/README.md b/packages/page-bounties/README.md new file mode 100644 index 0000000000..58aed8d610 --- /dev/null +++ b/packages/page-bounties/README.md @@ -0,0 +1 @@ +# @polkadot/bounties diff --git a/packages/page-bounties/package.json b/packages/page-bounties/package.json new file mode 100644 index 0000000000..fdabc9c4a8 --- /dev/null +++ b/packages/page-bounties/package.json @@ -0,0 +1,19 @@ +{ + "name": "@polkadot/app-bounties", + "private": true, + "version": "0.69.2-8", + "description": "A on-chain bounties", + "main": "index.js", + "scripts": {}, + "author": "", + "maintainers": [], + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@polkadot/react-components": "^0.69.2-1" + }, + "devDependencies": { + "@testing-library/react": "^11.2.2", + "testcontainers": "^4.7.0" + } +} diff --git a/packages/page-bounties/src/Bounties.tsx b/packages/page-bounties/src/Bounties.tsx new file mode 100644 index 0000000000..206e8263c9 --- /dev/null +++ b/packages/page-bounties/src/Bounties.tsx @@ -0,0 +1,63 @@ +// Copyright 2017-2020 @polkadot/app-bounties authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import React, { useRef } from 'react'; + +import type { Bytes, Option } from '@polkadot/types'; +import type { BlockNumber, Bounty as BountyType, BountyIndex } from '@polkadot/types/interfaces'; +import { BountyDescription } from '@polkadot/app-bounties/types'; +import { Table } from '@polkadot/react-components'; +import { useApi, useCall } from '@polkadot/react-hooks'; +import { StorageKey } from '@polkadot/types'; + +import Bounty from './Bounty'; +import { useTranslation } from './translate'; + +type BountyEntry = [{ args: [BountyIndex] }, Option]; +type BountyDescriptionEntry = [StorageKey, Option]; + +const transformBountyDescriptions = { + transform: (entries: BountyDescriptionEntry[]): BountyDescription[] => { + return entries.map(([key, description]) => ({ description: description.value.toHuman() as string, id: key.args[0] })); + } +}; + +function Bounties (): React.ReactElement { + const { t } = useTranslation(); + const { api } = useApi(); + + const bounties = useCall(api.query.treasury.bounties.entries); + const bountyDescriptions = useCall(api.query.treasury.bountyDescriptions.entries, undefined, transformBountyDescriptions); + const bestNumber = useCall(api.derive.chain.bestNumber); + + const headerRef = useRef([ + [t('bounties'), 'start'], + [t('title'), 'start'], + [], + [t('value'), 'start'], + [], + [t('curator'), 'start'], + [t('update due'), 'start'], + [], + [] + ]); + + return ( + ('No open bounties')} + header={headerRef.current} + > + {bounties && bountyDescriptions && bestNumber && bounties.map((bounty: BountyEntry, index: number): React.ReactNode => ( + + ))} +
+ ); +} + +export default React.memo(Bounties); diff --git a/packages/page-bounties/src/Bounty.tsx b/packages/page-bounties/src/Bounty.tsx new file mode 100644 index 0000000000..392ea76648 --- /dev/null +++ b/packages/page-bounties/src/Bounty.tsx @@ -0,0 +1,166 @@ +// Copyright 2017-2020 @polkadot/app-bounties authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import React, { useCallback, useMemo, useState } from 'react'; +import styled from 'styled-components'; + +import type { BlockNumber, Bounty as BountyType } from '@polkadot/types/interfaces'; +import { BountyDescription } from '@polkadot/app-bounties/types'; +import { AddressSmall, Icon, LinkExternal } from '@polkadot/react-components'; +import { BlockToTime, FormatBalance } from '@polkadot/react-query'; +import { formatNumber } from '@polkadot/util'; + +import { getBountyStatus } from './helpers'; +import { useTranslation } from './translate'; + +interface Props { + bestNumber: BlockNumber; + bounty: BountyType; + className?: string; + description: BountyDescription; + index: number; +} + +const EMPTY_CELL = '-'; + +function Bounty ({ bestNumber, bounty, className = '', description, index }: Props): React.ReactElement { + const { t } = useTranslation(); + const [isExpanded, setIsExpanded] = useState(false); + + const { bond, curatorDeposit, fee, proposer, status, value }: BountyType = bounty; + + const updateStatus = useCallback(() => getBountyStatus(status), [status]); + + const { bountyStatus, curator, updateDue } = updateStatus(); + + const blocksUntilUpdate = useMemo(() => updateDue?.sub(bestNumber), [bestNumber, updateDue]); + + const handleOnIconClick = () => { + setIsExpanded(!isExpanded); + }; + + return ( + <> + + {bountyStatus} + {description.description} + + + + {curator ? : EMPTY_CELL} + + {updateDue ? : EMPTY_CELL} + {updateDue && t('{{blocks}} blocks', { replace: { blocks: formatNumber(blocksUntilUpdate) } })} + + + + + +
+ +
+ + + + + +
Proposer
+ + + +
Value
+
Bond
+ + +
+
+ + +
Curators fee
+
Curators deposit
+ + +
{curator ? : EMPTY_CELL }
+
{curator ? : EMPTY_CELL }
+ + + + + + + ); +} + +export default React.memo(styled(Bounty)` + & .links { + display: inline-flex; + } + + & .table-column-icon { + width: 52px; + padding: 0; + + div { + padding: 0.75rem; + cursor: pointer; + } + } + + & .inline-balance { + display: inline-block; + width: 100%; + margin: 12px 0; + font-size: 1rem; + line-height: normal; + } + + & .column-with-label { + width: 110px; + vertical-align: middle; + padding: 0; + div { + text-align: right; + padding: 12px 0 12px 0; + font-size: 0.7rem; + line-height: normal; + text-transform: uppercase; + color: #4D4D4D; + &:nth-child(2){ + padding: 18px 0 10px 0; + } + } + } + + & .column-curator { + width: 100px; + } + + & .proposer { + vertical-align: middle; + div { + display: inline-block; + } + .label { + text-align: right; + padding: 12px 12px 12px 0; + font-size: 0.7rem; + line-height: normal; + text-transform: uppercase; + color: #4D4D4D; + } + } + & .title-column { + min-width: 20rem; + } +`); diff --git a/packages/page-bounties/src/helpers.ts b/packages/page-bounties/src/helpers.ts new file mode 100644 index 0000000000..ae233aeb53 --- /dev/null +++ b/packages/page-bounties/src/helpers.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2020 @polkadot/app-bounties authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { BountyStatus } from '@polkadot/types/interfaces'; + +import { BountyStatusType } from './types'; + +export const getBountyStatus = (status: BountyStatus): BountyStatusType => { + const statusAsString = status.type; + + let result: BountyStatusType = { + beneficiary: undefined, + bountyStatus: statusAsString, + curator: undefined, + unlockAt: undefined, + updateDue: undefined + }; + + if (status.isCuratorProposed) { + result = { + ...result, + curator: status.asCuratorProposed.curator + }; + } + + if (status.isActive) { + result = { + ...result, + curator: status.asActive.curator, + updateDue: status.asActive.updateDue + }; + } + + return result; +}; diff --git a/packages/page-bounties/src/index.tsx b/packages/page-bounties/src/index.tsx new file mode 100644 index 0000000000..0e08208e91 --- /dev/null +++ b/packages/page-bounties/src/index.tsx @@ -0,0 +1,39 @@ +// Copyright 2017-2020 @polkadot/app-bounties authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import React, { useRef } from 'react'; + +import { Tabs } from '@polkadot/react-components'; + +import Bounties from './Bounties'; +import { useTranslation } from './translate'; + +interface Props { + basePath: string; +} + +function BountiesApp ({ basePath }: Props): React.ReactElement { + const { t } = useTranslation(); + + const itemsRef = useRef([ + { + isRoot: true, + name: 'index', + text: t('Bounties') + } + ]); + + return ( +
+
+ +
+ +
+ ); +} + +export default React.memo(BountiesApp); diff --git a/packages/page-bounties/src/translate.ts b/packages/page-bounties/src/translate.ts new file mode 100644 index 0000000000..6c692a8d1b --- /dev/null +++ b/packages/page-bounties/src/translate.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2020 @polkadot/app-explorer authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { UseTranslationResponse } from 'react-i18next'; +import { useTranslation as useTranslationBase } from 'react-i18next'; + +export function useTranslation (): UseTranslationResponse { + return useTranslationBase('app-bounties'); +} diff --git a/packages/page-bounties/src/types.ts b/packages/page-bounties/src/types.ts new file mode 100644 index 0000000000..de73998338 --- /dev/null +++ b/packages/page-bounties/src/types.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2020 @polkadot/app-bounties authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { AccountId, BlockNumber } from '@polkadot/types/interfaces/runtime'; +import type { Codec } from '@polkadot/types/types'; + +export interface BountyStatusType { + beneficiary: string | undefined; + bountyStatus: string; + curator: AccountId | undefined; + unlockAt: number | undefined; + updateDue: BlockNumber | undefined; +} + +export interface BountyDescription { + id: Codec; + description: string; +} diff --git a/tsconfig.json b/tsconfig.json index 87213fc17e..ae2500e2ad 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,8 @@ "@polkadot/app-accounts/*": [ "packages/page-accounts/src/*" ], "@polkadot/app-addresses": [ "packages/page-addresses/src" ], "@polkadot/app-addresses/*": [ "packages/page-addresses/src/*" ], + "@polkadot/app-bounties": [ "packages/page-bounties/src" ], + "@polkadot/app-bounties/*": [ "packages/page-bounties/src/*" ], "@polkadot/app-calendar": [ "packages/page-calendar/src" ], "@polkadot/app-calendar/*": [ "packages/page-calendar/src/*" ], "@polkadot/app-claims": [ "packages/page-claims/src" ], diff --git a/yarn.lock b/yarn.lock index 5c4353c5de..3bbd693b0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2047,6 +2047,17 @@ __metadata: languageName: unknown linkType: soft +"@polkadot/app-bounties@workspace:packages/page-bounties": + version: 0.0.0-use.local + resolution: "@polkadot/app-bounties@workspace:packages/page-bounties" + dependencies: + "@babel/runtime": ^7.12.5 + "@polkadot/react-components": ^0.69.2-1 + "@testing-library/react": ^11.2.2 + testcontainers: ^4.7.0 + languageName: unknown + linkType: soft + "@polkadot/app-calendar@workspace:packages/page-calendar": version: 0.0.0-use.local resolution: "@polkadot/app-calendar@workspace:packages/page-calendar" @@ -2516,7 +2527,7 @@ __metadata: languageName: unknown linkType: soft -"@polkadot/react-components@^0.69.2-23, @polkadot/react-components@workspace:packages/react-components": +"@polkadot/react-components@^0.69.2-1, @polkadot/react-components@^0.69.2-23, @polkadot/react-components@workspace:packages/react-components": version: 0.0.0-use.local resolution: "@polkadot/react-components@workspace:packages/react-components" dependencies: