Skip to content

Commit

Permalink
Merge branch 'master' into safesnap_connext_integration
Browse files Browse the repository at this point in the history
  • Loading branch information
samuveth authored Nov 29, 2023
2 parents a7870af + 6c46fb3 commit f1b8b4e
Show file tree
Hide file tree
Showing 59 changed files with 6,210 additions and 179 deletions.
2 changes: 1 addition & 1 deletion src/components/BaseModalSelectItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defineProps<{
/>
<BasePill>{{ tag }}</BasePill>
</div>
<span class="text-skin-text" v-text="description" />
<span class="break-all text-skin-text" v-text="description" />
</div>
<i-ho-check v-if="selected" class="absolute right-0 text-md" />
</div>
Expand Down
62 changes: 62 additions & 0 deletions src/components/ModalOsnap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script setup lang="ts">
import { TreasuryWallet } from '@/helpers/interfaces';
import { Network } from '@/plugins/oSnap/types';
import { makeConfigureOsnapUrl } from '@/plugins/oSnap/utils/getters';
const props = defineProps<{
open: boolean;
isOsnapEnabled: boolean;
treasury: TreasuryWallet;
spaceName: string;
}>();
defineEmits<{
(e: 'close'): void;
}>();
const spaceUrl = window.location.href.replace('/settings', '');
const href = computed(() =>
makeConfigureOsnapUrl({
spaceUrl,
spaceName: props.spaceName,
safeAddress: props.treasury.address,
network: props.treasury.network as Network
})
);
</script>

<template>
<BaseModal :open="open" @close="$emit('close')">
<template #header>
<h3 v-text="'Configure oSnap'" />
</template>
<div class="m-4 grid grid-cols-[auto,auto] gap-2">
<i-ho-information-circle class="mt-1 text-sm" />
<p class="text-sm">
oSnap seamlessly integrates with Snapshot and your treasury,
automatically executing governance votes on-chain. Bypass the need for
privileged signers to create a DAO that's more efficient and truly
decentralized.
<BaseLink class="mt-1 block text-skin-link" link="https://uma.xyz/osnap"
>Learn more</BaseLink
>
</p>
</div>

<template #footer>
<a
:href="href"
target="_blank"
class="block w-full rounded-full py-[12px] text-white"
:class="
isOsnapEnabled ? 'bg-[hsl(349,65%,52%)]' : 'bg-[hsl(240,83%,58%)]'
"
@click.stop="$emit('close')"
>
{{ isOsnapEnabled ? 'Deactivate' : 'Activate' }} oSnap
<i-ho-external-link class="mb-[2px] ml-1 inline-block text-xs" />
</a>
<p class="mt-2 text-xs text-skin-text">
Note that the deactivation process takes place in the Safe app
</p>
</template>
</BaseModal>
</template>
3 changes: 3 additions & 0 deletions src/components/SettingsPluginsBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const modalPluginsOpen = ref(false);
function handleEditPlugins(name: string) {
if (props.isViewOnly) return;
// the oSnap plugin does not require any configuration
// so we don't need to open the modal
if (name === 'oSnap') return;
currentPlugin.value = {};
currentPlugin.value[name] = clone(form.value.plugins[name]);
modalPluginsOpen.value = true;
Expand Down
50 changes: 46 additions & 4 deletions src/components/SettingsTreasuriesBlock.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { ExtendedSpace, TreasuryWallet } from '@/helpers/interfaces';
import { clone } from '@snapshot-labs/snapshot.js/src/utils';
import { TreasuryWallet } from '@/helpers/interfaces';
const props = defineProps<{
context: 'setup' | 'settings';
space: ExtendedSpace;
error: string | Record<string, any>;
isViewOnly?: boolean;
}>();
Expand All @@ -17,16 +18,21 @@ const treasuryObj = {
};
const modalTreasuryOpen = ref(false);
const modalOsnapOpen = ref(false);
const currentTreasuryIndex = ref<number | null>(null);
const currentTreasury = ref<TreasuryWallet>(clone(treasuryObj));
const hasOsnapPlugin = computed(() => {
return Object.keys(props.space.plugins).includes('oSnap');
});
const isOsnapEnabledOnCurrentTreasury = ref(false);
function handleRemoveTreasury(i) {
function handleRemoveTreasury(i: number) {
form.value.treasuries = form.value.treasuries.filter(
(treasury, index) => index !== i
);
}
function handleEditTreasury(i) {
function handleEditTreasury(i: number) {
if (props.isViewOnly) return;
currentTreasuryIndex.value = i;
currentTreasury.value = clone(form.value.treasuries[i]);
Expand All @@ -39,7 +45,7 @@ function handleAddTreasury() {
modalTreasuryOpen.value = true;
}
function handleSubmitTreasury(treasury) {
function handleSubmitTreasury(treasury: TreasuryWallet) {
if (currentTreasuryIndex.value !== null) {
const treasuriesClone = clone(form.value.treasuries);
treasuriesClone[currentTreasuryIndex.value] = treasury;
Expand All @@ -48,16 +54,44 @@ function handleSubmitTreasury(treasury) {
form.value.treasuries = form.value.treasuries.concat(treasury);
}
}
function handleOpenConfigureOsnapModal(
treasuryIndex: number,
isEnabled: boolean
) {
if (props.isViewOnly || !hasOsnapPlugin.value) return;
currentTreasuryIndex.value = treasuryIndex;
currentTreasury.value = clone(form.value.treasuries[treasuryIndex]);
isOsnapEnabledOnCurrentTreasury.value = isEnabled;
modalOsnapOpen.value = true;
}
function handleCloseConfigureOsnapModal() {
modalOsnapOpen.value = false;
isOsnapEnabledOnCurrentTreasury.value = false;
}
</script>

<template>
<BaseBlock :title="$t('settings.treasuries.label')">
<div v-if="hasOsnapPlugin && form.treasuries.length === 0" class="mb-3">
<h2>Warning: no treasuries</h2>
<p>
You have installed the oSnap plugin, but you don't have any treasuries.
</p>
<p>
Please add a Safe as a treasury and enable oSnap on it to use the oSnap
plugin.
</p>
</div>
<div v-if="form.treasuries.length" class="mb-3 grid gap-3">
<SettingsTreasuriesBlockItem
:treasuries="form.treasuries"
:is-view-only="isViewOnly"
:has-osnap-plugin="hasOsnapPlugin"
@edit-treasury="i => handleEditTreasury(i)"
@remove-treasury="i => handleRemoveTreasury(i)"
@configure-osnap="handleOpenConfigureOsnapModal"
/>
</div>

Expand All @@ -78,6 +112,14 @@ function handleSubmitTreasury(treasury) {
@close="modalTreasuryOpen = false"
@add="handleSubmitTreasury"
/>
<ModalOsnap
v-if="hasOsnapPlugin"
:open="modalOsnapOpen"
:treasury="currentTreasury"
:space-name="form.name"
:is-osnap-enabled="isOsnapEnabledOnCurrentTreasury"
@close="handleCloseConfigureOsnapModal"
/>
</teleport>
</BaseBlock>
</template>
36 changes: 15 additions & 21 deletions src/components/SettingsTreasuriesBlockItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,26 @@ import { TreasuryWallet } from '@/helpers/interfaces';
defineProps<{
treasuries: TreasuryWallet[];
isViewOnly?: boolean;
hasOsnapPlugin: boolean;
}>();
const emit = defineEmits(['removeTreasury', 'editTreasury']);
const emit = defineEmits<{
removeTreasury: [index: number];
editTreasury: [index: number];
configureOsnap: [index: number, isEnabled: boolean];
}>();
</script>

<template>
<div
<SettingsTreasuriesBlockItemButton
v-for="(treasury, i) in treasuries"
:key="i"
class="flex h-full truncate"
@click="emit('editTreasury', i)"
>
<button
class="flex w-full items-center justify-between rounded-md border p-4"
:class="{ 'cursor-default': isViewOnly }"
>
<div class="flex items-center gap-2 truncate pr-[20px] text-left">
<h4 class="truncate">{{ treasury.name }}</h4>
</div>
<BaseButtonIcon
v-show="!isViewOnly"
class="-mr-2"
@click.stop="emit('removeTreasury', i)"
>
<BaseIcon name="close" size="14" />
</BaseButtonIcon>
</button>
</div>
:treasury="treasury"
:treasury-index="i"
:is-view-only="!!isViewOnly"
:has-osnap-plugin="hasOsnapPlugin"
@edit-treasury="i => emit('editTreasury', i)"
@remove-treasury="i => emit('removeTreasury', i)"
@configure-osnap="(i, isEnabled) => emit('configureOsnap', i, isEnabled)"
/>
</template>
65 changes: 65 additions & 0 deletions src/components/SettingsTreasuriesBlockItemButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { TreasuryWallet } from '@/helpers/interfaces';
import { Network } from '@/plugins/oSnap/types';
import { getIsOsnapEnabled } from '@/plugins/oSnap/utils/getters';
const props = defineProps<{
treasury: TreasuryWallet;
treasuryIndex: number;
isViewOnly?: boolean;
hasOsnapPlugin: boolean;
}>();
const emit = defineEmits<{
removeTreasury: [index: number];
editTreasury: [index: number];
configureOsnap: [index: number, isEnabled: boolean];
}>();
const isOsnapEnabled = ref(false);
async function updateIsOsnapEnabled() {
if (!props.hasOsnapPlugin) return;
isOsnapEnabled.value = await getIsOsnapEnabled(
props.treasury.network as Network,
props.treasury.address
);
}
onMounted(async () => {
await updateIsOsnapEnabled();
window.addEventListener('focus', updateIsOsnapEnabled);
});
onUnmounted(() => {
window.removeEventListener('focus', updateIsOsnapEnabled);
});
</script>

<template>
<button
class="flex h-full w-full items-center justify-between truncate rounded-md border p-4"
:class="{ 'cursor-default': isViewOnly }"
@click="emit('editTreasury', treasuryIndex)"
>
<div class="flex items-center gap-2 truncate pr-[20px] text-left">
<h4 class="truncate">{{ treasury.name }}</h4>
</div>
<div class="ml-auto mr-3">
<SettingsTreasuryActivateOsnapButton
v-show="hasOsnapPlugin"
:is-osnap-enabled="isOsnapEnabled"
@click.stop="
!isViewOnly && emit('configureOsnap', treasuryIndex, isOsnapEnabled)
"
/>
</div>
<BaseButtonIcon
v-show="!isViewOnly"
class="-mr-2"
@click.stop="emit('removeTreasury', treasuryIndex)"
>
<BaseIcon name="close" size="14" />
</BaseButtonIcon>
</button>
</template>
21 changes: 21 additions & 0 deletions src/components/SettingsTreasuryActivateOsnapButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
defineProps<{
isOsnapEnabled: boolean;
}>();
</script>

<template>
<button
v-if="isOsnapEnabled"
class="flex items-center gap-2 rounded-full px-3 py-2 bg-skin-primary text-white"
>
<span class="block h-[6px] w-[6px] rounded-full bg-green" />oSnap activated
</button>
<button
v-else
class="bg-skin-link text-skin-bg flex items-center gap-2 rounded-full px-3 py-2"
>
<span class="block h-[6px] w-[6px] rounded-full bg-skin-bg opacity-30" />
Activate oSnap
</button>
</template>
2 changes: 1 addition & 1 deletion src/components/SettingsVotingBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const { form } = useFormSpaceSettings(props.context);
:label="$t('settings.quorum.label')"
:hint="$t('settings.quorum.information')"
:disabled="isViewOnly"
placeholder="1000"
placeholder="0"
type="number"
@update:model-value="value => (form.voting.quorum = Number(value))"
/>
Expand Down
50 changes: 50 additions & 0 deletions src/components/SpaceCreateLegacyOsnap.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { ExtendedSpace } from '@/helpers/interfaces';
defineProps<{
space: ExtendedSpace | undefined;
legacyOsnap: { selection: boolean };
}>();
defineEmits<{
(event: 'legacyOsnapToggle'): void;
}>();
</script>

<template>
<div 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="legacyOsnap.selection"
@change="$emit('legacyOsnapToggle')"
/>
<label for="toggleOsnap">
Yes, use oSnap for transactions (this will restrict voting type to
Basic).
</label>
</div>
</div>
</template>
Loading

0 comments on commit f1b8b4e

Please sign in to comment.