diff --git a/components/dashboard/src/Menu.tsx b/components/dashboard/src/Menu.tsx index 54986d0ec62e85..f0d190c5c1fded 100644 --- a/components/dashboard/src/Menu.tsx +++ b/components/dashboard/src/Menu.tsx @@ -379,7 +379,7 @@ export default function Menu() { <ContextMenu menuEntries={[ { - title: (user && User.getPrimaryEmail(user)) || "", + title: (user && (User.getPrimaryEmail(user) || user?.name)) || "User", customFontStyle: "text-gray-400", separator: true, }, diff --git a/components/dashboard/src/admin/UserDetail.tsx b/components/dashboard/src/admin/UserDetail.tsx index 9e824d17de27d5..f7e9e750e7e7e9 100644 --- a/components/dashboard/src/admin/UserDetail.tsx +++ b/components/dashboard/src/admin/UserDetail.tsx @@ -50,7 +50,7 @@ export default function UserDetail(p: { user: User }) { }, [p.user]); const email = User.getPrimaryEmail(p.user); - const emailDomain = email.split("@")[email.split("@").length - 1]; + const emailDomain = email ? email.split("@")[email.split("@").length - 1] : undefined; const updateUser: UpdateUserFunction = async (fun) => { setActivity(true); @@ -62,6 +62,11 @@ export default function UserDetail(p: { user: User }) { }; const addStudentDomain = async () => { + if (!emailDomain) { + console.log("cannot add student's email domain because there is none!"); + return; + } + await updateUser(async (u) => { await getGitpodService().server.adminAddStudentEmailDomain(u.id, emailDomain); await getGitpodService() @@ -221,7 +226,9 @@ export default function UserDetail(p: { user: User }) { <Property name="Student" actions={ - !isStudent && !["gmail.com", "yahoo.com", "hotmail.com"].includes(emailDomain) + !isStudent && + emailDomain && + !["gmail.com", "yahoo.com", "hotmail.com"].includes(emailDomain) ? [ { label: `Make '${emailDomain}' a student domain`, diff --git a/components/dashboard/src/admin/UserSearch.tsx b/components/dashboard/src/admin/UserSearch.tsx index 1565d8c41f43de..88b0c98f525cf3 100644 --- a/components/dashboard/src/admin/UserSearch.tsx +++ b/components/dashboard/src/admin/UserSearch.tsx @@ -5,7 +5,6 @@ */ import { AdminGetListResult, User } from "@gitpod/gitpod-protocol"; -import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; import moment from "moment"; import { useEffect, useState } from "react"; import { useLocation } from "react-router"; @@ -113,12 +112,7 @@ function UserEntry(p: { user: User }) { if (!p) { return <></>; } - let email = "---"; - try { - email = User.getPrimaryEmail(p.user); - } catch (e) { - log.error(e); - } + const email = User.getPrimaryEmail(p.user) || "---"; return ( <Link key={p.user.id} to={"/admin/users/" + p.user.id} data-analytics='{"button_type":"sidebar_menu"}'> <div className="rounded-xl whitespace-nowrap flex space-x-2 py-6 px-6 w-full justify-between hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gitpod-kumquat-light group"> diff --git a/components/dashboard/src/settings/Account.tsx b/components/dashboard/src/settings/Account.tsx index dc60351227e9e4..084835ad24a474 100644 --- a/components/dashboard/src/settings/Account.tsx +++ b/components/dashboard/src/settings/Account.tsx @@ -21,7 +21,7 @@ export default function Account() { const [modal, setModal] = useState(false); const [typedEmail, setTypedEmail] = useState(""); - const primaryEmail = User.getPrimaryEmail(user!); + const primaryEmail = User.getPrimaryEmail(user!) || "---"; const deleteAccount = async () => { await getGitpodService().server.deleteAccount(); @@ -75,7 +75,7 @@ export default function Account() { </div> <div className="mt-4"> <h4>Email</h4> - <input type="text" disabled={true} value={User.getPrimaryEmail(user!)} /> + <input type="text" disabled={true} value={primaryEmail} /> </div> </div> <div className="lg:pl-14"> diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index 85c6f373701839..676c8ed034747a 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -68,13 +68,19 @@ export namespace User { }); return res; } - export function getPrimaryEmail(user: User): string { + + /** + * Tries to return the primaryEmail of the first identity this user signed up with. + * @param user + * @returns A primaryEmail, or undefined if there is none. + */ + export function getPrimaryEmail(user: User): string | undefined { const identities = user.identities.filter((i) => !!i.primaryEmail); if (identities.length <= 0) { - throw new Error(`No identity with primary email for user: ${user.id}!`); + return undefined; } - return identities[0].primaryEmail!; + return identities[0].primaryEmail || undefined; } export function getName(user: User): string | undefined { const name = user.fullName || user.name; diff --git a/components/server/ee/src/workspace/gitpod-server-impl.ts b/components/server/ee/src/workspace/gitpod-server-impl.ts index 0bddcf83b3127b..bd99fb96d68228 100644 --- a/components/server/ee/src/workspace/gitpod-server-impl.ts +++ b/components/server/ee/src/workspace/gitpod-server-impl.ts @@ -1120,6 +1120,9 @@ export class GitpodServerEEImpl extends GitpodServerImpl { try { const email = User.getPrimaryEmail(user); + if (!email) { + throw new Error("No identity with primary email for user"); + } return new Promise((resolve, reject) => { this.chargebeeProvider.hosted_page diff --git a/components/server/src/analytics.ts b/components/server/src/analytics.ts index 1be7f45297ac4e..ed39d7ec435a9e 100644 --- a/components/server/src/analytics.ts +++ b/components/server/src/analytics.ts @@ -59,7 +59,7 @@ function fullIdentify(user: User, request: Request, analytics: IAnalyticsWriter) }, traits: { ...resolveIdentities(user), - email: User.getPrimaryEmail(user), + email: User.getPrimaryEmail(user) || "", full_name: user.fullName, created_at: user.creationDate, unsubscribed_onboarding: user.additionalData?.emailNotificationSettings?.allowsOnboardingMail === false,