From a04c38ce6fe7c287942651364b87ef01b1435914 Mon Sep 17 00:00:00 2001 From: Megha-Dev-19 <100185149+Megha-Dev-19@users.noreply.github.com> Date: Wed, 10 Apr 2024 07:31:53 +0530 Subject: [PATCH] Proposal Notifications support (#167) * make vote notification optional * added notification for proposal * update file names * Update Proposals.jsx --------- Co-authored-by: Elliot Braem <16282460+elliotBraem@users.noreply.github.com> --- .../widget/Notification/Item/Left.jsx | 9 + .../widget/Notification/Item/Right.jsx | 24 +++ .../widget/Notification/Item/buildhub.jsx | 22 +++ .../NotificationRolesSelector.jsx | 173 ++++++++++++++++++ apps/builddao/widget/Proposals.jsx | 64 +++---- .../components/modals/propose/AddMember.jsx | 19 +- .../modals/propose/FunctionCall.jsx | 18 +- .../modals/propose/RemoveMember.jsx | 19 +- .../widget/components/modals/propose/Text.jsx | 19 +- .../components/modals/propose/Transfer.jsx | 23 ++- 10 files changed, 340 insertions(+), 50 deletions(-) create mode 100644 apps/builddao/widget/Notification/Item/Left.jsx create mode 100644 apps/builddao/widget/Notification/Item/Right.jsx create mode 100644 apps/builddao/widget/Notification/Item/buildhub.jsx create mode 100644 apps/builddao/widget/Notification/NotificationRolesSelector.jsx diff --git a/apps/builddao/widget/Notification/Item/Left.jsx b/apps/builddao/widget/Notification/Item/Left.jsx new file mode 100644 index 00000000..ea3c7e3e --- /dev/null +++ b/apps/builddao/widget/Notification/Item/Left.jsx @@ -0,0 +1,9 @@ +const { href } = VM.require("buildhub.near/widget/lib.url") || { + href: () => {} +}; + +if (!props.type) { + return "Loading ..."; +} + +return <>{props.message}; diff --git a/apps/builddao/widget/Notification/Item/Right.jsx b/apps/builddao/widget/Notification/Item/Right.jsx new file mode 100644 index 00000000..4c941075 --- /dev/null +++ b/apps/builddao/widget/Notification/Item/Right.jsx @@ -0,0 +1,24 @@ +const { href } = VM.require("buildhub.near/widget/lib.url") || { + href: () => {} +}; + +if (!props.type) { + return "Loading ..."; +} + +return ( + + View + +); diff --git a/apps/builddao/widget/Notification/Item/buildhub.jsx b/apps/builddao/widget/Notification/Item/buildhub.jsx new file mode 100644 index 00000000..058712ed --- /dev/null +++ b/apps/builddao/widget/Notification/Item/buildhub.jsx @@ -0,0 +1,22 @@ +const { value } = props; + +return ( + + ), + R: ( + + ), + ...props + }} + /> +); diff --git a/apps/builddao/widget/Notification/NotificationRolesSelector.jsx b/apps/builddao/widget/Notification/NotificationRolesSelector.jsx new file mode 100644 index 00000000..5d96449a --- /dev/null +++ b/apps/builddao/widget/Notification/NotificationRolesSelector.jsx @@ -0,0 +1,173 @@ +const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK"); +const { InputField } = VM.require("buildhub.near/widget/components") || { + InputField: <> +}; + +if (!DaoSDK) { + return <>; +} +const [groupsAndMembers, setGroupsAndMembers] = useState([]); +const [selectedRoles, setSelectedRoles] = useState({}); // { role:boolean } +const daoId = props.daoId || "build.sputnik-dao.near"; +const accountId = props.accountId ?? context.accountId; +const onUpdate = props.onUpdate ?? (() => {}); +const proposalType = props.proposalType; +const [message, setMessage] = useState( + `${accountId} created ${proposalType} proposal for ${daoId}` +); +const bootstrapTheme = props.bootstrapTheme || "dark"; + +const sdk = DaoSDK(daoId); + +const group = sdk.getGroupsAndMembers(); +if (group === null || !group.length) { + return; +} +setGroupsAndMembers(group); + +const handleCheckboxChange = (role) => { + setSelectedRoles((prevRoles) => { + if (prevRoles.hasOwnProperty(role)) { + return { + ...prevRoles, + [role]: !prevRoles[role] + }; + } else { + return { + ...prevRoles, + [role]: true + }; + } + }); +}; + +const ThemeContainer = + props.ThemeContainer || + styled.div` + --primary-color: rgb(255, 175, 81); + `; + +const Wrapper = styled.div` + .checked > span:first-child { + background: var(--primary-color) !important; + border-color: var(--primary-color) !important; + } + + .cbx:hover span:first-child { + border-color: var(--primary-color) !important; + } + + button[type="checkbox"]:hover { + background: none !important; + } + + label { + font-size: 13px; + } +`; + +const createNotificationsData = () => { + const someRoleSelected = Object.values(selectedRoles).some( + (value) => value === true + ); + if (!someRoleSelected) { + return null; + } + const membersToNotify = []; + Object.keys(selectedRoles).map((item) => { + if (selectedRoles[item] === true) { + membersToNotify = membersToNotify.concat( + groupsAndMembers.find((group) => group.name === item).members + ); + } + }); + const uniqueMembersArray = [...new Set(membersToNotify)]; + const notification = { + [accountId]: { + index: { + notify: JSON.stringify( + uniqueMembersArray.map((account) => { + return { + key: account, + value: { + message: message, + params: { + daoId: daoId, + tab: "proposals", + page: "proposal" + }, + type: "buildhub/custom", + widget: "buildhub.near/widget/home" + } + }; + }) + ) + } + } + }; + const call = [ + { + contractName: "social.near", + methodName: "set", + args: { data: notification, options: { refund_unused_deposit: true } }, + deposit: 200000000000000000000000 + } + ]; + return call; +}; + +useEffect(() => { + onUpdate(createNotificationsData()); +}, [selectedRoles]); + +const capitalizeFirstLetter = (string) => { + return string.charAt(0).toUpperCase() + string.slice(1); +}; + +const groupList = useMemo(() => { + return ( + Array.isArray(groupsAndMembers) && + groupsAndMembers.map((group) => { + const membersLength = group?.members.length; + if (!membersLength) { + return null; + } + return ( +
+ + {capitalizeFirstLetter(group.name)} ({membersLength} members) +
+ ), + onChange: (checked) => handleCheckboxChange(group.name), + checked: selectedRoles[group.name] ?? false + }} + /> + + ); + }) + ); +}, [groupsAndMembers, selectedRoles]); + +return ( + + +
Send notification to following roles: (Optional)
+
+ + setMessage(e.target.value)} + /> +
+
{groupList}
+
+
+); diff --git a/apps/builddao/widget/Proposals.jsx b/apps/builddao/widget/Proposals.jsx index 9e8430a1..3ea2fdeb 100644 --- a/apps/builddao/widget/Proposals.jsx +++ b/apps/builddao/widget/Proposals.jsx @@ -5,6 +5,7 @@ const { Button, Modal } = VM.require("buildhub.near/widget/components") || { const { Header } = VM.require("buildhub.near/widget/components.Header") || { Header: () => <>, + }; const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK") || (() => {}); @@ -113,7 +114,7 @@ const NotificationModal = () => { daoId, proposalId: voteDetails.proposalId, proposer: voteDetails.proposer, - showNotification: false, + showNotification: false }); setNotificationModal(false); }} @@ -128,7 +129,7 @@ const NotificationModal = () => { daoId, proposalId: voteDetails.proposalId, proposer: voteDetails.proposer, - showNotification: true, + showNotification: true }); setNotificationModal(false); }} @@ -156,13 +157,13 @@ const handleVote = ({ action, proposalId, proposer, showNotification }) => { daoId: daoId, proposalId: proposalId, }, - type: "custom", - widget: "buildhub.near/widget/Proposals", - }, - }, - ]), - }, - }, + type: "buildhub/custom", + widget: "buildhub.near/widget/Proposals" + } + } + ]) + } + } }; sdk.actProposal({ @@ -176,12 +177,14 @@ const handleVote = ({ action, proposalId, proposer, showNotification }) => { methodName: "set", args: { data: notification, - options: { refund_unused_deposit: true }, + options: { refund_unused_deposit: true } }, - deposit: 100000000000000000000000, - }, + deposit: Big(JSON.stringify(notification).length * 16) + .mul(Big(10).pow(20)) + .toString() + } ] - : null, + : null }); }; @@ -302,7 +305,8 @@ const proposalsComponent = useMemo(() => { handleVote: (data) => { setVoteDetails(data); setNotificationModal(true); - }, + + } }} /> ); @@ -321,7 +325,7 @@ return ( src="buildhub.near/widget/components.modals.CreateProposal" props={{ showModal: showProposalModal, - toggleModal: () => setShowModal(!showProposalModal), + toggleModal: () => setShowModal(!showProposalModal) }} /> setFiltersModal(!showFiltersModal), }} /> -
-
-

Proposals

-
- - -
+
+

Proposals

+
+ +
-
+
{proposalsComponent}
{!proposalId && ( diff --git a/apps/builddao/widget/components/modals/propose/AddMember.jsx b/apps/builddao/widget/components/modals/propose/AddMember.jsx index e1b6a780..68725864 100644 --- a/apps/builddao/widget/components/modals/propose/AddMember.jsx +++ b/apps/builddao/widget/components/modals/propose/AddMember.jsx @@ -1,5 +1,5 @@ const { Button } = VM.require("buildhub.near/widget/components") || { - Button: () => <>, + Button: () => <> }; const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK") || (() => {}); @@ -29,6 +29,7 @@ useEffect(() => { }, [props.item]); const memoizedKey = useMemo((editorKey) => editorKey, [editorKey]); const [validatedAddresss, setValidatedAddresss] = useState(true); +const [notificationsData, setNotificationData] = useState(null); const regex = /.{1}\.near$/; useEffect(() => { @@ -221,12 +222,21 @@ return ( embedCss: props.customCSS || MarkdownEditor, onChange: (v) => { setText(v); - }, + } }} /> - + { + setNotificationData(v); + }, + proposalType: "Add Member" + }} + />
diff --git a/apps/builddao/widget/components/modals/propose/FunctionCall.jsx b/apps/builddao/widget/components/modals/propose/FunctionCall.jsx index a68d101b..1bab145b 100644 --- a/apps/builddao/widget/components/modals/propose/FunctionCall.jsx +++ b/apps/builddao/widget/components/modals/propose/FunctionCall.jsx @@ -1,5 +1,5 @@ const { Button } = VM.require("buildhub.near/widget/components") || { - Button: () => <>, + Button: () => <> }; const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK") || (() => {}); @@ -14,6 +14,7 @@ const [deposit, setDeposit] = useState(0); const [validatedAddresss, setValidatedAddress] = useState(true); const [text, setText] = useState(""); const [editorKey, setEditorKey] = useState(0); +const [notificationsData, setNotificationData] = useState(null); const bootstrapTheme = props.bootstrapTheme; @@ -249,11 +250,21 @@ return ( embedCss: props.customCSS || MarkdownEditor, onChange: (v) => { setText(v); - }, + } }} /> + { + setNotificationData(v); + }, + proposalType: "Function Call" + }} + />
diff --git a/apps/builddao/widget/components/modals/propose/RemoveMember.jsx b/apps/builddao/widget/components/modals/propose/RemoveMember.jsx index 8dcba1e5..27e63ebf 100644 --- a/apps/builddao/widget/components/modals/propose/RemoveMember.jsx +++ b/apps/builddao/widget/components/modals/propose/RemoveMember.jsx @@ -1,5 +1,5 @@ const { Button } = VM.require("buildhub.near/widget/components") || { - Button: () => <>, + Button: () => <> }; const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK") || (() => {}); @@ -15,6 +15,7 @@ const sdk = DaoSDK(selectedDAO); const [text, setText] = useState(""); const [editorKey, setEditorKey] = useState(0); +const [notificationsData, setNotificationData] = useState(null); const bootstrapTheme = props.bootstrapTheme; useEffect(() => { @@ -223,12 +224,21 @@ return ( embedCss: props.customCSS || MarkdownEditor, onChange: (v) => { setText(v); - }, + } }} /> - + { + setNotificationData(v); + }, + proposalType: "Remove Member" + }} + />
diff --git a/apps/builddao/widget/components/modals/propose/Text.jsx b/apps/builddao/widget/components/modals/propose/Text.jsx index cfbb5072..04220e6b 100644 --- a/apps/builddao/widget/components/modals/propose/Text.jsx +++ b/apps/builddao/widget/components/modals/propose/Text.jsx @@ -1,5 +1,5 @@ const { Button } = VM.require("buildhub.near/widget/components") || { - Button: () => <>, + Button: () => <> }; const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK") || (() => {}); @@ -19,6 +19,7 @@ useEffect(() => { }, [props.item]); const memoizedKey = useMemo((editorKey) => editorKey, [editorKey]); const selectedDAO = props.selectedDAO; +const [notificationsData, setNotificationData] = useState(null); const sdk = DaoSDK(selectedDAO); const MarkdownEditor = ` @@ -165,10 +166,21 @@ return ( embedCss: props.customCSS || MarkdownEditor, onChange: (v) => { setText(v); - }, + } }} /> + { + setNotificationData(v); + }, + proposalType: "Add Member" + }} + /> + {console.log(notificationsData)}
diff --git a/apps/builddao/widget/components/modals/propose/Transfer.jsx b/apps/builddao/widget/components/modals/propose/Transfer.jsx index 2af69c4e..615afebf 100644 --- a/apps/builddao/widget/components/modals/propose/Transfer.jsx +++ b/apps/builddao/widget/components/modals/propose/Transfer.jsx @@ -1,5 +1,5 @@ const { Button } = VM.require("buildhub.near/widget/components") || { - Button: () => <>, + Button: () => <> }; const DaoSDK = VM.require("sdks.near/widget/SDKs.Sputnik.DaoSDK") || (() => {}); @@ -17,6 +17,8 @@ const bootstrapTheme = props.bootstrapTheme; const [text, setText] = useState(""); const [editorKey, setEditorKey] = useState(0); +const [notificationsData, setNotificationData] = useState(null); + useEffect(() => { if (!props.item) { return; @@ -37,8 +39,8 @@ const tokensData = [ icon: "", name: "NEAR", symbol: "NEAR", - tokenId: NearTokenId, - }, + tokenId: NearTokenId + } ]; if (res.body) { res.body?.tokens?.fts.map((item) => { @@ -262,11 +264,21 @@ return ( embedCss: props.customCSS || MarkdownEditor, onChange: (v) => { setText(v); - }, + } }} /> + { + setNotificationData(v); + }, + proposalType: "Add Member" + }} + />