Skip to content

Commit

Permalink
feat: send notification when access request is approved (#854)
Browse files Browse the repository at this point in the history
  • Loading branch information
khorshuheng authored Oct 2, 2024
1 parent 3b320b0 commit 1664fe8
Show file tree
Hide file tree
Showing 7 changed files with 353 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en" xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<meta charset="utf-8">
<meta name="x-apple-disable-message-reformatting">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
<!--[if mso]>
<noscript>
<xml>
<o:OfficeDocumentSettings xmlns:o="urn:schemas-microsoft-com:office:office">
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
</noscript>
<style>
td,th,div,p,a,h1,h2,h3,h4,h5,h6 {font-family: "Segoe UI", sans-serif; mso-line-height-rule: exactly;}
</style>
<![endif]-->
<title>Your access request has been approved</title>
<style>
.hover-opacity-90:hover {
opacity: 0.9 !important
}
@media (max-width: 600px) {
.sm-px-4 {
padding-left: 16px !important;
padding-right: 16px !important
}
.sm-py-12 {
padding-top: 48px !important;
padding-bottom: 48px !important
}
}
</style>
</head>
<body style="margin: 0; width: 100%; background-color: #faf5ff; padding: 0; -webkit-font-smoothing: antialiased; word-break: break-word">
<div style="display: none">
Workspace access request approved notification
&#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847; &#8199;&#65279;&#847;
</div>
<div role="article" aria-roledescription="email" aria-label="Your access request has been approved" lang="en">
<div class="sm-px-4 sm-py-12" style="background-color: #faf5ff; padding: 96px 48px; font-family: Helvetica, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif; color: #000">
<table align="center" cellpadding="0" cellspacing="0" role="none">
<tr>
<td style="width: 552px; max-width: 100%">
<p style="width: 100%; white-space: normal; overflow-wrap: break-word; text-align: center; font-size: 24px">
<span>Your request to access </span>
<span style="font-size: 30px; font-weight: 700">{{ workspace_name }}</span>
<span> has been approved </span>
</p>
<div role="separator" style="background-color: #cbd5e1; height: 1px; line-height: 1px; margin: 24px 20%">&zwj;</div>
<table align="center" cellpadding="0" cellspacing="0" role="none">
<tr>
<td style="width: 60px">
<div style="margin-right: 8px; height: 60px; width: 60px; overflow: hidden; border-radius: 16px; background-color: #fff; border: 2px solid black">
<img src="{{ workspace_icon_url }}" width="100%" height="100%" alt="{{ workspace_name }}" style="max-width: 100%; vertical-align: middle; line-height: 1; overflow: hidden; object-fit: cover">
</div>
</td>
<td>
<div style="margin-bottom: 8px; font-weight: 700">{{ workspace_name }}</div>
<div style="font-size: 14px; color: #64748b">
{{ workspace_member_count }} members
</div>
</td>
</tr>
</table>
<div style="text-align: center;">
<a href="{{ launch_workspace_url }}" class="hover-opacity-90" style="margin-top: 32px; margin-bottom: 32px; display: inline-block; width: 60%; cursor: pointer; border-radius: 16px; padding: 16px 24px; color: #f8fafc; text-decoration: none; background-color: #9327ff; font-size: 20px; font-weight: 400; line-height: 20px">
<!--[if mso]>
<i style="mso-font-width: 150%; mso-text-raise: 30px" hidden>&emsp;</i>
<![endif]-->
<span style="mso-text-raise: 16px">
<div style="font-size: 24px; font-weight: 500">View workspace</div>
</span>
<!--[if mso]>
<i hidden style="mso-font-width: 150%;">&emsp;&#8203;</i>
<![endif]-->
</a>
</div>
<div style="
margin-left: auto;
margin-right: auto;
width: 70%;
text-align: center;
font-size: 14px;
line-height: 18px;
color: #64748b;
">
By clicking "View workspace" above, you confirm that you have read,
understood, and agreed to AppFlowy's
<a href="https://appflowy.io/terms/app" style="color: #64748b">Terms & Conditions</a>
and
<a href="https://appflowy.io/privacy/app" style="color: #64748b">Privacy Policy</a>.
</div>
<div role="separator" style="background-color: #cbd5e1; height: 1px; line-height: 1px; margin: 24px 20%;">&zwj;</div>
</td>
</tr>
<tr>
<td style="padding-left: 24px; padding-right: 24px; text-align: center; font-size: 12px; color: #475569">
<p style="margin: 0 0 16px; cursor: pointer; text-transform: uppercase">
<a href="https://appflowy.io">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/main/assets/mailer_templates/build_production/images/appflowy-logo.png" width="150px" style="max-width: 100%; vertical-align: middle; line-height: 1;" alt="">
</a>
</p>
<p style="margin: 0; font-size: 14px; font-weight: 500; color: #000;">
Bring projects, knowledge, and teams together with the power of AI.
</p>
<p style="cursor: default">
<a href="https://twitter.com/appflowy" style="margin-right: 16px; color: #4338ca; text-decoration: none">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/main/assets/mailer_templates/build_production/images/twitter.png" width="20" alt="Maizzle" style="max-width: 100%; vertical-align: middle; line-height: 1;">
</a>
<a href="https://www.reddit.com/r/AppFlowy" style="margin-right: 16px; color: #4338ca; text-decoration: none;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/main/assets/mailer_templates/build_production/images/reddit.png" width="20" alt="Maizzle" style="max-width: 100%; vertical-align: middle; line-height: 1;">
</a>
<a href="https://github.com/AppFlowy-IO/AppFlowy" style="margin-right: 16px; color: #4338ca; text-decoration: none;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/main/assets/mailer_templates/build_production/images/github.png" width="20" alt="Maizzle" style="max-width: 100%; vertical-align: middle; line-height: 1;">
</a>
<a href="https://discord.gg/9Q2xaN37tV" style="margin-right: 16px; color: #4338ca; text-decoration: none;">
<img src="https://raw.githubusercontent.com/AppFlowy-IO/AppFlowy-Cloud/main/assets/mailer_templates/build_production/images/discord.png" width="20" alt="Maizzle" style="max-width: 100%; vertical-align: middle; line-height: 1;">
</a>
</p>
</td>
</tr>
</table>
</div>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions email_template/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module.exports = {
userName: "John Doe",
acceptUrl: "https://appflowy.io",
approveUrl: "https://appflowy.io",
launchWorkspaceUrl: "https://appflowy.io",
workspaceName: "AppFlowy",
workspaceMembersCount: "100",
workspaceIconURL: "https://cdn-icons-png.flaticon.com/512/1078/1078013.png",
Expand Down
1 change: 1 addition & 0 deletions email_template/config.production.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = {
userName: "{{ username }}",
acceptUrl: "{{ accept_url }}",
approveUrl: "{{ approve_url }}",
launchWorkspaceUrl: "{{ launch_workspace_url }}",
workspaceName: "{{ workspace_name }}",
workspaceMembersCount: "{{ workspace_member_count }}",
workspaceIconURL: "{{ workspace_icon_url }}",
Expand Down
135 changes: 135 additions & 0 deletions email_template/src/templates/access_request_approved_notification.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
title: "Your access request has been approved"
preheader: "Workspace access request approved notification"
bodyClass: bg-purple-50
---

<x-main>
<div
class="bg-purple-50 font-helvetica sm:px-4 px-12 sm:py-12 py-24 text-black"
>
<table align="center">
<tr>
<td class="w-[552px] max-w-full">
<p class="w-full text-center break-words whitespace-normal text-2xl">
<span class="mx-2=1">Your request to access </span>
<span class="text-3xl font-bold">{{ workspaceName }}</span>
<span class="mx-2=1"> has been approved </span>
</p>
<x-divider space-x="20%" />
<table align="center">
<tr>
<td class="w-[60px]">
<div
style="border: 2px solid black"
class="rounded-2xl mr-2 w-[60px] h-[60px] bg-white overflow-hidden"
>
<img
src="{{ workspaceIconURL }}"
class="overflow-hidden object-cover"
width="100%"
height="100%"
alt="{{ workspaceName }}"
/>
</div>
</td>
<td>
<div class="font-bold mb-2">{{ workspaceName }}</div>
<div class="text-sm text-slate-500">
{{ workspaceMembersCount }} members
</div>
</td>
</tr>
</table>
<x-button
align="center"
class="hover:opacity-90 cursor-pointer !text-xl !leading-[20px] !bg-[#9327ff] !font-normal w-[60%] my-8 rounded-2xl"
href="{{ launchWorkspaceUrl }}"
>
<div class="font-medium text-[24px]">View workspace</div>
</x-button>
<div
style="
margin-left: auto;
margin-right: auto;
width: 70%;
text-align: center;
font-size: 14px;
line-height: 18px;
color: #64748b;
"
>
By clicking "View workspace" above, you confirm that you have read,
understood, and agreed to AppFlowy's
<a href="https://appflowy.io/terms/app" style="color: #64748b"
>Terms & Conditions</a
>
and
<a href="https://appflowy.io/privacy/app" style="color: #64748b"
>Privacy Policy</a
>.
</div>
<x-divider space-x="20%" />
</td>
</tr>
<tr>
<td class="text-center text-slate-600 text-xs px-6">
<p class="m-0 mb-4 uppercase cursor-pointer">
<a href="https://appflowy.io">
<img
src="{{ cdnBaseUrl }}images/appflowy-logo.png"
width="150px"
/>
</a>
</p>
<p class="m-0 text-sm text-black font-medium">
Bring projects, knowledge, and teams together with the power of AI.
</p>

<p class="cursor-default">
<a
href="https://twitter.com/appflowy"
class="text-indigo-700 [text-decoration:none] mr-4"
>
<img
src="{{ cdnBaseUrl }}images/twitter.png"
width="20"
alt="Maizzle"
/>
</a>
<a
href="https://www.reddit.com/r/AppFlowy"
class="text-indigo-700 [text-decoration:none] mr-4"
>
<img
src="{{ cdnBaseUrl }}images/reddit.png"
width="20"
alt="Maizzle"
/>
</a>
<a
href="https://github.com/AppFlowy-IO/AppFlowy"
class="text-indigo-700 [text-decoration:none] mr-4"
>
<img
src="{{ cdnBaseUrl }}images/github.png"
width="20"
alt="Maizzle"
/>
</a>
<a
href="https://discord.gg/9Q2xaN37tV"
class="text-indigo-700 [text-decoration:none] mr-4"
>
<img
src="{{ cdnBaseUrl }}images/discord.png"
width="20"
alt="Maizzle"
/>
</a>
</p>
</td>
</tr>
</table>
</div>
</x-main>
19 changes: 17 additions & 2 deletions src/api/access_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,22 @@ async fn post_approve_access_request_handler(
let uid = state.user_cache.get_user_uid(&uuid).await?;
let access_request_id = access_request_id.into_inner();
let is_approved = approve_access_request_params.is_approved;
approve_or_reject_access_request(&state.pg_pool, access_request_id, uid, *uuid, is_approved)
.await?;
let appflowy_web_url = state
.config
.appflowy_web_url
.clone()
.ok_or(AppError::Internal(anyhow!(
"AppFlowy web url has not been set"
)))?;
approve_or_reject_access_request(
&state.pg_pool,
state.mailer.clone(),
&appflowy_web_url,
access_request_id,
uid,
*uuid,
is_approved,
)
.await?;
Ok(Json(AppResponse::Ok()))
}
35 changes: 33 additions & 2 deletions src/biz/access_request/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
folder_view::{to_dto_view_icon, to_view_layout},
ops::get_latest_collab_folder,
},
mailer::{Mailer, WorkspaceAccessRequestMailerParam},
mailer::{Mailer, WorkspaceAccessRequestApprovedMailerParam, WorkspaceAccessRequestMailerParam},
};

pub async fn create_access_request(
Expand Down Expand Up @@ -58,7 +58,7 @@ pub async fn create_access_request(
workspace_name: access_request.workspace.workspace_name,
workspace_icon_url,
workspace_member_count: access_request.workspace.member_count.unwrap_or(0),
approve_url: approve_url.to_string(),
approve_url,
},
)
.await
Expand Down Expand Up @@ -110,6 +110,8 @@ pub async fn get_access_request(

pub async fn approve_or_reject_access_request(
pg_pool: &PgPool,
mailer: Mailer,
appflowy_web_url: &str,
request_id: Uuid,
uid: i64,
user_uuid: Uuid,
Expand All @@ -133,6 +135,35 @@ pub async fn approve_or_reject_access_request(
role,
)
.await?;
let cloned_mailer = mailer.clone();
let launch_workspace_url = format!(
"{}/app/{}",
appflowy_web_url, &access_request.workspace.workspace_id
);

// use default icon until we have workspace icon
let workspace_icon_url =
"https://miro.medium.com/v2/resize:fit:2400/1*mTPfm7CwU31-tLhtLNkyJw.png".to_string();
tokio::spawn(async move {
if let Err(err) = cloned_mailer
.send_workspace_access_request_approval_notification(
&access_request.requester.name,
&access_request.requester.email,
WorkspaceAccessRequestApprovedMailerParam {
workspace_name: access_request.workspace.workspace_name,
workspace_icon_url,
workspace_member_count: access_request.workspace.member_count.unwrap_or(0),
launch_workspace_url,
},
)
.await
{
tracing::error!(
"Failed to send access request approved notification email: {:?}",
err
);
};
});
}
let status = if is_approved {
AFAccessRequestStatusColumn::Approved
Expand Down
Loading

0 comments on commit 1664fe8

Please sign in to comment.