Skip to content

Commit 9bf1d1b

Browse files
committed
Redesign message notification email: add avatar, profile link, and metadata; refine translations and layout for better engagement.
1 parent 292ba01 commit 9bf1d1b

4 files changed

Lines changed: 190 additions & 22 deletions

File tree

‎backend/email/emails/functions/mock.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,19 @@ export type PartialProfile = Pick<
1717
> &
1818
Partial<ProfileRow>
1919

20+
const avatarUrl =
21+
'https://firebasestorage.googleapis.com/v0/b/compass-130ba.firebasestorage.app/o/user-images%2FMartin%2Flove-images%2FeD09lRtuRF.jpg?alt=media&token=db41b5f2-b1c0-4df9-aa9c-11d44c970947'
2022
export const mockUser: User = {
2123
createdTime: 0,
22-
avatarUrl: 'https://martinbraquet.com/wp-content/uploads/BDo_Tbzj.jpeg',
24+
avatarUrl: avatarUrl,
2325
id: '0k1suGSJKVUnHbCPEhHNpgZPkUP2',
2426
username: 'Martin',
2527
name: 'Martin',
2628
}
2729

2830
export const jamesUser: User = {
2931
createdTime: 0,
30-
avatarUrl: 'https://martinbraquet.com/wp-content/uploads/BDo_Tbzj.jpeg',
32+
avatarUrl: avatarUrl,
3133
id: '5LZ4LgYuySdL1huCWe7bti02ghx2',
3234
username: 'James',
3335
name: 'James',

‎backend/email/emails/new-message.tsx‎

Lines changed: 178 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
Container,
55
Head,
66
Html,
7+
Img,
78
Link,
89
Preview,
910
Section,
@@ -13,7 +14,7 @@ import {ANDROID_APP_URL} from 'common/constants'
1314
import {DOMAIN} from 'common/envs/constants'
1415
import {type ProfileRow} from 'common/profiles/profile'
1516
import {type User} from 'common/user'
16-
import {container, content, Footer, main} from 'email/utils'
17+
import {container, content, DARK_MODE_CSS, Footer, main} from 'email/utils'
1718
import React from 'react'
1819
import {createT} from 'shared/locale'
1920

@@ -31,7 +32,7 @@ interface NewMessageEmailProps {
3132

3233
export const NewMessageEmail = ({
3334
fromUser,
34-
// fromUserProfile,
35+
fromUserProfile,
3536
toUser,
3637
channelId,
3738
unsubscribeUrl,
@@ -41,16 +42,25 @@ export const NewMessageEmail = ({
4142
const name = toUser.name.split(' ')[0]
4243
const creatorName = fromUser.name
4344
const messagesUrl = `https://${DOMAIN}/messages/${channelId}`
44-
// const userImgSrc = getOgImageUrl(fromUser, fromUserProfile)
45+
const profileUrl = `https://${DOMAIN}/${fromUser.username}`
46+
// Every client renders <img> reliably, none of them render a CSS-generated placeholder — so an absent
47+
// avatar falls back to the same asset the web app uses rather than to a broken image icon.
48+
const avatarUrl = fromUser.avatarUrl
4549
const t = createT(locale)
4650

51+
// Age and city are the two facts a reader can act on at a glance; anything more turns the card into a
52+
// profile page and competes with the one button this email has.
53+
const meta = [fromUserProfile?.age, fromUserProfile?.city].filter(Boolean).join(' · ')
54+
4755
return (
4856
<Html>
49-
<Head />
57+
<Head>
58+
<style>{DARK_MODE_CSS}</style>
59+
</Head>
5060
<Preview>
5161
{t('email.new_message.preview', 'New message from {creatorName}', {creatorName})}
5262
</Preview>
53-
<Body style={main}>
63+
<Body style={main} className="cm-body">
5464
<Container style={container}>
5565
{/*<Section style={logoContainer}>*/}
5666
{/* <Img*/}
@@ -61,52 +71,155 @@ export const NewMessageEmail = ({
6171
{/* />*/}
6272
{/*</Section>*/}
6373

64-
<Section style={content}>
74+
<Section style={content} className="cm-surface">
6575
<div
6676
style={{
6777
textAlign: 'center',
68-
marginBottom: '32px',
78+
marginBottom: '28px',
6979
}}
7080
>
81+
{/* A pill rather than a headline: it names the event in one glance, above the fold and above
82+
the greeting, which is what the reader is scanning for when the subject line already
83+
said someone wrote to them. */}
84+
<span
85+
className="cm-chip"
86+
style={{
87+
display: 'inline-block',
88+
backgroundColor: '#faf3e9',
89+
border: '1px solid #e8c99e',
90+
borderRadius: '9999px',
91+
padding: '6px 14px',
92+
fontFamily: "'DM Sans', sans-serif",
93+
fontSize: '11px',
94+
fontWeight: '600',
95+
letterSpacing: '0.12em',
96+
textTransform: 'uppercase',
97+
color: '#a6682e',
98+
lineHeight: '1.2',
99+
}}
100+
>
101+
{t('email.new_message.badge', 'New message')}
102+
</span>
103+
71104
<Text
72105
style={{
73106
fontSize: '28px',
74107
fontFamily: "'Cormorant Garamond', serif",
75108
fontWeight: '500',
76109
color: '#1e1a14',
77-
marginBottom: '8px',
110+
margin: '18px 0 0 0',
78111
letterSpacing: '-0.01em',
79112
lineHeight: '1.1',
80113
}}
114+
className="cm-name"
81115
>
82116
{t('email.new_message.greeting', 'Hi {name},', {name})}
83117
</Text>
84118
</div>
85119

86120
<div
121+
className="cm-card"
87122
style={{
88123
backgroundColor: '#f7f4ef',
89124
border: '1px solid #dee5b2',
90-
borderRadius: '14px',
91-
padding: '24px',
125+
borderRadius: '18px',
126+
padding: '28px 24px 26px 24px',
92127
margin: '24px 0',
93128
textAlign: 'center',
94129
}}
95130
>
131+
{/* The avatar is the whole point of the redesign: a face converts where a name does not.
132+
The ring is a padded round div rather than a box-shadow, which Gmail strips. */}
133+
{avatarUrl && (
134+
<Link href={profileUrl} style={{textDecoration: 'none'}}>
135+
<span
136+
style={{
137+
display: 'inline-block',
138+
padding: '4px',
139+
backgroundColor: '#e8c99e',
140+
borderRadius: '9999px',
141+
lineHeight: '0',
142+
}}
143+
>
144+
<Img
145+
src={avatarUrl}
146+
alt={creatorName}
147+
width={96}
148+
height={96}
149+
style={{
150+
width: '96px',
151+
height: '96px',
152+
borderRadius: '9999px',
153+
objectFit: 'cover',
154+
display: 'block',
155+
border: '3px solid #ffffff',
156+
}}
157+
/>
158+
</span>
159+
</Link>
160+
)}
161+
96162
<Text
163+
className="cm-name"
97164
style={{
98-
fontSize: '18px',
165+
fontSize: '22px',
99166
fontFamily: "'Cormorant Garamond', serif",
100-
fontWeight: '500',
167+
fontWeight: '600',
101168
color: '#1e1a14',
102-
marginBottom: '20px',
169+
margin: '16px 0 0 0',
170+
lineHeight: '1.2',
103171
letterSpacing: '0.01em',
172+
}}
173+
>
174+
{creatorName}
175+
</Text>
176+
177+
<Text
178+
className="cm-accent"
179+
style={{
180+
fontSize: '13px',
181+
fontFamily: "'DM Sans', sans-serif",
182+
color: '#c17f3e',
183+
margin: '4px 0 0 0',
104184
lineHeight: '1.3',
105185
}}
106186
>
107-
{t('email.new_message.message', '{creatorName} just messaged you!', {creatorName})}
187+
@{fromUser.username}
188+
{meta ? ` · ${meta}` : ''}
108189
</Text>
109190

191+
{/* A chat bubble, not a paragraph: the asymmetric corner reads as a message in every client
192+
without needing a CSS triangle that Outlook would drop. */}
193+
<div
194+
style={{
195+
backgroundColor: '#ffffff',
196+
border: '1px solid #e8c99e',
197+
borderRadius: '18px 18px 18px 4px',
198+
padding: '18px 20px',
199+
margin: '22px 0 0 0',
200+
textAlign: 'left',
201+
}}
202+
className="cm-chip"
203+
>
204+
<Text
205+
style={{
206+
fontSize: '18px',
207+
fontFamily: "'Cormorant Garamond', serif",
208+
fontStyle: 'italic',
209+
fontWeight: '500',
210+
color: '#1e1a14',
211+
margin: '0',
212+
letterSpacing: '0.01em',
213+
lineHeight: '1.4',
214+
}}
215+
className="cm-name"
216+
>
217+
{t('email.new_message.message', '{creatorName} just messaged you!', {
218+
creatorName,
219+
})}
220+
</Text>
221+
</div>
222+
110223
<Button
111224
href={messagesUrl}
112225
style={{
@@ -119,22 +232,69 @@ export const NewMessageEmail = ({
119232
textDecoration: 'none',
120233
textAlign: 'center' as const,
121234
display: 'inline-block',
122-
padding: '14px 32px',
123-
margin: '0',
235+
padding: '14px 36px',
236+
margin: '24px 0 0 0',
124237
border: '1px solid #a6682e',
125238
transition: 'all 0.12s ease',
126239
}}
127240
>
128-
{t('email.new_message.viewButton', 'View message')}
241+
{t('email.new_message.viewButton', 'Read & reply')}
129242
</Button>
243+
244+
<Text
245+
className="cm-muted"
246+
style={{
247+
fontSize: '12px',
248+
fontFamily: "'DM Sans', sans-serif",
249+
color: '#8c8070',
250+
margin: '14px 0 0 0',
251+
lineHeight: '1.5',
252+
}}
253+
>
254+
<Link
255+
href={profileUrl}
256+
style={{color: '#c17f3e', textDecoration: 'none', fontWeight: '500'}}
257+
>
258+
{t('email.new_message.view_profile', 'View profile')}
259+
</Link>
260+
</Text>
130261
</div>
131262

263+
{/* The nudge sits outside the card so the card stays a single, clean unit — and because the
264+
reader who is going to reply has already clicked by the time they reach this line. */}
265+
<Text
266+
className="cm-muted"
267+
style={{
268+
fontSize: '14px',
269+
fontFamily: "'Cormorant Garamond', serif",
270+
fontStyle: 'italic',
271+
lineHeight: '1.6',
272+
color: '#8c8070',
273+
marginTop: '24px',
274+
textAlign: 'center',
275+
}}
276+
>
277+
{t(
278+
'email.new_message.nudge',
279+
'One sentence back is usually all it takes to start something real.',
280+
)}
281+
</Text>
282+
283+
<hr
284+
style={{
285+
border: 'none',
286+
borderTop: '1px solid #ece7de',
287+
margin: '28px 0 0 0',
288+
}}
289+
/>
290+
132291
<Text
292+
className="cm-muted"
133293
style={{
134294
fontSize: '13px',
135295
lineHeight: '1.75',
136296
color: '#8c8070',
137-
marginTop: '32px',
297+
marginTop: '20px',
138298
textAlign: 'center',
139299
}}
140300
>

‎common/messages/de.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@
314314
"email.new_message.message": "{creatorName} hat Ihnen gerade eine Nachricht geschickt!",
315315
"email.new_message.preview": "Neue Nachricht von {creatorName}",
316316
"email.new_message.subject": "{creatorName} hat Ihnen eine Nachricht geschickt!",
317-
"email.new_message.viewButton": "Nachricht anzeigen",
317+
"email.new_message.viewButton": "Lesen und antworten",
318318
"email.search_alerts.callToAction": "Wenn jemand zu Ihnen passt, treten Sie in Kontakt. Ein einfaches Hallo kann der Beginn einer bedeutungsvollen Freundschaft, Zusammenarbeit oder Beziehung sein.",
319319
"email.search_alerts.communityNote": "Compass wird von der Community aufgebaut und erhalten — keine Werbung, keine versteckten Algorithmen, keine Abonnements. Ihre Präsenz und Teilnahme machen es möglich.",
320320
"email.search_alerts.greeting": "Hallo {name},",
@@ -1749,6 +1749,9 @@
17491749
"email.new_message.daily_limit": "Um deine Inbox nicht zu überladen, erhältst du höchstens eine E-Mail pro Tag pro Konversation. Wenn {creatorName} heute weitere Nachrichten sendet, wirst du erst morgen wieder per E-Mail benachrichtigt.",
17501750
"email.new_message.app_prompt": "Willst du Echtzeit-Benachrichtigungen? Lade die Compass App für Android herunter:",
17511751
"email.new_message.app_link": "App herunterladen",
1752+
"email.new_message.badge": "Neue Nachricht",
1753+
"email.new_message.view_profile": "Profil ansehen",
1754+
"email.new_message.nudge": "Ein einziger Satz zurück reicht oft, damit etwas Echtes entsteht.",
17521755
"faq.empty.cta": "Frage stellen →",
17531756
"faq.empty.text": "Versuche ein allgemeineres Wort — oder frag uns direkt, wir beantworten jede Nachricht.",
17541757
"faq.empty.title": "Dazu passt hier nichts.",

‎common/messages/fr.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@
314314
"email.new_message.message": "{creatorName} vient de vous envoyer un message !",
315315
"email.new_message.preview": "Nouveau message de {creatorName}",
316316
"email.new_message.subject": "{creatorName} vous a envoyé un message !",
317-
"email.new_message.viewButton": "Voir le message",
317+
"email.new_message.viewButton": "Lire et répondre",
318318
"email.search_alerts.callToAction": "Si quelqu'un vous parle, contactez-les. Un simple bonjour peut être le début d'une amitié, d'une collaboration ou d'une relation significative.",
319319
"email.search_alerts.communityNote": "Compass est construit et soutenu par la communauté — pas de publicité, pas d'algorithmes cachés, pas d'abonnements. Votre présence et votre participation le rendent possible.",
320320
"email.search_alerts.greeting": "Bonjour {name},",
@@ -1748,6 +1748,9 @@
17481748
"email.new_message.daily_limit": "Pour éviter de surcharger votre boîte de réception, vous recevrez au plus un e-mail par jour par conversation. Si {creatorName} envoie d'autres messages aujourd'hui, vous ne serez pas notifié par e-mail.",
17491749
"email.new_message.app_prompt": "Vous voulez des notifications en temps réel ? Téléchargez l'application Compass pour Android :",
17501750
"email.new_message.app_link": "Obtenir l'app",
1751+
"email.new_message.badge": "Nouveau message",
1752+
"email.new_message.view_profile": "Voir le profil",
1753+
"email.new_message.nudge": "Une seule phrase en retour suffit souvent à faire naître quelque chose de vrai.",
17511754
"faq.empty.cta": "Poser une question →",
17521755
"faq.empty.text": "Essayez un mot plus général — ou posez-nous directement la question, nous répondons à chaque message.",
17531756
"faq.empty.title": "Aucun résultat pour cette recherche.",

0 commit comments

Comments
 (0)