Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add proposal edit #4256

Merged
merged 8 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 131 additions & 129 deletions src/components/SpaceCreateVoting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const props = defineProps<{
dateStart: number;
dateEnd: number;
osnap: { enabled: boolean; selection: boolean };
isEditing: boolean;
}>();

const {
Expand Down Expand Up @@ -54,16 +55,13 @@ watch(
{ immediate: true }
);

// we need to watch for selection change to properly update the voting form stae
watch(
() => props.osnap.selection,
() => {
// If using osnap, we can only allow basic voting, for, against, abstain
if (props.osnap.selection) {
form.value.type = 'basic';
} else {
// Initialize the proposal type if set in space
if (props.space?.voting?.type) form.value.type = props.space.voting.type;
} else if (props.space?.voting?.type) {
form.value.type = props.space.voting.type;
}
}
);
Expand All @@ -85,135 +83,139 @@ defineEmits<{
</script>

<template>
<div v-if="osnap.enabled" class="mb-4">
<div v-if="space?.voting?.type && space.voting.type !== 'basic'">
<h6>Where is oSnap?</h6>
<p class="mb-3">
oSnap is currently disabled because your space's voting settings
disallow the basic voting type which is a requirement for oSnap to work
properly.
</p>
<p>
Have your admin visit your
<a :href="`#/${space.id}/settings`">settings page</a> under Voting ->
Type, and make sure either "Any" or "Basic Voting" is selected. This
will allow you to create oSnap proposals.
</p>
</div>
<div v-else>
<h6>oSnap Proposal</h6>
<p>
Are you planning for this proposal to initiate a transaction that your
organizations Safe will execute if approved? (Remember, oSnap enables
trustless and permissionless execution)
</p>
<br />
<input
id="toggleOsnap"
type="checkbox"
:checked="osnap.selection"
@change="$emit('osnapToggle')"
/>
<label for="toggleOsnap">
Yes, use oSnap for transactions (this will restrict voting type to
Basic).
</label>
<div>
<div v-if="osnap.enabled" class="mb-4">
<div v-if="space?.voting?.type && space.voting.type !== 'basic'">
<h6>Where is oSnap?</h6>
<p class="mb-3">
oSnap is currently disabled because your space's voting settings
disallow the basic voting type which is a requirement for oSnap to
work properly.
</p>
<p>
Have your admin visit your
<a :href="`#/${space.id}/settings`">settings page</a> under Voting ->
Type, and make sure either "Any" or "Basic Voting" is selected. This
will allow you to create oSnap proposals.
</p>
</div>
<div v-else>
<h6>oSnap Proposal</h6>
<p>
Are you planning for this proposal to initiate a transaction that your
organizations Safe will execute if approved? (Remember, oSnap enables
trustless and permissionless execution)
</p>
<br />
<input
id="toggleOsnap"
type="checkbox"
:checked="osnap.selection"
@change="$emit('osnapToggle')"
/>
<label for="toggleOsnap">
Yes, use oSnap for transactions (this will restrict voting type to
Basic).
</label>
</div>
</div>
</div>
<div class="mb-5 space-y-4">
<BaseBlock :title="$t('create.voting')">
<InputSelectVoteType
:type="space.voting?.type || form.type"
:disabled="!!space.voting?.type || osnap.selection"
@update:type="value => (form.type = value)"
/>

<h4 class="mb-1 mt-3" v-text="$t('create.choices')" />
<div class="flex">
<div class="w-full overflow-hidden">
<draggable
v-model="form.choices"
v-bind="{ animation: 200 }"
:disabled="disableChoiceEdit"
item-key="id"
handle=".drag-handle"
class="space-y-2"
>
<div class="mb-5 space-y-4">
<BaseBlock :title="$t('create.voting')">
<InputSelectVoteType
:type="space.voting?.type || form.type"
:disabled="!!space.voting?.type || osnap.selection"
@update:type="value => (form.type = value)"
/>

<h4 class="mb-1 mt-3" v-text="$t('create.choices')" />
<div class="flex">
<div class="w-full overflow-hidden">
<draggable
v-model="form.choices"
v-bind="{ animation: 200 }"
:disabled="disableChoiceEdit"
item-key="id"
handle=".drag-handle"
class="space-y-2"
>
<template #item="{ element, index }">
<UiInput
v-model="element.text"
maxlength="32"
:disabled="disableChoiceEdit"
:placeholder="index > 0 ? $t('optional') : ''"
class="group"
:focus-on-mount="index === 0"
:data-testid="`input-proposal-choice-${index}`"
>
<template #label>
<div
class="drag-handle flex cursor-grab items-center active:cursor-grabbing"
:class="{
'cursor-not-allowed active:cursor-not-allowed':
disableChoiceEdit
}"
>
<BaseIcon name="draggable" size="16" class="mr-[12px]" />
{{ $tc('create.choice', [index + 1]) }}
</div>
</template>
<template #info>
<span
class="hidden text-xs text-skin-text group-focus-within:block"
>
{{ `${element.text.length}/32` }}
</span>
</template>
</UiInput>
</template>
</draggable>
</div>
<div v-if="!disableChoiceEdit" class="ml-2 flex w-[48px] items-end">
<BaseButtonRound
v-if="!disableChoiceEdit"
size="42px"
@click="addChoices(1)"
>
<i-ho-plus-sm class="text-skin-link" />
</BaseButtonRound>
<template #item="{ element, index }">
<UiInput
v-model="element.text"
maxlength="32"
:disabled="disableChoiceEdit"
:placeholder="index > 0 ? $t('optional') : ''"
class="group"
:focus-on-mount="index === 0"
:data-testid="`input-proposal-choice-${index}`"
>
<template #label>
<div
class="drag-handle flex cursor-grab items-center active:cursor-grabbing"
:class="{
'cursor-not-allowed active:cursor-not-allowed':
disableChoiceEdit
}"
>
<BaseIcon name="draggable" size="16" class="mr-[12px]" />
{{ $tc('create.choice', [index + 1]) }}
</div>
</template>
<template #info>
<span
class="hidden text-xs text-skin-text group-focus-within:block"
>
{{ `${element.text.length}/32` }}
</span>
</template>
</UiInput>
</template>
</draggable>
</div>
<div v-if="!disableChoiceEdit" class="ml-2 flex w-[48px] items-end">
<BaseButtonRound
v-if="!disableChoiceEdit"
size="42px"
@click="addChoices(1)"
>
<i-ho-plus-sm class="text-skin-link" />
</BaseButtonRound>
</div>
</div>
</div>
</BaseBlock>

<BaseBlock
:title="$t('create.period')"
:information="$t('create.votingPeriodExplainer')"
>
<div class="space-y-2 md:flex md:space-x-3 md:space-y-0">
<SpaceCreateVotingDateStart
:delay="space.voting?.delay"
:date="dateStart"
@select="value => setDateStart(value)"
/>

<SpaceCreateVotingDateEnd
:period="space.voting?.period"
:date="dateEnd"
@select="value => setDateEnd(value)"
/>
</div>
</BaseBlock>
</BaseBlock>

<BaseBlock v-if="$route.query.snapshot" :title="$t('snapshot')">
<UiInput
v-model="form.snapshot"
:number="true"
:placeholder="$t('create.snapshotBlock')"
<BaseBlock
:title="$t('create.period')"
:information="$t('create.votingPeriodExplainer')"
>
<template #label>
{{ $t('snapshot') }}
</template>
</UiInput>
</BaseBlock>
<div class="space-y-2 md:flex md:space-x-3 md:space-y-0">
<SpaceCreateVotingDateStart
:delay="space.voting?.delay"
:date="dateStart"
:disabled="isEditing"
@select="value => setDateStart(value)"
/>

<SpaceCreateVotingDateEnd
:period="space.voting?.period"
:date="dateEnd"
:disabled="isEditing"
@select="value => setDateEnd(value)"
/>
</div>
</BaseBlock>

<BaseBlock v-if="$route.query.snapshot" :title="$t('snapshot')">
<UiInput
v-model="form.snapshot"
:number="true"
:placeholder="$t('create.snapshotBlock')"
>
<template #label>
{{ $t('snapshot') }}
</template>
</UiInput>
</BaseBlock>
</div>
</div>
</template>
18 changes: 12 additions & 6 deletions src/components/SpaceProposalHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ const { web3Account } = useWeb3();
const isCreator = computed(() => props.proposal?.author === web3Account.value);

const threeDotItems = computed(() => {
const items = [
{ text: t('duplicate'), action: 'duplicate' },
];
const items: { text: string; action: string }[] = [];
if (isCreator.value && props.proposal.state === 'pending')
items.push({ text: t('edit'), action: 'edit' });
items.push({ text: t('duplicate'), action: 'duplicate' });

if ((props.isAdmin || props.isModerator) && !props.proposal.flagged) {
items.push({ text: t('flag'), action: 'flag' });
} else {
Expand Down Expand Up @@ -64,14 +66,15 @@ async function handleSelect(e) {
proposal: props.proposal
});
}
if (e === 'duplicate') {
if (e === 'duplicate' || e === 'edit') {
resetForm();
router.push({
name: 'spaceCreate',
params: {
key: props.proposal.space.id,
sourceProposal: props.proposal.id
}
},
query: { editing: e === 'edit' ? 'true' : undefined }
});
}
}
Expand Down Expand Up @@ -158,8 +161,11 @@ watch(
</template>
<template #item="{ item }">
<div class="flex items-center gap-2">
<i-ho-pencil v-if="item.action === 'edit'" />
<i-ho-document-duplicate v-if="item.action === 'duplicate'" />
<i-ho-flag v-if="item.action === 'report' || item.action === 'flag'" />
<i-ho-flag
v-if="item.action === 'report' || item.action === 'flag'"
/>
<i-ho-trash v-if="item.action === 'delete'" />
{{ item.text }}
</div>
Expand Down
22 changes: 18 additions & 4 deletions src/composables/useClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ export function useClient() {
}

async function sendEIP712(space: { id: string }, type: string, payload: any) {
let plugins = {};
if (
payload.metadata?.plugins &&
Object.keys(payload.metadata?.plugins).length !== 0
)
plugins = payload.metadata.plugins;
const client = clientEIP712;
if (type === 'proposal') {
let plugins = {};
if (Object.keys(payload.metadata?.plugins).length !== 0)
plugins = payload.metadata.plugins;
if (type === 'create-proposal') {
return client.proposal(auth.web3, web3.value.account, {
space: space.id,
type: payload.type,
Expand All @@ -53,6 +56,17 @@ export function useClient() {
plugins: JSON.stringify(plugins),
app: DEFINED_APP
});
} else if (type === 'update-proposal') {
return client.updateProposal(auth.web3, web3.value.account, {
proposal: payload.id,
space: space.id,
type: payload.type,
title: payload.name,
body: payload.body,
discussion: payload.discussion,
choices: payload.choices,
plugins: JSON.stringify(plugins)
});
} else if (type === 'vote') {
return client.vote(auth.web3, web3.value.account, {
space: space.id,
Expand Down
2 changes: 1 addition & 1 deletion src/composables/useFormSpaceProposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function useFormSpaceProposal() {
isBodySet: boolean;
}>(`snapshot.proposal.${route.params.key}`, clone(EMPTY_PROPOSAL_DRAFT));

const sourceProposal = computed(() => route.params.sourceProposal);
const sourceProposal = computed(() => route.params.sourceProposal as string);

function resetForm() {
formDraft.value = clone(EMPTY_PROPOSAL_DRAFT);
Expand Down
1 change: 1 addition & 0 deletions src/locales/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@
"delegationSuccess": "Delegation successful",
"delegationRemoved": "Delegation removed",
"proposalCreated": "Proposal created",
"proposalUpdated": "Proposal updated",
"waitingForOtherSigners": "Waiting for other signers",
"voteSuccessful": "Your vote is in!",
"ensSet": "ENS text record was successfully set",
Expand Down
Loading