Skip to content

Commit

Permalink
feat: Email notifications (snapshot-labs#3942)
Browse files Browse the repository at this point in the history
* feat: Add subscription form to email report (snapshot-labs#3754)

* feat: add subscribtion form to email report

* feat: add the email notification form to profile page

* feat: show subscription result in modal

* fix(UI): move the whole subscribe logic into modal

* refactor: move the subscription handler into a composable

* feat: require wallet ownership proof

* fix: fix typo

* Update src/locales/default.json

Co-authored-by: Sam <[email protected]>

* fix: remove redundant submit button

* refactor: extract sign function into helper and UI improvement

* fix: fix form input not passing the value

* refactor: remove `timestamp` from data type

* Update src/components/ModalEmailSubscription.vue

Co-authored-by: Dmytro Tolok <[email protected]>

* fix(email modal): remove unnecessary event

* fix(email subscription): add translation to button caption

* fix(helpers:sign): fix signer defining and remove check ignore comment

* fix(eslint): update .eslintrc-auto-import.json file

* fix(text): fix default text for emailSubscription.inputCaption

* fix(text): change texting for success message of email subscription

* feat(email subscription): add subscribe form to the modal after voting

* fix(email subscription): Changed the view of email subscription success modal

* fix(email subscription): change text for success subscription modal

* fix(texting): fix placeholder for email input

Co-authored-by: Sam <[email protected]>

* fix(texting): fix translation for email form

Co-authored-by: Sam <[email protected]>

* fix(UI): improve success message UI

---------

Co-authored-by: Sam <[email protected]>
Co-authored-by: Dmytro Tolok <[email protected]>

* feat(email management): add functionality for managing email subscriptions (snapshot-labs#3907)

* feat: add subscribtion form to email report

* feat: add the email notification form to profile page

* feat: show subscription result in modal

* fix(UI): move the whole subscribe logic into modal

* refactor: move the subscription handler into a composable

* feat: require wallet ownership proof

* fix: fix typo

* Update src/locales/default.json

* fix: remove redundant submit button

* refactor: extract sign function into helper and UI improvement

* fix: fix form input not passing the value

* refactor: remove `timestamp` from data type

* Update src/components/ModalEmailSubscription.vue

* fix(email modal): remove unnecessary event

* fix(email subscription): add translation to button caption

* fix(helpers:sign): fix signer defining and remove check ignore comment

* fix(eslint): update .eslintrc-auto-import.json file

* fix(text): fix default text for emailSubscription.inputCaption

* fix(text): change texting for success message of email subscription

* feat(email subscription): add subscribe form to the modal after voting

* fix(email subscription): Changed the view of email subscription success modal

* fix(email subscription): change text for success subscription modal

* fix(texting): fix placeholder for email input


* fix(texting): fix translation for email form

* fix(UI): improve success message UI

* feat(jsonrpc): add interfaces for JSON-RPC

* refactor(sign): simplified interface for `sign` helper

* refactor(useEmailSubscription): flattify structure

* feat(subscr.managm.): load current user subscriptions on page load

* feat(subscriptions): Add new prop to `useEmailSubscription` composable

* feat(subscr.update): add method for updating subscriptions

* refactor(subscriptions): minor changes to code

* feat(subscr.managm.): add modal

* feat(menu): udated menu to show proper modal

* refactor(subscriptions): replace redundant prop to composable
In scope of this added loading subscriptions right after closing subscription modal

* refactor(change naming): Change name for email management modal

* refactor(subscriptions): add HOC component for defining which modal to show

* feat(signing): change method from regular sign to signing with alias

* refactor(comment): remove old commented code

* feat(translations): add translations

* fix(scaffolds): remove redundat scaffolding code

* refactor(email managm.): remove redundat function wrapper

* fix(CR): fix CR comments

1. Removed $attrs in ModalEmailHOC
2. fixed signing with aliases functionality
3. removed hardcodded email and added comment why we still need empty line
4. removed redundant `}`

* fix(HOC): remove HOC component since it is redundant

* fix(ui-lib): use tune checkbox

* fix(navbar): prevent rendering navbar account component until account is not loaded

* refactor(useEmailSubscription): split composable on multiple for separation of concepts

* fix(types): remove redundant interfaces

* fix(eof): add empty line

* fix(useFetch): remove redundant arguments

* fix(lint): fix lint errors

* fix(switch): change custom switch to ui lib `tune`

* chore(tune): update version

* fix(CR): remove old styles
* fix namings

* fix(CR): remove custom styles

---------

Co-authored-by: Wan Qi Chen <[email protected]>
Co-authored-by: Sam <[email protected]>

* feat(alias revert): revert back sign with alias feature

* feat(subscription status): handle subscriber status from api

* feat(verify modal): add verify modal

* fix(revert): revert BaseMessage success status

* revert(tune): revert redundant style of checkbox

* fix(eslint): remove unused variable

* fix(subscription): add additional check for data

* fix(email management): change the order of options

* fix(fonts): fix fonts for tune sublabel

* fix(packages): upgrade packages after wrong merge

* refactor(html): remove redundant condition for is-disabled prop

* fix(verify email): change title

* chore(submodule): updated submodule

* fix(email): removed masked email from resend modal

* fix(vote): remove subscribe button after vote action if user subscribed

* feat(error handling): add error handling on subscription update request

* feat(notify): add flach notifications on attempt to update email settings

* fix(vote): fix wrong check

* refactor(name): change name of variable

* fix(error modal): remove error modal view on subscribe fail

* chore: fix definition order

* fix: use same text for all subscriptions state

* fix: fix definition order

* fix: fix missing plural

* fix: fix unused variable

---------

Co-authored-by: Wan <[email protected]>
Co-authored-by: Sam <[email protected]>
  • Loading branch information
3 people authored Aug 10, 2023
1 parent 3a392c0 commit 76678e4
Show file tree
Hide file tree
Showing 15 changed files with 543 additions and 61 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
VITE_HUB_URL=https://testnet.snapshot.org
VITE_RELAYER_URL=https://testnet.snapshot.org
VITE_SCORES_URL=https://score.snapshot.org
VITE_ENVELOP_URL=https://core.envelop.fyi
VITE_SIDEKICK_URL=https://sh5.co
VITE_BROVIDER_URL=https://rpc.snapshot.org
VITE_IPFS_GATEWAY=snapshot.mypinata.cloud
Expand Down
2 changes: 2 additions & 0 deletions .eslintrc-auto-import.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@
"watchTxStatus": true,
"toValue": true,
"useFlaggedMessageStatus": true,
"useEmailSubscription": true,
"useEmailFetchClient": true,
"useStatement": true
}
}
40 changes: 39 additions & 1 deletion src/components/MenuAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ const props = defineProps<{
}>();
const emit = defineEmits(['switchWallet']);
const { t } = useI18n();
const { domain } = useApp();
const { logout } = useWeb3();
const router = useRouter();
const { userState, loadEmailSubscriptions } = useEmailSubscription();
const showModalEmail = ref(false);
onMounted(loadEmailSubscriptions);
watch(showModalEmail, () => {
if (!showModalEmail.value) {
loadEmailSubscriptions();
}
});
function handleAction(e) {
if (e === 'viewProfile')
Expand All @@ -19,6 +29,10 @@ function handleAction(e) {
params: { address: props.address }
});
if (e === 'switchWallet') return emit('switchWallet');
if (e === 'subscribeEmail') {
showModalEmail.value = true;
return true;
}
return logout();
}
Expand All @@ -38,6 +52,11 @@ function handleAction(e) {
action: 'switchWallet',
extras: { icon: 'switch' }
},
{
text: t('emailSubscription.title'),
action: 'subscribeEmail',
extras: { icon: 'mail' }
},
{ text: 'Log out', action: 'logout', extras: { icon: 'logout' } }
]"
@select="handleAction($event)"
Expand All @@ -57,6 +76,7 @@ function handleAction(e) {
class="ml-[2px]"
/>
<i-ho-refresh v-if="item.extras.icon === 'switch'" />
<i-ho-mail v-if="item.extras.icon === 'mail'" />
<i-ho-logout
v-if="item.extras.icon === 'logout'"
class="ml-[2px]"
Expand All @@ -69,4 +89,22 @@ function handleAction(e) {
</template>
</BaseMenu>
</div>

<teleport to="#modal">
<ModalEmailSubscription
v-if="userState === 'NOT_SUBSCRIBED'"
:open="showModalEmail"
@close="showModalEmail = false"
/>
<ModalEmailResend
v-else-if="userState === 'UNVERIFIED'"
:open="showModalEmail"
@close="showModalEmail = false"
/>
<ModalEmailManagement
v-else-if="userState === 'VERIFIED'"
:open="showModalEmail"
@close="showModalEmail = false"
/>
</teleport>
</template>
75 changes: 75 additions & 0 deletions src/components/ModalEmailManagement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<script setup lang="ts">
defineProps<{
open: boolean;
}>();
const emit = defineEmits(['close']);
const { loading, error, clientSubscriptions, updateSubscriptions } =
useEmailSubscription();
const { t } = useI18n();
const { notify } = useFlashNotification();
const updateSubscriptionKeys = (key, value) => {
clientSubscriptions.value = { ...clientSubscriptions.value, [key]: value };
};
watchEffect(() => {
if (error.value) {
notify(['red', t('notify.somethingWentWrong')]);
}
});
const submit = async () => {
await updateSubscriptions();
if (error.value) {
error.value = null;
} else {
notify(['green', t('notify.emailPreferencesUpdated')]);
emit('close');
}
};
</script>

<template>
<BaseModal :open="open" max-height="510px" @close="emit('close')">
<template #header>
<div class="flex flex-row items-center justify-center">
<h3>{{ $t('emailManagement.title') }}</h3>
</div>
</template>

<div class="mx-4 mb-4 mt-2 text-center">
<p class="text-sm text-skin-text opacity-60">
{{ t('emailManagement.subtitle') }}
</p>
</div>

<form class="mx-6 my-4 flex flex-col space-y-4" @submit.prevent="submit">
<TuneSwitch
:model-value="clientSubscriptions.summary"
:label="t('emailManagement.optionSummary')"
:sublabel="t('emailManagement.optionSummaryDescription')"
@update:model-value="updateSubscriptionKeys('summary', $event)"
/>

<TuneSwitch
:model-value="clientSubscriptions.newProposal"
:label="t('emailManagement.optionNewProposal')"
:sublabel="t('emailManagement.optionNewProposalDescription')"
@update:model-value="updateSubscriptionKeys('newProposal', $event)"
/>

<TuneSwitch
:model-value="clientSubscriptions.closedProposal"
:label="t('emailManagement.optionClosedProposal')"
:sublabel="t('emailManagement.optionClosedProposalDescription')"
@update:model-value="updateSubscriptionKeys('closedProposal', $event)"
/>

<BaseButton class="mt-6 w-full" primary type="submit" :loading="loading">
{{ t('emailManagement.updatePreferences') }}
</BaseButton>
</form>
</BaseModal>
</template>
23 changes: 23 additions & 0 deletions src/components/ModalEmailResend.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
defineProps<{
open: boolean;
}>();
const emit = defineEmits(['close']);
</script>

<template>
<BaseModal :open="open" @close="emit('close')">
<template #header>
<div class="flex flex-row items-center justify-center">
<h3>{{ $t('emailResend.title') }}</h3>
</div>
</template>
<div class="m-4 mb-6 text-center">
{{ $t('emailResend.description') }}

<BaseButton class="mt-4 w-full" primary @click="emit('close')">
{{ $t('close') }}
</BaseButton>
</div>
</BaseModal>
</template>
101 changes: 101 additions & 0 deletions src/components/ModalEmailSubscription.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
defineProps<{
open: boolean;
}>();
const emit = defineEmits(['close']);
type ModalView = 'SUBSCRIBE' | 'SUCCESS';
const { web3Account } = useWeb3();
const { subscribe, error, loading, loadEmailSubscriptions } =
useEmailSubscription();
const { t } = useI18n();
const { notify } = useFlashNotification();
const email = ref('');
const modalView = ref<ModalView>('SUBSCRIBE');
watchEffect(() => {
if (error.value) {
notify(['red', t('notify.somethingWentWrong')]);
error.value = null;
}
});
function close() {
email.value = '';
emit('close');
modalView.value = 'SUBSCRIBE';
loadEmailSubscriptions();
}
async function submit() {
const isSucceed = await subscribe(email.value, web3Account.value);
if (isSucceed) {
modalView.value = 'SUCCESS';
}
}
</script>

<template>
<BaseModal :open="open" @close="close">
<template #header>
<div class="flex flex-row items-center justify-center">
<h3>{{ $t('emailSubscription.title') }}</h3>
</div>
</template>

<template v-if="modalView === 'SUBSCRIBE'">
<div class="m-4">
{{ $t('emailSubscription.description') }}
</div>

<form class="m-4" @submit.prevent="submit">
<BaseInput
v-model="email"
:placeholder="$t('emailSubscription.inputPlaceholder')"
class="!pl-[40px]"
type="email"
autocomplete="off"
required
focus-on-mount
>
<template #before>
<i-ho-mail class="text-[16px]" />
</template>
</BaseInput>

<small>{{ $t('emailSubscription.inputCaption') }}</small>

<BaseButton
class="mt-3 w-full"
primary
:type="'submit'"
:loading="loading"
>
{{ $t('emailSubscription.subscribe') }}
</BaseButton>
</form>
</template>

<div v-if="modalView === 'SUCCESS'" class="m-4 text-center">
<i-ho-check-circle
class="mx-auto my-4 text-center text-[3em] text-green"
/>
<h3>
{{ $t('emailSubscription.postSubscribeMessage.successThanks') }}
</h3>
<p class="mt-3 italic">
{{ $t('emailSubscription.postSubscribeMessage.successConfirmation') }}
</p>
</div>

<template v-if="modalView === 'SUCCESS'" #footer>
<BaseButton class="w-full" primary @click="close">
{{ $t('close') }}
</BaseButton>
</template>
</BaseModal>
</template>
17 changes: 16 additions & 1 deletion src/components/ModalPostVote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ExtendedSpace, Proposal } from '@/helpers/interfaces';
const { isGnosisSafe } = useClient();
const { shareVote, shareProposalTwitter, shareProposalLenster } = useSharing();
const { web3Account } = useWeb3();
const { userState } = useEmailSubscription();
const props = defineProps<{
open: boolean;
Expand All @@ -13,7 +14,12 @@ const props = defineProps<{
selectedChoices: any;
}>();
const emit = defineEmits(['close']);
const emit = defineEmits(['close', 'subscribeEmail']);
const subscribeEmail = () => {
emit('subscribeEmail');
emit('close');
};
const imgPath = computed(() => {
return isGnosisSafe.value
Expand Down Expand Up @@ -80,6 +86,15 @@ function share(shareTo: 'twitter' | 'lenster') {
{{ $t('shareOnLenster') }}
</BaseButton>

<BaseButton
v-if="userState !== 'VERIFIED'"
class="flex !h-[42px] w-full items-center justify-center gap-2"
@click="subscribeEmail"
>
<i-ho-mail class="text-skin-link" />
{{ $t('proposal.postVoteModal.subscribe') }}
</BaseButton>

<div v-if="isGnosisSafe">
<BaseLink
:link="`https://gnosis-safe.io/app/eth:${web3Account}/transactions/queue`"
Expand Down
2 changes: 1 addition & 1 deletion src/components/NavbarAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ watchEffect(() => {
</script>

<template>
<template v-if="auth.isAuthenticated.value">
<template v-if="auth.isAuthenticated && web3Account">
<MenuAccount :address="web3Account" @switchWallet="modalAccountOpen = true">
<BaseButton
:loading="web3.authLoading || loadingProfiles || reloadingProfile"
Expand Down
7 changes: 7 additions & 0 deletions src/components/SpaceProposalPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const { isMessageVisible, setMessageVisibility } = useFlaggedMessageStatus(
const proposalId: string = route.params.id as string;
const modalOpen = ref(false);
const modalEmailSubscriptionOpen = ref(false);
const selectedChoices = ref<any>(null);
const loadedResults = ref(false);
const results = ref<Results | null>(null);
Expand Down Expand Up @@ -224,6 +225,12 @@ onMounted(() => setMessageVisibility(props.proposal.flagged));
:proposal="proposal"
:selected-choices="selectedChoices"
@close="isModalPostVoteOpen = false"
@subscribeEmail="modalEmailSubscriptionOpen = true"
/>
<ModalEmailSubscription
:open="modalEmailSubscriptionOpen"
:address="web3Account"
@close="modalEmailSubscriptionOpen = false"
/>
</teleport>
</template>
Loading

0 comments on commit 76678e4

Please sign in to comment.