Skip to content

Commit

Permalink
Proposal Notifications support (#167)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
Megha-Dev-19 and elliotBraem authored Apr 10, 2024
1 parent ad82aa6 commit a04c38c
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 50 deletions.
9 changes: 9 additions & 0 deletions apps/builddao/widget/Notification/Item/Left.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const { href } = VM.require("buildhub.near/widget/lib.url") || {
href: () => {}
};

if (!props.type) {
return "Loading ...";
}

return <>{props.message}</>;
24 changes: 24 additions & 0 deletions apps/builddao/widget/Notification/Item/Right.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { href } = VM.require("buildhub.near/widget/lib.url") || {
href: () => {}
};

if (!props.type) {
return "Loading ...";
}

return (
<a
className="fw-bold text-muted"
href={href({
widgetSrc: props.widget,
params: {
page: props.page,
proposalId: props.params.proposalId,
tab: props.params.tab,
daoId: props.params.daoId
}
})}
>
View
</a>
);
22 changes: 22 additions & 0 deletions apps/builddao/widget/Notification/Item/buildhub.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const { value } = props;

return (
<Widget
src="mob.near/widget/Notification.Item.LR"
props={{
L: (
<Widget
src="buildhub.near/widget/notification.Item.Left"
props={{ ...value }}
/>
),
R: (
<Widget
src="buildhub.near/widget/notification.Item.Right"
props={{ ...value }}
/>
),
...props
}}
/>
);
173 changes: 173 additions & 0 deletions apps/builddao/widget/Notification/NotificationRolesSelector.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div key={group}>
<Widget
src="nearui.near/widget/Input.Checkbox"
props={{
label: (
<div>
{capitalizeFirstLetter(group.name)} ({membersLength} members)
</div>
),
onChange: (checked) => handleCheckboxChange(group.name),
checked: selectedRoles[group.name] ?? false
}}
/>
</div>
);
})
);
}, [groupsAndMembers, selectedRoles]);

return (
<ThemeContainer>
<Wrapper className="d-flex flex-column gap-2">
<div>Send notification to following roles: (Optional)</div>
<div>
<label htmlFor={"notifications-label" + daoId}>Custom Message</label>
<input
name={"notifications-label" + daoId}
id={"notifications-label" + daoId}
className="form-control"
data-bs-theme={bootstrapTheme}
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
<div>{groupList}</div>
</Wrapper>
</ThemeContainer>
);
64 changes: 33 additions & 31 deletions apps/builddao/widget/Proposals.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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") || (() => {});

Expand Down Expand Up @@ -113,7 +114,7 @@ const NotificationModal = () => {
daoId,
proposalId: voteDetails.proposalId,
proposer: voteDetails.proposer,
showNotification: false,
showNotification: false
});
setNotificationModal(false);
}}
Expand All @@ -128,7 +129,7 @@ const NotificationModal = () => {
daoId,
proposalId: voteDetails.proposalId,
proposer: voteDetails.proposer,
showNotification: true,
showNotification: true
});
setNotificationModal(false);
}}
Expand Down Expand Up @@ -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({
Expand All @@ -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
});
};

Expand Down Expand Up @@ -302,7 +305,8 @@ const proposalsComponent = useMemo(() => {
handleVote: (data) => {
setVoteDetails(data);
setNotificationModal(true);
},

}
}}
/>
);
Expand All @@ -321,7 +325,7 @@ return (
src="buildhub.near/widget/components.modals.CreateProposal"
props={{
showModal: showProposalModal,
toggleModal: () => setShowModal(!showProposalModal),
toggleModal: () => setShowModal(!showProposalModal)
}}
/>
<Widget
Expand All @@ -338,23 +342,21 @@ return (
toggleModal: () => setFiltersModal(!showFiltersModal),
}}
/>
<Header asChild>
<div className="d-flex align-items-center w-100 justify-content-between">
<h3 className="text-white m-0">Proposals</h3>
<div className="d-flex align-items-center gap-3">
<Button variant="outline" onClick={() => setFiltersModal(true)}>
Filters
</Button>
<Button
variant="primary"
disabled={!context.accountId}
onClick={() => setShowModal(true)}
>
Create Proposal
</Button>
</div>
<div className="d-flex justify-content-between">
<h3 className="text-white">Proposals</h3>
<div className="d-flex gap-3">
<Button variant="outline" onClick={() => setFiltersModal(true)}>
Filters
</Button>
<Button
variant="primary"
disabled={!context.accountId}
onClick={() => setShowModal(true)}
>
Create Proposal
</Button>
</div>
</Header>
</div>
<NotificationModal />
<div className="d-flex flex-column gap-4">{proposalsComponent}</div>
{!proposalId && (
Expand Down
Loading

0 comments on commit a04c38c

Please sign in to comment.