diff --git a/backend/api/src/create-private-user-message-channel.ts b/backend/api/src/create-private-user-message-channel.ts
index a8dc6e0..fe0fd92 100644
--- a/backend/api/src/create-private-user-message-channel.ts
+++ b/backend/api/src/create-private-user-message-channel.ts
@@ -1,13 +1,24 @@
-import { APIError, APIHandler } from 'api/helpers/endpoint'
-import { filterDefined } from 'common/util/array'
-import { uniq } from 'lodash'
-import { createSupabaseDirectClient } from 'shared/supabase/init'
-import { addUsersToPrivateMessageChannel } from 'api/helpers/private-messages'
-import { getPrivateUser, getUser } from 'shared/utils'
+import {APIError, APIHandler} from 'api/helpers/endpoint'
+import {filterDefined} from 'common/util/array'
+import {uniq} from 'lodash'
+import {createSupabaseDirectClient} from 'shared/supabase/init'
+import {addUsersToPrivateMessageChannel} from 'api/helpers/private-messages'
+import {getPrivateUser, getUser} from 'shared/utils'
+import * as admin from 'firebase-admin'
export const createPrivateUserMessageChannel: APIHandler<
'create-private-user-message-channel'
> = async (body, auth) => {
+ // Do not use auth.creds.data as its info can be staled. It comes from a client token, which refreshes hourly or so
+ const user = await admin.auth().getUser(auth.uid)
+ // console.log(JSON.stringify(user, null, 2))
+ if (!user?.emailVerified) {
+ throw new APIError(
+ 403,
+ 'You must verify your email to contact people.'
+ )
+ }
+
const userIds = uniq(body.userIds.concat(auth.uid))
const pg = createSupabaseDirectClient()
@@ -41,11 +52,12 @@ export const createPrivateUserMessageChannel: APIHandler<
const currentChannel = await pg.oneOrNone(
`
- select channel_id from private_user_message_channel_members
- group by channel_id
- having array_agg(user_id::text) @> array[$1]::text[]
- and array_agg(user_id::text) <@ array[$1]::text[]
- `,
+ select channel_id
+ from private_user_message_channel_members
+ group by channel_id
+ having array_agg(user_id::text) @> array [$1]::text[]
+ and array_agg(user_id::text) <@ array [$1]::text[]
+ `,
[userIds]
)
if (currentChannel)
@@ -55,17 +67,19 @@ export const createPrivateUserMessageChannel: APIHandler<
}
const channel = await pg.one(
- `insert into private_user_message_channels default values returning id`
+ `insert into private_user_message_channels default
+ values
+ returning id`
)
await pg.none(
`insert into private_user_message_channel_members (channel_id, user_id, role, status)
- values ($1, $2, 'creator', 'joined')
- `,
+ values ($1, $2, 'creator', 'joined')
+ `,
[channel.id, creatorId]
)
const memberIds = userIds.filter((id) => id !== creatorId)
await addUsersToPrivateMessageChannel(memberIds, channel.id, pg)
- return { status: 'success', channelId: Number(channel.id) }
+ return {status: 'success', channelId: Number(channel.id)}
}
diff --git a/web/components/messaging/email-verification-prompt.tsx b/web/components/messaging/email-verification-prompt.tsx
new file mode 100644
index 0000000..e28b7df
--- /dev/null
+++ b/web/components/messaging/email-verification-prompt.tsx
@@ -0,0 +1,26 @@
+import {Button} from 'web/components/buttons/button'
+import {Col} from 'web/components/layout/col'
+import {sendVerificationEmail} from 'web/lib/firebase/email-verification'
+import clsx from 'clsx'
+
+interface EmailVerificationPromptProps {
+ firebaseUser: any
+ t: any
+ className?: string
+}
+
+export function EmailVerificationPrompt(
+ {
+ firebaseUser,
+ t,
+ className
+ }: EmailVerificationPromptProps) {
+ return (
+
+ {t('messaging.email_verification_required', "You must verify your email to message people.")}
+
+
+ )
+}
diff --git a/web/components/messaging/send-message-button.tsx b/web/components/messaging/send-message-button.tsx
index 52a5626..1c2bd50 100644
--- a/web/components/messaging/send-message-button.tsx
+++ b/web/components/messaging/send-message-button.tsx
@@ -15,9 +15,10 @@ import {MAX_COMMENT_LENGTH} from 'common/comment'
import {CommentInputTextArea} from 'web/components/comments/comment-input'
import {Title} from 'web/components/widgets/title'
import {Row} from 'web/components/layout/row'
-import {firebaseLogin} from 'web/lib/firebase/users'
+import {auth, firebaseLogin} from 'web/lib/firebase/users'
import {useT} from 'web/lib/locale'
import {Tooltip} from "web/components/widgets/tooltip";
+import {EmailVerificationPrompt} from 'web/components/messaging/email-verification-prompt'
export const SendMessageButton = (props: {
toUser: User
@@ -25,11 +26,12 @@ export const SendMessageButton = (props: {
includeLabel?: boolean
circleButton?: boolean
}) => {
- const { toUser, currentUser, includeLabel, circleButton } = props
+ const {toUser, currentUser, includeLabel, circleButton} = props
+ const firebaseUser = auth.currentUser
const router = useRouter()
const privateUser = usePrivateUser()
const channelMemberships = useSortedPrivateMessageMemberships(currentUser?.id)
- const { memberIdsByChannelId } = channelMemberships
+ const {memberIdsByChannelId} = channelMemberships
const t = useT()
const [openComposeModal, setOpenComposeModal] = useState(false)
@@ -123,13 +125,15 @@ export const SendMessageButton = (props: {
{t('send_message.title', 'Message')} {toUser.name}
-
+ {firebaseUser?.emailVerified ? :
+
+ }
{error}
diff --git a/web/lib/firebase/email-verification.ts b/web/lib/firebase/email-verification.ts
new file mode 100644
index 0000000..284fedd
--- /dev/null
+++ b/web/lib/firebase/email-verification.ts
@@ -0,0 +1,26 @@
+import toast from "react-hot-toast";
+import {sendEmailVerification, User} from "firebase/auth";
+
+
+export const sendVerificationEmail = async (
+ user: User,
+ t: any
+) => {
+ // if (!privateUser?.email) {
+ // toast.error(t('settings.email.no_email', 'No email found on your account.'))
+ // return
+ // }
+ if (!user) {
+ toast.error(t('settings.email.must_sign_in', 'You must be signed in to send a verification email.'))
+ return
+ }
+ toast
+ .promise(sendEmailVerification(user), {
+ loading: t('settings.email.sending', 'Sending verification email...'),
+ success: t('settings.email.verification_sent', 'Verification email sent — check your inbox and spam.'),
+ error: t('settings.email.verification_failed', 'Failed to send verification email.'),
+ })
+ .catch(() => {
+ console.error("Failed to send verification email")
+ })
+}
\ No newline at end of file
diff --git a/web/messages/de.json b/web/messages/de.json
index 9da99a3..6b4e03f 100644
--- a/web/messages/de.json
+++ b/web/messages/de.json
@@ -822,6 +822,7 @@
"settings.email.verification_failed": "Senden der Bestätigungs-E-Mail fehlgeschlagen.",
"settings.email.verification_sent": "Bestätigungs-E-Mail gesendet – prüfen Sie Ihren Posteingang und Spam-Ordner.",
"settings.email.verified": "E-Mail bestätigt ✔️",
+ "messaging.email_verification_required": "Sie müssen Ihre E-Mail überprüfen, um Personen zu schreiben.",
"settings.general.account": "Konto",
"settings.general.email": "E-Mail",
"settings.general.language": "Sprache",
diff --git a/web/messages/fr.json b/web/messages/fr.json
index 039fbbf..438d0e7 100644
--- a/web/messages/fr.json
+++ b/web/messages/fr.json
@@ -822,6 +822,7 @@
"settings.email.verification_failed": "Échec de l'envoi de l'e-mail de vérification.",
"settings.email.verification_sent": "E-mail de vérification envoyé — vérifiez votre boîte de réception et les spams.",
"settings.email.verified": "E-mail vérifié ✔️",
+ "messaging.email_verification_required": "Vous devez vérifier votre e-mail pour pouvoir envoyer des messages.",
"settings.general.account": "Compte",
"settings.general.email": "E-mail",
"settings.general.language": "Langue",
diff --git a/web/pages/messages/index.tsx b/web/pages/messages/index.tsx
index f1d6a3c..5101beb 100644
--- a/web/pages/messages/index.tsx
+++ b/web/pages/messages/index.tsx
@@ -22,12 +22,15 @@ import {BannedBadge} from 'web/components/widgets/user-link'
import {PrivateMessageChannel} from 'common/supabase/private-messages'
import {SEO} from "web/components/SEO";
import {useT} from 'web/lib/locale'
+import {auth} from "web/lib/firebase/users";
+import {EmailVerificationPrompt} from "web/components/messaging/email-verification-prompt";
export default function MessagesPage() {
useRedirectIfSignedOut()
const currentUser = useUser()
+ const firebaseUser = auth.currentUser
const t = useT()
return (
@@ -36,7 +39,11 @@ export default function MessagesPage() {
description={t('messages.seo.description', 'Your Messages')}
url={`/messages`}
/>
- {currentUser && }
+ {currentUser && (
+ firebaseUser?.emailVerified ?
+
+ :
+ )}
)
}
diff --git a/web/pages/settings.tsx b/web/pages/settings.tsx
index ecc7aa8..f4b3d12 100644
--- a/web/pages/settings.tsx
+++ b/web/pages/settings.tsx
@@ -12,7 +12,7 @@ import {useRedirectIfSignedOut} from "web/hooks/use-redirect-if-signed-out";
import {deleteAccount} from "web/lib/util/delete";
import router from "next/router";
import {Button} from "web/components/buttons/button";
-import {sendEmailVerification, updateEmail} from 'firebase/auth';
+import {updateEmail} from 'firebase/auth';
import {auth} from "web/lib/firebase/users";
import {NotificationSettings} from "web/components/notifications";
import ThemeIcon from "web/components/theme-icon";
@@ -22,6 +22,7 @@ import {AboutSettings} from "web/components/about-settings";
import {LanguagePicker} from "web/components/language/language-picker";
import {useT} from "web/lib/locale";
import HiddenProfilesModal from 'web/components/settings/hidden-profiles-modal'
+import {sendVerificationEmail} from "web/lib/firebase/email-verification";
export default function NotificationsPage() {
const t = useT()
@@ -107,27 +108,6 @@ const LoadedGeneralSettings = (props: {
changeUserEmail(data.newEmail)
}
-
- const sendVerificationEmail = async () => {
- if (!privateUser?.email) {
- toast.error(t('settings.email.no_email', 'No email found on your account.'))
- return
- }
- if (!user) {
- toast.error(t('settings.email.must_sign_in', 'You must be signed in to send a verification email.'))
- return
- }
- toast
- .promise(sendEmailVerification(user), {
- loading: t('settings.email.sending', 'Sending verification email...'),
- success: t('settings.email.verification_sent', 'Verification email sent — check your inbox and spam.'),
- error: t('settings.email.verification_failed', 'Failed to send verification email.'),
- })
- .catch(() => {
- console.log("Failed to send verification email")
- })
- }
-
const isEmailVerified = user.emailVerified
return <>
@@ -147,7 +127,8 @@ const LoadedGeneralSettings = (props: {
{t('settings.general.account', 'Account')}
{t('settings.general.email', 'Email')}
-