-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: email notifications feature (#1012)
* feat: add newsletter widget on profile * feat: add popup to configure newsletter * style: add darkmode and responsive to newsletter components * feat: add notification page in settings to manage all newsletters * feat: added authors list to newsletter settings * feat: connect newsletter popup to authors in notifications settings page * fix: rebuild newsletter process with different UI * feat: add new email address section in newsletter popup * feat: added list of emails, create subscription, list authors (#1114) * feat: added list of emails, create subscription, list authors * feat: ui binding for manage all newsletters * feat: error handling in email ui * feat: refetching email subs on create * style: polish newsletter popup * fix: refetch newsletter on delete * style: polish status and delete alert * fix: newsletter type * fix: hide newsletter widget when unauth * style: change newsletter into notification * fix: add missing popup portal to settings layout * feat: upload static images used in emails (#1142) * ci: add cure53 deploy * fix: add cure53 ipfs addr to bootstrap nodes * fix: updated socials interface and type (#1169) * chore: upgrade near-api-js to v1.0.0 (#1155) * fix: prevent duplicate posts and clearing of editor (#1170) * chore: remove cure53 bootstrap node Co-authored-by: Irshad PI <[email protected]> Co-authored-by: Irshad PI <[email protected]> Co-authored-by: jack dishman <[email protected]> Co-authored-by: David Grisham <[email protected]> Co-authored-by: Rahul Trivedi <[email protected]> Co-authored-by: Christos Panagiotakopoulos <[email protected]>
- Loading branch information
1 parent
253eb47
commit 6f0b131
Showing
14 changed files
with
1,111 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
CONTRACT_NAME=dev-1657702549987-47675900699610 | ||
HCAPTCHA_SITE_KEY="7e68122f-db5e-4100-bdab-0240658070d9" | ||
|
||
CAPSULE_SERVER=https://cure53.blogchain.app/server | ||
DOMAIN=https://cure53.blogchain.app | ||
STRIPE_PUBLISHABLE_KEY="pk_test_51I81pBCPCJ3FaYLGnUrPUMxipudV7gWWA7qAiqIVMAqnULA4a2uluUgBQxX8yKzAe2iGYOoSMX2rSbF45wtKlhXI00Olk8hJmc" | ||
|
||
ORBIT_URL=https://cure53.blogchain.app/orbit | ||
DEBUG=true | ||
|
||
# Discord | ||
TORUS_DISCORD_VERIFIER="audit-verifier-august-discord" | ||
TORUS_DISCORD_CLIENTID="906210984396468275" | ||
|
||
TORUS_GOOGLE_VERIFIER="audit-verifier-august" | ||
TORUS_GOOGLE_CLIENTID="653379121360-j8t9ua763vfvd86d1qjguonhrgqvkigo.apps.googleusercontent.com" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { AxiosError } from 'axios' | ||
import { genericRequest } from './utilities/request' | ||
|
||
export enum EmailSubscriptionMode { | ||
AllPosts = `AllPosts`, | ||
Topics = `Topics`, | ||
} | ||
|
||
export interface IEmailSubscription { | ||
_id?: string | ||
username?: string | ||
authorID: string | ||
email: string | ||
verified: boolean | ||
createdAt: Date | ||
updatedAt: Date | ||
topics: string[] | ||
mode: EmailSubscriptionMode | ||
} | ||
|
||
export interface UserEmail { | ||
email: string | ||
authorSubbed: boolean | ||
} | ||
|
||
export async function startEmailSubscription( | ||
authorID: string, | ||
email: string, | ||
topics: string[] = [], | ||
mode: EmailSubscriptionMode = EmailSubscriptionMode.AllPosts, | ||
username?: string, | ||
) { | ||
try { | ||
const body = { | ||
authorID, | ||
email, | ||
topics, | ||
mode, | ||
} | ||
const response = await genericRequest({ | ||
method: `post`, | ||
path: `/emails/auth/subscribe`, | ||
username, | ||
body, | ||
}) | ||
return response | ||
} catch (err) { | ||
if (err instanceof AxiosError && err.response) { | ||
throw new Error(err.response.data?.error ?? err.message) | ||
} | ||
throw new Error(`Network error: ${err}`) | ||
} | ||
} | ||
|
||
export async function listAllAuthors(username: string) { | ||
try { | ||
const response = await genericRequest<{ data: Array<string> }>({ | ||
method: `get`, | ||
path: `/emails/authors`, | ||
username, | ||
}) | ||
return response.data | ||
} catch (err) { | ||
if (err instanceof AxiosError && err.response) { | ||
throw new Error(err.response.data?.error ?? err.message) | ||
} | ||
throw new Error(`Network error: ${err}`) | ||
} | ||
} | ||
|
||
export async function listForAuthor(authorID: string, username: string) { | ||
try { | ||
const response = await genericRequest<{ authorID: string }, { data: Array<IEmailSubscription> }>({ | ||
method: `get`, | ||
path: `/emails/subscribed`, | ||
params: { authorID }, | ||
username, | ||
}) | ||
return response.data | ||
} catch (err) { | ||
if (err instanceof AxiosError && err.response) { | ||
throw new Error(err.response.data?.error ?? err.message) | ||
} | ||
throw new Error(`Network error: ${err}`) | ||
} | ||
} | ||
|
||
export async function listEmails(username: string, authorID: string) { | ||
try { | ||
const response = await genericRequest<{ authorID: string }, { data: Array<UserEmail> }>({ | ||
method: `get`, | ||
path: `/emails/list`, | ||
username, | ||
params: { authorID }, | ||
}) | ||
return response.data | ||
} catch (err) { | ||
if (err instanceof AxiosError && err.response) { | ||
throw new Error(err.response.data?.error ?? err.message) | ||
} | ||
throw new Error(`Network error: ${err}`) | ||
} | ||
} | ||
|
||
export async function deleteSubscription(username: string, id: string) { | ||
try { | ||
const response = await genericRequest({ | ||
method: `delete`, | ||
path: `/emails/${id}`, | ||
username, | ||
}) | ||
return response.data | ||
} catch (err) { | ||
if (err instanceof AxiosError && err.response) { | ||
throw new Error(err.response.data?.error ?? err.message) | ||
} | ||
throw new Error(`Network error: ${err}`) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
<template> | ||
<div class="my-2 flex flex-row items-center justify-between"> | ||
<!-- newsletter email --> | ||
<p class="flex items-center text-primary text-sm focus:outline-none mb-2"> | ||
{{ newsletter.email }} | ||
</p> | ||
<div class="flex items-center relative"> | ||
<div class=""> | ||
<span | ||
class="ml-1 flex h-3 w-3 modal-animation mr-4" | ||
@mouseenter="showStatus = true" | ||
@mouseleave="showStatus = false" | ||
> | ||
<span | ||
class="absolute inline-flex h-3 w-3 animate-ping rounded-full opacity-75" | ||
:class="newsletter.verified ? `bg-positive` : `bg-neutral`" | ||
></span> | ||
<span | ||
class="relative inline-flex h-3 w-3 rounded-full" | ||
:class="newsletter.verified ? `bg-positive` : `bg-neutral`" | ||
></span> | ||
</span> | ||
<!-- Info hover --> | ||
<div | ||
v-show="showStatus" | ||
class="absolute w-max z-10 border-lightBorder modal-animation rounded-lg border bg-lightBG dark:bg-gray7 p-2 pr-4 shadow-lg text-gray5 dark:text-gray1 self-center text-xs" | ||
:class="$colorMode.dark ? `EmailInfoOpenDark` : `EmailInfoOpen`" | ||
style="top: -5px; right: 80px" | ||
> | ||
{{ newsletter.verified ? 'Notification is active on this email' : 'Awaiting email verification' }} | ||
</div> | ||
</div> | ||
<!-- delete --> | ||
<div class="icon relative flex items-center"> | ||
<button class="focus:outline-none text-gray5 dark:text-gray3 ml-2" @click.stop="toggleDropdownDelete"> | ||
<MoreIcon /> | ||
</button> | ||
<div | ||
v-show="showDelete" | ||
class="bg-lightBG dark:bg-darkBG dark:text-darkPrimaryText text-lightPrimaryText border-lightBorder modal-animation absolute z-10 flex w-min flex-col rounded-lg border p-2 shadow-lg" | ||
:class="$colorMode.dark ? `deleteEmailOpenDark` : `deleteEmailOpen`" | ||
style="top: 35px; right: -5px" | ||
> | ||
<!-- Delete --> | ||
<button class="focus:outline-none text-negative flex" @click="toggleDeleteConfirm(newsletter._id)"> | ||
<BinIcon class="p-1" /> | ||
<span class="text-negative ml-1 self-center text-sm pr-1">Delete</span> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
<portal v-if="showDeleteConfirm" to="deleteEmail"> | ||
<BasicConfirmAlert | ||
:text="`Are you sure you want to cancel this email notification? You can still add it again later`" | ||
@close="showDeleteConfirm = false" | ||
@confirm="deleteNewsletter(newsletter._id)" | ||
/> | ||
</portal> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import MoreIcon from '@/components/icons/More.vue' | ||
import BinIcon from '@/components/icons/Bin.vue' | ||
import { deleteSubscription } from '@/backend/emails' | ||
import BasicConfirmAlert from '@/components/popups/BasicConfirmAlert.vue' | ||
interface IData { | ||
showDelete: boolean | ||
showDeleteConfirm: boolean | ||
showStatus: boolean | ||
} | ||
export default Vue.extend({ | ||
components: { | ||
BinIcon, | ||
MoreIcon, | ||
BasicConfirmAlert, | ||
}, | ||
props: { | ||
newsletter: { | ||
type: Object, | ||
required: true, | ||
}, | ||
}, | ||
data(): IData { | ||
return { | ||
showDelete: false, | ||
showDeleteConfirm: false, | ||
showStatus: false, | ||
} | ||
}, | ||
created() { | ||
window.addEventListener(`click`, this.handleDropdown, false) | ||
}, | ||
destroyed() { | ||
window.removeEventListener(`click`, this.handleDropdown, false) | ||
}, | ||
methods: { | ||
toggleDropdownDelete() { | ||
this.showDelete = !this.showDelete | ||
}, | ||
toggleDeleteConfirm() { | ||
this.showDeleteConfirm = !this.showDeleteConfirm | ||
}, | ||
async deleteNewsletter(id: string) { | ||
try { | ||
await deleteSubscription(this.$store.state.session.id, id) | ||
this.$toastSuccess(`Newsletter deleted successfully`) | ||
this.$emit(`subscriptionDeleted`) | ||
} catch (err) { | ||
this.$handleError(err) | ||
} | ||
}, | ||
handleDropdown(e: any): void { | ||
if (!e.target || e.target.parentNode === null || e.target.parentNode.classList === undefined) { | ||
return | ||
} | ||
if (!e.target.parentNode.classList.contains(`icon`)) { | ||
this.showDelete = false | ||
} | ||
}, | ||
}, | ||
}) | ||
</script> | ||
<style> | ||
.deleteEmailOpen::before { | ||
content: ''; | ||
position: absolute; | ||
top: -0.5rem; | ||
right: 0.5rem; | ||
transform: rotate(45deg); | ||
width: 1rem; | ||
height: 1rem; | ||
background-color: #fff; | ||
border-radius: 2px; | ||
} | ||
.deleteEmailOpenDark::before { | ||
content: ''; | ||
position: absolute; | ||
top: -0.5rem; | ||
right: 0.5rem; | ||
transform: rotate(45deg); | ||
width: 1rem; | ||
height: 1rem; | ||
background-color: #121212; | ||
border-radius: 2px; | ||
} | ||
.EmailInfoOpen::before { | ||
content: ''; | ||
position: absolute; | ||
top: 0.5rem; | ||
right: -0.5rem; | ||
transform: rotate(45deg); | ||
width: 1rem; | ||
height: 1rem; | ||
background-color: #fff; | ||
border-radius: 2px; | ||
z-index: 1; | ||
} | ||
.EmailInfoOpenDark::before { | ||
content: ''; | ||
position: absolute; | ||
top: 0.5rem; | ||
right: -0.5rem; | ||
transform: rotate(45deg); | ||
width: 1rem; | ||
height: 1rem; | ||
background-color: #686868; | ||
border-radius: 2px; | ||
z-index: 1; | ||
} | ||
</style> |
Oops, something went wrong.