Skip to content

Commit

Permalink
feat: enable vote casting form again on offchain active proposal (#514)
Browse files Browse the repository at this point in the history
* feat: enable vote casting form again on offchain active proposal

* fix: show vp and correct title on edit vote mode

* fix: revert message

* fix: pre-populate vote form with current choice

* refactor: import types as type

* fix: exit editMode after vote

* fix: avoid latency when filling votes

* fix(ux): make voting power modal accessible on keyboard

* fix: set default props value

* fix: avoid vote choice flashing

* fix: add back pending vote message for onchain

* fix: fix votes flashing

* fix(ui): remove uneeded classes

* refactor: better namimg

* fix: do not pre-populate form on shielded voting

* fix lint

* fix: use camelCase for event name

* fix: revert changes fixed in another PR

* refactor: improve variable name

* refactor: remove unused props

* refactor: revert plural changes

* fix: fix invalid computed property reading

* refactor: avoid usage of `!`

* fix: add disabled to state to button when not editable

* fix: avoid intermediate message flashing

---------

Co-authored-by: ChaituVR <[email protected]>
  • Loading branch information
wa0x6e and ChaituVR authored Aug 1, 2024
1 parent 405fb4f commit 62e045c
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 25 deletions.
65 changes: 55 additions & 10 deletions apps/ui/src/components/ProposalVote.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
<script setup lang="ts">
import { SUPPORTED_VOTING_TYPES } from '@/helpers/constants';
import { _t } from '@/helpers/utils';
import { getNetwork } from '@/networks';
import { _t, getChoiceText } from '@/helpers/utils';
import { getNetwork, offchainNetworks } from '@/networks';
import { Proposal as ProposalType } from '@/types';
const props = defineProps<{ proposal: ProposalType }>();
const props = withDefaults(
defineProps<{ proposal: ProposalType; editMode?: boolean }>(),
{
editMode: false
}
);
defineEmits<{
(e: 'enterEditMode');
}>();
const { votes, pendingVotes } = useAccount();
const { getTsFromCurrent } = useMetaStore();
Expand All @@ -31,20 +40,56 @@ const isSupported = computed(() => {
SUPPORTED_VOTING_TYPES.includes(props.proposal.type)
);
});
const currentVote = computed(
() => votes.value[`${props.proposal.network}:${props.proposal.id}`]
);
const isEditable = computed(() => {
return (
currentVote.value &&
offchainNetworks.includes(props.proposal.network) &&
props.proposal.state === 'active'
);
});
</script>

<template>
<slot v-if="currentVote && !editMode" name="voted" :vote="currentVote">
<div class="py-2">
<UiButton
class="!h-[48px] text-left w-full flex items-center rounded-lg space-x-2"
:disabled="!isEditable"
@click="$emit('enterEditMode')"
>
<div
v-if="proposal.privacy"
class="flex space-x-2 items-center grow truncate"
:class="{ 'text-skin-text': !isEditable }"
>
<IH-lock-closed class="size-[16px] shrink-0" />
<span class="truncate">Encrypted choice</span>
</div>
<div
v-else
class="grow truncate"
:class="{ 'text-skin-text': !isEditable }"
v-text="getChoiceText(proposal.choices, currentVote.choice)"
/>
<IH-pencil v-if="isEditable" class="shrink-0" />
</UiButton>
</div>
</slot>
<slot
v-if="votes[`${proposal.network}:${proposal.id}`]"
name="voted"
:vote="votes[`${proposal.network}:${proposal.id}`]"
v-else-if="
!isEditable &&
pendingVotes[proposal.id] &&
!offchainNetworks.includes(props.proposal.network)
"
name="voted-pending"
>
You have already voted for this proposal
</slot>

<slot v-else-if="pendingVotes[proposal.id]" name="voted-pending">
You have already voted for this proposal
</slot>
<slot v-else-if="proposal.state === 'pending'" name="waiting">
Voting for this proposal hasn't started yet. Voting will start
{{ _t(start) }}.
Expand Down
9 changes: 7 additions & 2 deletions apps/ui/src/components/ProposalVoteApproval.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
<script setup lang="ts">
import { Choice, Proposal } from '@/types';
defineProps<{
type ApprovalChoice = number[];
const props = defineProps<{
sendingType: Choice | null;
proposal: Proposal;
defaultChoice?: Choice;
}>();
const emit = defineEmits<{
(e: 'vote', value: Choice);
}>();
const selectedChoices = ref<number[]>([]);
const selectedChoices = ref<ApprovalChoice>(
(!props.proposal.privacy && (props.defaultChoice as ApprovalChoice)) || []
);
function toggleSelectedChoice(choice: number) {
if (selectedChoices.value.includes(choice)) {
Expand Down
8 changes: 6 additions & 2 deletions apps/ui/src/components/ProposalVoteRankedChoice.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
import Draggable from 'vuedraggable';
import { Choice, Proposal } from '@/types';
type RankedChoice = number[];
const props = defineProps<{
sendingType: Choice | null;
proposal: Proposal;
defaultChoice?: Choice;
}>();
const emit = defineEmits<{
(e: 'vote', value: Choice);
}>();
const selectedChoices = ref<number[]>(
props.proposal.choices.map((_, i) => i + 1)
const selectedChoices = ref<RankedChoice>(
(!props.proposal.privacy && (props.defaultChoice as RankedChoice)) ||
props.proposal.choices.map((_, i) => i + 1)
);
</script>

Expand Down
7 changes: 5 additions & 2 deletions apps/ui/src/components/ProposalVoteSingleChoice.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
<script setup lang="ts">
import { Choice, Proposal } from '@/types';
defineProps<{
const props = defineProps<{
sendingType: Choice | null;
proposal: Proposal;
defaultChoice?: Choice;
}>();
const emit = defineEmits<{
(e: 'vote', value: number);
}>();
const selectedChoice = ref<number | null>(null);
const selectedChoice = ref<number | null>(
(!props.proposal.privacy && (props.defaultChoice as number)) || null
);
</script>

<template>
Expand Down
9 changes: 7 additions & 2 deletions apps/ui/src/components/ProposalVoteWeighted.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@
import { _p, getChoiceWeight } from '@/helpers/utils';
import { Choice, Proposal } from '@/types';
defineProps<{
type WeightedChoice = Record<string, number>;
const props = defineProps<{
sendingType: Choice | null;
proposal: Proposal;
defaultChoice?: Choice;
}>();
defineEmits<{
(e: 'vote', value: Choice);
}>();
const selectedChoices = ref<Record<string, number>>({});
const selectedChoices = ref<WeightedChoice>(
(!props.proposal.privacy && (props.defaultChoice as WeightedChoice)) || {}
);
function increaseChoice(index: number) {
selectedChoices.value[index] ||= 0;
Expand Down
5 changes: 4 additions & 1 deletion apps/ui/src/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,10 @@ export function getChoiceText(
}

if (Array.isArray(choice)) {
return choice.map(index => availableChoices[index - 1]).join(', ');
return (
choice.map(index => availableChoices[index - 1]).join(', ') ||
'Blank vote'
);
}

const total = Object.values(choice).reduce((acc, weight) => acc + weight, 0);
Expand Down
40 changes: 34 additions & 6 deletions apps/ui/src/views/Proposal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ const { resolved, address: spaceAddress, networkId } = useResolve(param);
const { setTitle } = useTitle();
const proposalsStore = useProposalsStore();
const { web3 } = useWeb3();
const { loadVotes } = useAccount();
const { loadVotes, votes } = useAccount();
const { vote } = useActions();
const sendingType = ref<Choice | null>(null);
const votingPowers = ref([] as VotingPower[]);
const votingPowerStatus = ref<VotingPowerStatus>('loading');
const votingPowerDetailsError =
ref<utils.errors.VotingPowerDetailsError | null>(null);
const editMode = ref(false);
const network = computed(() =>
networkId.value ? getNetwork(networkId.value) : null
Expand Down Expand Up @@ -53,6 +54,12 @@ const votingPowerDecimals = computed(() => {
);
});
const currentVote = computed(
() =>
proposal.value &&
votes.value[`${proposal.value.network}:${proposal.value.id}`]
);
async function getVotingPower() {
if (!network.value) return;
Expand Down Expand Up @@ -104,9 +111,11 @@ async function handleVoteClick(choice: Choice) {
id.value,
networkId.value!
);
await loadVotes(proposal.value.network, [proposal.value.space.id]);
}
} finally {
sendingType.value = null;
editMode.value = false;
}
}
Expand Down Expand Up @@ -196,12 +205,22 @@ watchEffect(() => {
['pending', 'active'].includes(proposal.state)
"
>
<h4 class="mb-2 eyebrow flex items-center">
<IH-cursor-click class="inline-block mr-2" />
<span>Cast your vote</span>
<h4 class="mb-2 eyebrow flex items-center space-x-2">
<template v-if="editMode">
<IH-cursor-click />
<span>Edit your vote</span>
</template>
<template v-else-if="currentVote">
<IH-check-circle />
<span>Your vote</span>
</template>
<template v-else>
<IH-cursor-click />
<span>Cast your vote</span>
</template>
</h4>
<IndicatorVotingPower
v-if="web3.account && networkId"
v-if="web3.account && networkId && (!currentVote || editMode)"
v-slot="props"
:network-id="networkId"
:status="votingPowerStatus"
Expand Down Expand Up @@ -251,7 +270,12 @@ watchEffect(() => {
</a>
</template>
</IndicatorVotingPower>
<ProposalVote v-if="proposal" :proposal="proposal">
<ProposalVote
v-if="proposal"
:proposal="proposal"
:edit-mode="editMode"
@enter-edit-mode="editMode = true"
>
<ProposalVoteBasic
v-if="proposal.type === 'basic'"
:sending-type="sendingType"
Expand All @@ -261,24 +285,28 @@ watchEffect(() => {
v-else-if="proposal.type === 'single-choice'"
:proposal="proposal"
:sending-type="sendingType"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteApproval
v-else-if="proposal.type === 'approval'"
:proposal="proposal"
:sending-type="sendingType"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteRankedChoice
v-else-if="proposal.type === 'ranked-choice'"
:proposal="proposal"
:sending-type="sendingType"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
<ProposalVoteWeighted
v-else-if="['weighted', 'quadratic'].includes(proposal.type)"
:proposal="proposal"
:sending-type="sendingType"
:default-choice="currentVote?.choice"
@vote="handleVoteClick"
/>
</ProposalVote>
Expand Down

0 comments on commit 62e045c

Please sign in to comment.