Skip to content

Commit 5efee7f

Browse files
committed
Allow users to add multiple email addresses to their account
1 parent f13d5c5 commit 5efee7f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1949
-304
lines changed

app/components/email-input.hbs

Lines changed: 55 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,64 @@
11
<div ...attributes>
2-
{{#unless @user.email}}
3-
<div local-class="friendly-message" data-test-no-email>
4-
<p>
5-
Please add your email address. We will only use
6-
it to contact you about your account. We promise we'll never share it!
7-
</p>
8-
</div>
9-
{{/unless}}
10-
11-
{{#if this.isEditing }}
12-
<div local-class="row">
13-
<div local-class="label">
14-
<label for="email-input">Email</label>
15-
</div>
16-
<form local-class="email-form" {{on "submit" (prevent-default (perform this.saveEmailTask))}}>
17-
<Input
18-
@type="email"
19-
@value={{this.value}}
20-
id="email-input"
21-
placeholder="Email"
22-
local-class="input"
23-
data-test-input
24-
/>
25-
26-
<div local-class="actions">
27-
<button
28-
type='submit'
29-
local-class="save-button"
30-
class="button button--small"
31-
disabled={{not this.value}}
32-
data-test-save-button
33-
>
34-
Save
35-
</button>
2+
{{#unless this.email.id }}
3+
<div local-class="row">
4+
<form local-class="email-form" {{on "submit" (prevent-default (perform this.saveEmailTask))}}>
5+
<Input @type="email" @value={{this.value}} id="email-input" placeholder="Email" local-class="input"
6+
data-test-input {{did-insert this.focus}} oninput={{this.validate}} />
367

37-
<button
38-
type="button"
39-
class="button button--small"
40-
data-test-cancel-button
41-
{{on "click" (fn (mut this.isEditing) false)}}
42-
>
43-
Cancel
44-
</button>
45-
</div>
46-
</form>
47-
</div>
48-
{{else}}
49-
<div local-class="row">
50-
<div local-class="label">
51-
<dt>Email</dt>
52-
</div>
53-
<div local-class="email-column" data-test-email-address>
54-
<dd>
55-
{{ @user.email }}
56-
{{#if @user.email_verified}}
57-
<span local-class="verified" data-test-verified>Verified!</span>
58-
{{/if}}
59-
</dd>
60-
</div>
618
<div local-class="actions">
62-
<button
63-
type="button"
64-
class="button button--small"
65-
data-test-edit-button
66-
{{on "click" this.editEmail}}
67-
>
68-
Edit
9+
<button type='submit' local-class="save-button" class="button button--small" disabled={{not this.isValid}}
10+
data-test-save-button>
11+
Verify
6912
</button>
7013
</div>
71-
</div>
72-
{{#if (and @user.email (not @user.email_verified))}}
73-
<div local-class="row">
74-
<div local-class="label">
75-
{{#if @user.email_verification_sent}}
76-
<p data-test-verification-sent>We have sent a verification email to your address.</p>
14+
</form>
15+
</div>
16+
{{else}}
17+
<div local-class="row">
18+
<div local-class="email-column" data-test-email-address>
19+
<dd>
20+
{{ this.email.email }}
21+
<span class="badges">
22+
{{#if this.email.verified}}
23+
<span local-class="badge verified" data-test-verified>Verified</span>
24+
{{#if this.email.send_notifications }}
25+
<span local-class="badge">Notifications are sent here</span>
7726
{{/if}}
78-
<p data-test-not-verified>Your email has not yet been verified.</p>
79-
</div>
80-
<div local-class="actions">
81-
<button
82-
type="button"
83-
class="button button--small"
84-
disabled={{this.disableResend}}
85-
data-test-resend-button
86-
{{on "click" (perform this.resendEmailTask)}}
87-
>
88-
{{#if this.disableResend}}
89-
Sent!
90-
{{else if @user.email_verification_sent}}
91-
Resend
27+
{{else}}
28+
{{#if this.email.verification_email_sent}}
29+
<span local-class="badge pending-verification" data-test-verification-sent>Unverified - email sent</span>
9230
{{else}}
93-
Send verification email
31+
<span local-class="badge unverified" data-test-verification-not-sent>Unverified</span>
9432
{{/if}}
95-
</button>
96-
</div>
97-
</div>
98-
{{/if}}
99-
{{/if}}
33+
{{/if}}
34+
</span>
35+
</dd>
36+
</div>
37+
<div local-class="actions">
38+
{{#unless this.email.verified}}
39+
<button type="button" class="button button--small" disabled={{this.disableResend}} data-test-resend-button
40+
{{on "click" (perform this.resendEmailTask)}}>
41+
{{#if this.disableResend}}
42+
Sent!
43+
{{else if this.email.verification_email_sent}}
44+
Resend
45+
{{else}}
46+
Verify
47+
{{/if}}
48+
</button>
49+
{{/unless}}
50+
{{#if (and (not this.email.send_notifications) this.email.verified)}}
51+
<button type="button" class="button button--small" data-test-edit-button {{on "click" (prevent-default (perform this.enableNotificationsTask))}}>
52+
Enable notifications
53+
</button>
54+
{{/if}}
55+
{{#if @canDelete}}
56+
<button disabled={{this.email.send_notifications}} title={{if this.email.send_notifications "Cannot delete notifications email"}} type="button" class="button button--small button--red" data-test-edit-button {{on "click" (prevent-default (perform this.deleteEmailTask))}}>
57+
Remove
58+
</button>
59+
{{/if}}
60+
</div>
61+
</div>
62+
{{/unless}}
10063

101-
</div>
64+
</div>

app/components/email-input.js

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,22 @@ import { task } from 'ember-concurrency';
88
export default class EmailInput extends Component {
99
@service notifications;
1010

11+
@tracked email = this.args.email || { email: '', id: null };
12+
@tracked isValid = false;
1113
@tracked value;
12-
@tracked isEditing = false;
1314
@tracked disableResend = false;
1415

16+
@action focus(element) {
17+
element.focus();
18+
}
19+
20+
@action validate(event) {
21+
this.isValid = event.target.checkValidity();
22+
}
23+
1524
resendEmailTask = task(async () => {
1625
try {
17-
await this.args.user.resendVerificationEmail();
26+
await this.args.user.resendVerificationEmail(this.email.id);
1827
this.disableResend = true;
1928
} catch (error) {
2029
let detail = error.errors?.[0]?.detail;
@@ -26,30 +35,47 @@ export default class EmailInput extends Component {
2635
}
2736
});
2837

29-
@action
30-
editEmail() {
31-
this.value = this.args.user.email;
32-
this.isEditing = true;
33-
}
38+
deleteEmailTask = task(async () => {
39+
try {
40+
await this.args.user.deleteEmail(this.email.id);
41+
} catch (error) {
42+
console.error('Error deleting email:', error);
43+
let detail = error.errors?.[0]?.detail;
44+
if (detail && !detail.startsWith('{')) {
45+
this.notifications.error(`Error in deleting email: ${detail}`);
46+
} else {
47+
this.notifications.error('Unknown error in deleting email');
48+
}
49+
}
50+
});
3451

3552
saveEmailTask = task(async () => {
36-
let userEmail = this.value;
37-
let user = this.args.user;
38-
3953
try {
40-
await user.changeEmail(userEmail);
41-
42-
this.isEditing = false;
43-
this.disableResend = false;
54+
this.email = await this.args.user.addEmail(this.value);
55+
this.disableResend = true;
56+
await this.args.onAddEmail?.();
4457
} catch (error) {
4558
let detail = error.errors?.[0]?.detail;
4659

47-
let msg =
48-
detail && !detail.startsWith('{')
49-
? `An error occurred while saving this email, ${detail}`
50-
: 'An unknown error occurred while saving this email.';
60+
if (detail && !detail.startsWith('{')) {
61+
this.notifications.error(`Error in saving email: ${detail}`);
62+
} else {
63+
console.error('Error saving email:', error);
64+
this.notifications.error('Unknown error in saving email');
65+
}
66+
}
67+
});
5168

52-
this.notifications.error(`Error in saving email: ${msg}`);
69+
enableNotificationsTask = task(async () => {
70+
try {
71+
await this.args.user.updateNotificationEmail(this.email.id);
72+
} catch (error) {
73+
let detail = error.errors?.[0]?.detail;
74+
if (detail && !detail.startsWith('{')) {
75+
this.notifications.error(`Error in enabling notifications: ${detail}`);
76+
} else {
77+
this.notifications.error('Unknown error in enabling notifications');
78+
}
5379
}
5480
});
5581
}

app/components/email-input.module.css

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
.friendly-message {
2-
margin-top: 0;
3-
}
4-
51
.row {
62
width: 100%;
73
border: 1px solid var(--gray-border);
84
border-bottom-width: 0;
95
padding: var(--space-2xs) var(--space-s);
106
display: flex;
117
align-items: center;
8+
justify-content: space-between;
129

1310
&:last-child {
1411
border-bottom-width: 1px;
@@ -22,12 +19,41 @@
2219
}
2320

2421
.email-column {
22+
padding: var(--space-xs) 0;
23+
}
24+
25+
.email-column dd {
26+
margin: 0;
27+
display: flex;
28+
flex-wrap: wrap;
29+
gap: var(--space-3xs);
2530
flex: 20;
2631
}
2732

33+
.email-column .badges {
34+
display: flex;
35+
flex-wrap: wrap;
36+
gap: var(--space-3xs);
37+
}
38+
39+
.badge {
40+
padding: var(--space-4xs) var(--space-2xs);
41+
background-color: var(--main-bg-dark);
42+
font-size: 0.8rem;
43+
border-radius: 100px;
44+
}
45+
2846
.verified {
29-
color: green;
30-
font-weight: bold;
47+
background-color: var(--green800);
48+
color: var(--grey200);
49+
}
50+
51+
.pending-verification {
52+
background-color: light-dark(var(--orange-200), var(--orange-500));
53+
}
54+
55+
.unverified {
56+
background-color: light-dark(var(--orange-300), var(--orange-600));
3157
}
3258

3359
.email-form {
@@ -38,13 +64,22 @@
3864
}
3965

4066
.input {
41-
width: 400px;
67+
background-color: var(--main-bg);
68+
border: 0;
69+
flex: 1;
70+
margin: calc(var(--space-3xs) * -1) calc(var(--space-2xs) * -1);
71+
padding: var(--space-3xs) var(--space-2xs);
4272
margin-right: var(--space-xs);
4373
}
4474

75+
.input:focus {
76+
outline: none;
77+
}
78+
4579
.actions {
4680
display: flex;
4781
align-items: center;
82+
gap: var(--space-3xs);
4883
}
4984

5085
.save-button {

app/controllers/settings/profile.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,22 @@ import { task } from 'ember-concurrency';
88
export default class extends Controller {
99
@service notifications;
1010

11+
@tracked isAddingEmail = false;
12+
1113
@tracked publishNotifications;
14+
@tracked notificationEmailId;
1215

1316
@action handleNotificationsChange(event) {
1417
this.publishNotifications = event.target.checked;
1518
}
1619

20+
@action handleNotificationEmailChange(event) {
21+
this.notificationEmailId = event.target.value;
22+
}
23+
1724
updateNotificationSettings = task(async () => {
1825
try {
26+
await this.model.user.updateNotificationEmail(this.notificationEmailId);
1927
await this.model.user.updatePublishNotifications(this.publishNotifications);
2028
} catch {
2129
this.notifications.error(

0 commit comments

Comments
 (0)