sebastian2357 ff366a4075
fix(webapp): mobile optimization (#8516)
* - optimized header
- added possibility of extra mobile logo

* - changed behavior of NotificationMenu link get directly open for mobile

* - moved notification links to the top of the menu

* - optimized chat view for mobile

* - added logo branding structure

* - added logo branding structure

* - fixed chat height

* - fixed paddings for internal pages

* Fix linting

* Fix linting

---------

Co-authored-by: Sebastian Stein <sebastian@codepassion.de>
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-05-09 17:04:06 +00:00

322 lines
9.0 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import path from 'node:path'
import Email from 'email-templates'
import { createTransport } from 'nodemailer'
// import type Email as EmailType from '@types/email-templates'
import CONFIG, { nodemailerTransportOptions } from '@config/index'
import logosWebapp from '@config/logosBranded'
import metadata from '@config/metadata'
import { UserDbProperties } from '@db/types/User'
const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI)
const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI)
const defaultParams = {
welcomeImageUrl,
APPLICATION_NAME: CONFIG.APPLICATION_NAME,
ORGANIZATION_NAME: metadata.ORGANIZATION_NAME,
ORGANIZATION_URL: CONFIG.ORGANIZATION_URL,
supportUrl: CONFIG.SUPPORT_URL,
settingsUrl,
renderSettingsUrl: true,
}
const from = `${CONFIG.APPLICATION_NAME} <${CONFIG.EMAIL_DEFAULT_SENDER}>`
const transport = createTransport(nodemailerTransportOptions)
const email = new Email({
message: {
from,
},
transport,
i18n: {
locales: ['en', 'de'],
defaultLocale: 'en',
retryInDefaultLocale: false,
directory: path.join(__dirname, 'locales'),
updateFiles: false,
objectNotation: true,
mustacheConfig: {
tags: ['{', '}'],
disable: false,
},
},
send: CONFIG.SEND_MAIL,
preview: false,
// This is very useful to see the emails sent by the unit tests
/*
preview: {
open: {
app: 'brave-browser',
},
},
*/
})
interface OriginalMessage {
to: string
from: string
attachments: string[]
subject: string
html: string
text: string
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const sendNotificationMail = async (notification: any): Promise<OriginalMessage> => {
const locale = notification?.to?.locale
const to = notification?.email
const name = notification?.to?.name
const template = notification?.reason
try {
const { originalMessage } = await email.send({
template: path.join(__dirname, 'templates', template),
message: {
to,
},
locals: {
...defaultParams,
locale,
name,
postTitle:
notification?.from?.__typename === 'Comment'
? notification?.from?.post?.title
: notification?.from?.title,
postUrl: new URL(
notification?.from?.__typename === 'Comment'
? `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}`
: `/post/${notification?.from?.id}/${notification?.from?.slug}`,
CONFIG.CLIENT_URI,
),
postAuthorName:
notification?.from?.__typename === 'Comment'
? undefined
: notification?.from?.author?.name,
postAuthorUrl:
notification?.from?.__typename === 'Comment'
? undefined
: new URL(
`user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
CONFIG.CLIENT_URI,
),
commenterName:
notification?.from?.__typename === 'Comment'
? notification?.from?.author?.name
: undefined,
commenterUrl:
notification?.from?.__typename === 'Comment'
? new URL(
`/profile/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
CONFIG.CLIENT_URI,
)
: undefined,
commentUrl:
notification?.from?.__typename === 'Comment'
? new URL(
`/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}#commentId-${notification?.from?.id}`,
CONFIG.CLIENT_URI,
)
: undefined,
// chattingUser: 'SR-71',
// chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
groupUrl:
notification?.from?.__typename === 'Group'
? new URL(
`/groups/${notification?.from?.id}/${notification?.from?.slug}`,
CONFIG.CLIENT_URI,
)
: undefined,
groupName:
notification?.from?.__typename === 'Group' ? notification?.from?.name : undefined,
groupRelatedUserName:
notification?.from?.__typename === 'Group' ? notification?.relatedUser?.name : undefined,
groupRelatedUserUrl:
notification?.from?.__typename === 'Group'
? new URL(
`/profile/${notification?.relatedUser?.id}/${notification?.relatedUser?.slug}`,
CONFIG.CLIENT_URI,
)
: undefined,
},
})
return originalMessage as OriginalMessage
} catch (error) {
throw new Error(error)
}
}
export interface ChatMessageEmailInput {
senderUser: UserDbProperties
recipientUser: UserDbProperties
email: string
}
export const sendChatMessageMail = async (
data: ChatMessageEmailInput,
): Promise<OriginalMessage> => {
const { senderUser, recipientUser } = data
const to = data.email
try {
const { originalMessage } = await email.send({
template: path.join(__dirname, 'templates', 'chat_message'),
message: {
to,
},
locals: {
...defaultParams,
locale: recipientUser.locale,
name: recipientUser.name,
chattingUser: senderUser.name,
chattingUserUrl: new URL(`/profile/${senderUser.id}/${senderUser.slug}`, CONFIG.CLIENT_URI),
chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
},
})
return originalMessage as OriginalMessage
} catch (error) {
throw new Error(error)
}
}
interface VerifyMailInput {
email: string
nonce: string
locale: string
}
interface RegistrationMailInput extends VerifyMailInput {
inviteCode?: string
}
export const sendRegistrationMail = async (
data: RegistrationMailInput,
): Promise<OriginalMessage> => {
const { nonce, locale, inviteCode } = data
const to = data.email
const actionUrl = new URL('/registration', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('email', to)
actionUrl.searchParams.set('nonce', nonce)
if (inviteCode) {
actionUrl.searchParams.set('inviteCode', inviteCode)
actionUrl.searchParams.set('method', 'invite-code')
} else {
actionUrl.searchParams.set('method', 'invite-mail')
}
try {
const { originalMessage } = await email.send({
template: path.join(__dirname, 'templates', 'registration'),
message: {
to,
},
locals: {
...defaultParams,
locale,
actionUrl,
nonce,
renderSettingsUrl: false,
},
})
return originalMessage as OriginalMessage
} catch (error) {
throw new Error(error)
}
}
interface EmailVerificationInput extends VerifyMailInput {
name: string
}
export const sendEmailVerification = async (
data: EmailVerificationInput,
): Promise<OriginalMessage> => {
const { nonce, locale, name } = data
const to = data.email
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('email', to)
actionUrl.searchParams.set('nonce', nonce)
try {
const { originalMessage } = await email.send({
template: path.join(__dirname, 'templates', 'emailVerification'),
message: {
to,
},
locals: {
...defaultParams,
locale,
actionUrl,
nonce,
name,
renderSettingsUrl: false,
},
})
return originalMessage as OriginalMessage
} catch (error) {
throw new Error(error)
}
}
export const sendResetPasswordMail = async (
data: EmailVerificationInput,
): Promise<OriginalMessage> => {
const { nonce, locale, name } = data
const to = data.email
const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('email', to)
actionUrl.searchParams.set('nonce', nonce)
try {
const { originalMessage } = await email.send({
template: path.join(__dirname, 'templates', 'resetPassword'),
message: {
to,
},
locals: {
...defaultParams,
locale,
actionUrl,
nonce,
name,
renderSettingsUrl: false,
},
})
return originalMessage as OriginalMessage
} catch (error) {
throw new Error(error)
}
}
export const sendWrongEmail = async (data: {
locale: string
email: string
}): Promise<OriginalMessage> => {
const { locale } = data
const to = data.email
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI)
try {
const { originalMessage } = await email.send({
template: path.join(__dirname, 'templates', 'wrongEmail'),
message: {
to,
},
locals: {
...defaultParams,
locale,
actionUrl,
renderSettingsUrl: false,
},
})
return originalMessage as OriginalMessage
} catch (error) {
throw new Error(error)
}
}