diff --git a/.github/workflows/check_and_build_pull_requests.yaml b/.github/workflows/check_and_build_pull_requests.yaml index 821ea815..95d4c2c2 100644 --- a/.github/workflows/check_and_build_pull_requests.yaml +++ b/.github/workflows/check_and_build_pull_requests.yaml @@ -22,7 +22,7 @@ jobs: - name: Checkout Code 🛎 uses: actions/checkout@v4 - - name: Cache turbo build setup + - name: Cache turbo build setup 🚀 uses: actions/cache@v4 with: path: .turbo diff --git a/apps/mail-bridge/routes/postal/events/[...mailServer].post.ts b/apps/mail-bridge/routes/postal/events/[...mailServer].post.ts index af4802a2..f291b8a0 100644 --- a/apps/mail-bridge/routes/postal/events/[...mailServer].post.ts +++ b/apps/mail-bridge/routes/postal/events/[...mailServer].post.ts @@ -1,3 +1,7 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +// TODO: Make the event handler + /** * used for all event notifications from Postal */ diff --git a/apps/mail-bridge/routes/postal/mail/inbound/[...mailServer].post.ts b/apps/mail-bridge/routes/postal/mail/inbound/[...mailServer].post.ts index 5cef66aa..62915011 100644 --- a/apps/mail-bridge/routes/postal/mail/inbound/[...mailServer].post.ts +++ b/apps/mail-bridge/routes/postal/mail/inbound/[...mailServer].post.ts @@ -1,6 +1,6 @@ import { db } from '@u22n/database'; import { simpleParser } from 'mailparser'; -// @ts-expect-error, No types yet +// @ts-expect-error, not typed yet import { authenticate } from 'mailauth'; import { and, eq, inArray } from '@u22n/database/orm'; import type { InferInsertModel } from '@u22n/database/orm'; @@ -55,24 +55,26 @@ export default eventHandler(async (event) => { return; } - let orgId: number | null = null; + let orgId: number = 0; let orgPublicId: string | null = null; - - if (!event.context.params?.mailServer) { - console.error('⛔ no mailserver found in the event context', { + const [orgIdStr, mailserverId] = event.context.params!.mailServer!.split('/'); + if (!orgIdStr || !mailserverId) { + console.error('⛔ no orgId or mailserverId found', { payloadPostalEmailId }); return; } - const [orgIdStr = '', mailserverId = ''] = - event.context.params.mailServer.split('/'); - if (orgIdStr === '0' || mailserverId === 'root') { // handle for root emails // get the email identity for the root email - const [rootEmailUsername = '', rootEmailDomain = ''] = - payloadEmailTo.split('@'); + const [rootEmailUsername, rootEmailDomain] = payloadEmailTo.split('@'); + if (!rootEmailUsername || !rootEmailDomain) { + console.error('⛔ invalid root email username or domain', { + payloadPostalEmailId + }); + return; + } const rootEmailIdentity = await db.query.emailIdentities.findFirst({ where: and( eq(emailIdentities.username, rootEmailUsername), @@ -100,6 +102,8 @@ export default eventHandler(async (event) => { orgId = rootEmailIdentity.orgId; orgPublicId = rootEmailIdentity.org.publicId; } else { + orgId = Number(orgIdStr); + // handle for org emails if (!validateTypeId('postalServers', mailserverId)) { console.error('⛔ invalid mailserver id', { @@ -123,7 +127,6 @@ export default eventHandler(async (event) => { } } }); - // prelimary checks if (!mailServer || +mailServer.orgId !== orgId) { console.error('⛔ mailserver not found or does not belong to this org', { @@ -138,11 +141,19 @@ export default eventHandler(async (event) => { orgPublicId = mailServer.org.publicId; } + if (orgId === 0 || !orgPublicId) { + console.error('⛔ orgId or orgPublicId not found', { + payloadPostalEmailId + }); + return; + } + //* parse the email payload const payloadEmail = Buffer.from(payloadEmailB64, 'base64').toString('utf-8'); const parsedEmail = await simpleParser(payloadEmail); //! verify email auth (DKIM, SPF, etc.) - unhandled right now + // eslint-disable-next-line @typescript-eslint/no-unused-vars const auth = await authenticate(payloadEmail, { trustReceived: true }); @@ -162,6 +173,17 @@ export default eventHandler(async (event) => { } // Extract key email properties + if ( + !parsedEmail.from || + !parsedEmail.to || + !parsedEmail.subject || + !parsedEmail.messageId + ) { + console.error('⛔ missing email attributes', { + payloadPostalEmailId + }); + return; + } if (parsedEmail.from.value.length > 1) { console.error( '⛔ multiple from addresses detected in a message, only using first email address', @@ -238,6 +260,20 @@ export default eventHandler(async (event) => { : Promise.resolve([]) ]); + if ( + !messageToPlatformObject || + !messageToPlatformObject[0] || + !messageFromPlatformObject || + !messageFromPlatformObject[0] + ) { + console.error( + '⛔ no messageToPlatformObject or messageFromPlatformObject found', + { + payloadPostalEmailId + } + ); + return; + } // check the from contact and update their signature if it is null if (messageFromPlatformObject[0]?.type === 'contact') { const contact = await db.query.contacts.findFirst({ @@ -250,7 +286,13 @@ export default eventHandler(async (event) => { signaturePlainText: true } }); - if (!contact?.signaturePlainText) { + if (!contact) { + console.error('⛔ no contact found for from address', { + payloadPostalEmailId + }); + return; + } + if (!contact.signaturePlainText) { await db .update(contacts) .set({ @@ -279,7 +321,7 @@ export default eventHandler(async (event) => { // if theres no email identity ids, then we assume that this email has no destination, so we need to send the bounce message if (!emailIdentityIds.length) { - //! SEND BOUNCE MESSAGE + // !FIX SEND BOUNCE MESSAGE console.error('⛔ no email identity ids found', { messageAddressIds }); return; @@ -325,7 +367,7 @@ export default eventHandler(async (event) => { //* start to process the conversation let hasReplyToButIsNewConvo: boolean | null = null; - let convoId: number | null = null; + let convoId: number = 0; let replyToId: number | null = null; let subjectId: number | null = null; @@ -336,6 +378,12 @@ export default eventHandler(async (event) => { const fromAddressPlatformObject = messageFromPlatformObject.find( (a) => a.ref === 'from' ); + if (!fromAddressPlatformObject) { + console.error('⛔ no from address platform object found', { + payloadPostalEmailId + }); + return; + } const convoParticipantsToAdd: ConvoParticipantInsertDbType[] = []; // if the email has a reply to header, then we need to check if a message exists in the system with that reply to id @@ -382,6 +430,15 @@ export default eventHandler(async (event) => { convoId = existingMessage.convoId; replyToId = existingMessage.id; + if (!existingMessage.convoId || convoId === 0) { + console.error('⛔ no convoId found for existing message', { + payloadPostalEmailId + }); + return; + } + if (!existingMessage.subject) { + existingMessage.subject = { id: 0, subject: 'No Subject' }; + } // check if the subject is the same as existing, if not, add a new subject to the convo if (subject !== existingMessage.subject?.subject) { const newSubject = await db.insert(convos).values({ @@ -535,8 +592,9 @@ export default eventHandler(async (event) => { id: true } }); - fromAddressParticipantId = contactParticipant?.id || null; - } else if (fromAddressPlatformObject?.type === 'emailIdentity') { + // @ts-expect-error we check and define earlier up + fromAddressParticipantId = contactParticipant.id; + } else if (fromAddressPlatformObject.type === 'emailIdentity') { // we need to get the first person/group in the routing rule and add them to the convo const emailIdentityParticipant = await db.query.emailIdentities.findFirst( { @@ -565,26 +623,31 @@ export default eventHandler(async (event) => { } ); const firstDestination = - emailIdentityParticipant?.routingRules.destinations[0]; + // @ts-expect-error we check and define earlier up + emailIdentityParticipant.routingRules.destinations[0]; let convoParticipantFromAddressIdentity; - if (firstDestination?.orgMemberId) { + // @ts-expect-error we check and define earlier up + if (firstDestination.orgMemberId) { convoParticipantFromAddressIdentity = await db.query.convoParticipants.findFirst({ where: and( eq(convoParticipants.orgId, orgId), - eq(convoParticipants.convoId, convoId || 0), + eq(convoParticipants.convoId, convoId), + // @ts-expect-error we check and define earlier up eq(convoParticipants.orgMemberId, firstDestination.orgMemberId) ), columns: { id: true } }); - } else if (firstDestination?.groupId) { + // @ts-expect-error we check and define earlier up + } else if (firstDestination.groupId) { convoParticipantFromAddressIdentity = await db.query.convoParticipants.findFirst({ where: and( eq(convoParticipants.orgId, orgId), - eq(convoParticipants.convoId, convoId || 0), + eq(convoParticipants.convoId, convoId), + // @ts-expect-error we check and define earlier up eq(convoParticipants.userGroupId, firstDestination.groupId) ), columns: { @@ -592,8 +655,8 @@ export default eventHandler(async (event) => { } }); } - fromAddressParticipantId = - convoParticipantFromAddressIdentity?.id || null; + // @ts-expect-error we check and define earlier up + fromAddressParticipantId = convoParticipantFromAddressIdentity.id; } } @@ -604,20 +667,26 @@ export default eventHandler(async (event) => { to: messageToPlatformObject.map((a) => { return { id: a.id, - type: a.type + type: a.type, + publicId: a.publicId, + email: a.email }; }), from: messageFromPlatformObject.map((a) => { return { id: a.id, - type: a.type + type: a.type, + publicId: a.publicId, + email: a.email }; }), cc: messageCcPlatformObject.map((a) => { return { id: a.id, - type: a.type + type: a.type, + publicId: a.publicId, + email: a.email }; }) || [], postalMessages: [ @@ -625,8 +694,7 @@ export default eventHandler(async (event) => { id: payloadPostalEmailId, postalMessageId: messageId, recipient: payloadEmailTo, - // @ts-expect-error, not sure about this yet - token: null + token: '' } ], emailHeaders: JSON.stringify(parsedEmail.headers) @@ -641,7 +709,6 @@ export default eventHandler(async (event) => { convoEntryBody, tipTapExtensions ); - const insertNewConvoEntry = await db.insert(convoEntries).values({ orgId: orgId, publicId: typeIdGenerator('convoEntries'), @@ -682,7 +749,7 @@ export default eventHandler(async (event) => { publicId: string; signedUrl: string; }; - const preUpload = (await fetch( + const preUpload: PreSignedData = await fetch( `${useRuntimeConfig().storage.url}/api/attachments/internalPresign`, { method: 'post', @@ -696,7 +763,7 @@ export default eventHandler(async (event) => { filename: input.fileName }) } - ).then((res) => res.json())) as PreSignedData; + ).then((res: Response) => res.json() as Promise); if (!preUpload || !preUpload.publicId || !preUpload.signedUrl) { throw new Error('Missing attachmentPublicId or presignedUrl'); } @@ -736,8 +803,8 @@ export default eventHandler(async (event) => { await Promise.all( attachments.map((attachment) => { return uploadAndAttachAttachment({ - orgId: orgId || 0, - fileName: attachment.filename || '', + orgId: orgId, + fileName: attachment.filename || 'No Filename', fileType: attachment.contentType, fileContent: attachment.content, convoId: convoId || 0, diff --git a/apps/mail-bridge/routes/postal/mail/recovery.post.ts b/apps/mail-bridge/routes/postal/mail/recovery.post.ts deleted file mode 100644 index be527a0c..00000000 --- a/apps/mail-bridge/routes/postal/mail/recovery.post.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * This endpoint is used as the webhook destination for incoming mail sent to the recovery email address domain. - * - */ - -import { eventHandler, readBody } from '#imports'; - -export default eventHandler((event) => { - const _mailBody = readBody(event); - - //! Verify the source of webhook call - //! extract the username from to email address of the mail body - //! Verify the user requested a recovery email and get the recovery email address from redis - //! extract the recovery code from the mail body - //! send the recovery code to the user's recovery email address - - return { status: "I'm Alive 🏝️" }; -}); diff --git a/apps/mail-bridge/trpc/routers/sendMailRouter.ts b/apps/mail-bridge/trpc/routers/sendMailRouter.ts index 31347448..4987dc65 100644 --- a/apps/mail-bridge/trpc/routers/sendMailRouter.ts +++ b/apps/mail-bridge/trpc/routers/sendMailRouter.ts @@ -56,7 +56,7 @@ export const sendMailRouter = router({ } ] } - } as ConvoEntryMetadata + } }; } @@ -77,6 +77,7 @@ export const sendMailRouter = router({ where: eq(emailIdentities.publicId, sendAsEmailIdentityPublicId), columns: { id: true, + publicId: true, username: true, domainName: true, sendName: true, @@ -256,7 +257,17 @@ export const sendMailRouter = router({ const entryMetadata: ConvoEntryMetadata = { email: { to: [], - from: [{ id: +sendAsEmailIdentity.id, type: 'emailIdentity' }], + from: [ + { + id: +sendAsEmailIdentity.id, + type: 'emailIdentity', + publicId: sendAsEmailIdentity.publicId, + email: + sendAsEmailIdentity.username + + '@' + + sendAsEmailIdentity.domainName + } + ], cc: [], messageId: sendMailPostalResponse.data.message_id, postalMessages: transformedMessages.map((message) => ({ @@ -267,7 +278,7 @@ export const sendMailRouter = router({ }; return { success: true, - metadata: entryMetadata + metadata: entryMetadata as ConvoEntryMetadata }; } else { console.error( diff --git a/apps/mail-bridge/types.ts b/apps/mail-bridge/types.ts index d56c6e0c..5c71a176 100644 --- a/apps/mail-bridge/types.ts +++ b/apps/mail-bridge/types.ts @@ -14,6 +14,8 @@ export interface postalEmailPayload { export interface MessageParseAddressPlatformObject { id: number; type: 'contact' | 'emailIdentity'; + publicId: string; + email: string; contactType: | 'person' | 'product' diff --git a/apps/mail-bridge/utils/contactParsing.ts b/apps/mail-bridge/utils/contactParsing.ts index ca9f1249..6c410eb7 100644 --- a/apps/mail-bridge/utils/contactParsing.ts +++ b/apps/mail-bridge/utils/contactParsing.ts @@ -17,11 +17,12 @@ export async function parseAddressIds(input: { const parsedAddressIds: MessageParseAddressPlatformObject[] = []; for (const addressObject of input.addresses) { if (!addressObject.address) { - continue; + return []; + } + const [emailUsername, emailDomain] = addressObject.address.split('@'); + if (!emailDomain || !emailUsername) { + return []; } - const [emailUsername = '', emailDomain = ''] = - addressObject.address.split('@'); - // check if email is existing contact const contactQuery = await db.query.contacts.findFirst({ where: and( @@ -31,6 +32,9 @@ export async function parseAddressIds(input: { ), columns: { id: true, + publicId: true, + emailUsername: true, + emailDomain: true, name: true, type: true } @@ -39,6 +43,8 @@ export async function parseAddressIds(input: { parsedAddressIds.push({ id: contactQuery.id, type: 'contact', + publicId: contactQuery.publicId, + email: contactQuery.emailUsername + '@' + contactQuery.emailDomain, contactType: contactQuery.type, ref: input.addressType }); @@ -61,7 +67,10 @@ export async function parseAddressIds(input: { eq(emailIdentities.username, emailUsername) ), columns: { - id: true + id: true, + publicId: true, + username: true, + domainName: true } }); @@ -69,6 +78,9 @@ export async function parseAddressIds(input: { parsedAddressIds.push({ id: emailIdentityQuery.id, type: 'emailIdentity', + publicId: emailIdentityQuery.publicId, + email: + emailIdentityQuery.username + '@' + emailIdentityQuery.domainName, contactType: null, ref: input.addressType }); @@ -85,7 +97,10 @@ export async function parseAddressIds(input: { eq(emailIdentities.isCatchAll, true) ), columns: { - id: true + id: true, + publicId: true, + username: true, + domainName: true } }); @@ -93,6 +108,11 @@ export async function parseAddressIds(input: { parsedAddressIds.push({ id: emailIdentityCatchAllQuery.id, type: 'emailIdentity', + publicId: emailIdentityCatchAllQuery.publicId, + email: + emailIdentityCatchAllQuery.username + + '@' + + emailIdentityCatchAllQuery.domainName, contactType: null, ref: input.addressType }); @@ -108,6 +128,7 @@ export async function parseAddressIds(input: { id: true } }); + let contactGlobalReputationId: number | null = null; if (contactGlobalReputation) { contactGlobalReputationId = contactGlobalReputation.id; @@ -127,16 +148,19 @@ export async function parseAddressIds(input: { const contactInsert = await db.insert(contacts).values({ publicId: typeIdGenerator('contacts'), orgId: input.orgId, - reputationId: contactGlobalReputationId || 0, + reputationId: +contactGlobalReputationId!, type: 'unknown', emailUsername: emailUsername, emailDomain: emailDomain, name: addressObject.name || emailUsername + '@' + emailDomain, screenerStatus: 'pending' }); + parsedAddressIds.push({ id: Number(contactInsert.insertId), type: 'contact', + publicId: contactInsert.insertId, + email: emailUsername + '@' + emailDomain, contactType: 'unknown', ref: input.addressType }); diff --git a/apps/platform/trpc/routers/convoRouter/convoRouter.ts b/apps/platform/trpc/routers/convoRouter/convoRouter.ts index 3e1c181d..34ec9ec9 100644 --- a/apps/platform/trpc/routers/convoRouter/convoRouter.ts +++ b/apps/platform/trpc/routers/convoRouter/convoRouter.ts @@ -24,7 +24,8 @@ import { userGroupMembers, type ConvoEntryMetadataEmailAddress, convoAttachments, - pendingAttachments + pendingAttachments, + type ConvoEntryMetadata } from '@u22n/database/schema'; import { typeIdValidator, @@ -126,6 +127,7 @@ export const convoRouter = router({ where: eq(contacts.publicId, convoMessageTo.publicId), columns: { id: true, + publicId: true, emailUsername: true, emailDomain: true } @@ -136,7 +138,12 @@ export const convoRouter = router({ message: 'TO address contact not found' }); } - convoMetadataToAddress = { id: +contactResponse.id, type: 'contact' }; + convoMetadataToAddress = { + id: Number(contactResponse.id), + type: 'contact', + publicId: contactResponse.publicId, + email: `${contactResponse.emailUsername}@${contactResponse.emailDomain}` + }; return `${contactResponse.emailUsername}@${contactResponse.emailDomain}`; } else if (convoMessageToType === 'group') { if (!validateTypeId('userGroups', convoMessageTo.publicId)) { @@ -175,6 +182,8 @@ export const convoRouter = router({ with: { identity: { columns: { + id: true, + publicId: true, username: true, domainName: true } @@ -188,8 +197,10 @@ export const convoRouter = router({ }); } convoMetadataToAddress = { - id: +emailIdentitiesResponse.id, - type: 'emailIdentity' + id: Number(emailIdentitiesResponse.identity.id), + type: 'emailIdentity', + publicId: emailIdentitiesResponse.identity.publicId, + email: `${emailIdentitiesResponse.identity.username}@${emailIdentitiesResponse.identity.domainName}` }; return `${emailIdentitiesResponse.identity.username}@${emailIdentitiesResponse.identity.domainName}`; } else if (convoMessageToType === 'user') { @@ -235,6 +246,8 @@ export const convoRouter = router({ with: { identity: { columns: { + id: true, + publicId: true, username: true, domainName: true } @@ -248,8 +261,10 @@ export const convoRouter = router({ }); } convoMetadataToAddress = { - id: +emailIdentitiesResponse.id, - type: 'emailIdentity' + id: Number(emailIdentitiesResponse.identity.id), + type: 'emailIdentity', + publicId: emailIdentitiesResponse.identity.publicId, + email: `${emailIdentitiesResponse.identity.username}@${emailIdentitiesResponse.identity.domainName}` }; return `${emailIdentitiesResponse.identity.username}@${emailIdentitiesResponse.identity.domainName}`; } else { @@ -346,7 +361,10 @@ export const convoRouter = router({ ), columns: { id: true, - reputationId: true + reputationId: true, + publicId: true, + emailUsername: true, + emailDomain: true } }); @@ -354,12 +372,16 @@ export const convoRouter = router({ if (newConvoToEmailAddress === email && !convoMetadataToAddress) { convoMetadataToAddress = { id: +existingContact.id, - type: 'contact' + type: 'contact', + publicId: existingContact.publicId, + email: `${existingContact.emailUsername}@${existingContact.emailDomain}` }; } else { convoMetadataCcAddresses.push({ - id: +existingContact.id, - type: 'contact' + id: Number(existingContact.id), + type: 'contact', + publicId: existingContact.publicId, + email: `${existingContact.emailUsername}@${existingContact.emailDomain}` }); } orgContactIds.push(existingContact.id); @@ -389,12 +411,16 @@ export const convoRouter = router({ if (newConvoToEmailAddress === email && !convoMetadataToAddress) { convoMetadataToAddress = { id: +newContactInsertResponse.insertId, - type: 'contact' + type: 'contact', + publicId: newContactPublicId, + email: `${emailUsername}@${emailDomain}` }; } else { convoMetadataCcAddresses.push({ id: +newContactInsertResponse.insertId, - type: 'contact' + type: 'contact', + publicId: newContactPublicId, + email: `${emailUsername}@${emailDomain}` }); } orgContactIds.push(+newContactInsertResponse.insertId); @@ -422,12 +448,16 @@ export const convoRouter = router({ if (newConvoToEmailAddress === email && !convoMetadataToAddress) { convoMetadataToAddress = { id: +newContactInsertResponse.insertId, - type: 'contact' + type: 'contact', + publicId: newContactPublicId, + email: `${emailUsername}@${emailDomain}` }; } else { convoMetadataCcAddresses.push({ id: +newContactInsertResponse.insertId, - type: 'contact' + type: 'contact', + publicId: newContactPublicId, + email: `${emailUsername}@${emailDomain}` }); } orgContactIds.push(+newContactInsertResponse.insertId); @@ -632,6 +662,8 @@ export const convoRouter = router({ with: { identity: { columns: { + id: true, + publicId: true, username: true, domainName: true } @@ -664,8 +696,10 @@ export const convoRouter = router({ } if (emailIdentityResponse) { convoMetadataCcAddresses.push({ - id: +emailIdentityResponse.id, - type: 'emailIdentity' + id: +emailIdentityResponse.identity.id, + type: 'emailIdentity', + publicId: emailIdentityResponse.identity.publicId, + email: `${emailIdentityResponse.identity.username}@${emailIdentityResponse.identity.domainName}` }); ccEmailAddresses.push( `${emailIdentityResponse.identity.username}@${emailIdentityResponse.identity.domainName}` @@ -691,6 +725,8 @@ export const convoRouter = router({ with: { identity: { columns: { + id: true, + publicId: true, username: true, domainName: true } @@ -718,7 +754,9 @@ export const convoRouter = router({ if (emailIdentityResponse) { convoMetadataCcAddresses.push({ id: +emailIdentityResponse.id, - type: 'emailIdentity' + type: 'emailIdentity', + publicId: emailIdentityResponse.identity.publicId, + email: `${emailIdentityResponse.identity.username}@${emailIdentityResponse.identity.domainName}` }); ccEmailAddresses.push( `${emailIdentityResponse.identity.username}@${emailIdentityResponse.identity.domainName}` @@ -764,12 +802,13 @@ export const convoRouter = router({ mailBridgeSendMailResponse.metadata.email.to = [ convoMetadataToAddress! ]; - mailBridgeSendMailResponse.metadata.email.cc = convoMetadataCcAddresses; + mailBridgeSendMailResponse.metadata.email.cc = + convoMetadataCcAddresses!; await db .update(convoEntries) .set({ - metadata: mailBridgeSendMailResponse.metadata + metadata: mailBridgeSendMailResponse.metadata as ConvoEntryMetadata }) .where(eq(convoEntries.id, +insertConvoEntryResponse.insertId)); @@ -941,7 +980,8 @@ export const convoRouter = router({ emailDomain: true, setName: true, signaturePlainText: true, - signatureHtml: true + signatureHtml: true, + type: true } } } diff --git a/apps/platform/utils/auth/passkeys.ts b/apps/platform/utils/auth/passkeys.ts index 59d5f201..85ec185b 100644 --- a/apps/platform/utils/auth/passkeys.ts +++ b/apps/platform/utils/auth/passkeys.ts @@ -126,6 +126,7 @@ async function generateAuthenticationOptions({ async function verifyAuthenticationResponse({ authenticationResponse, + // eslint-disable-next-line @typescript-eslint/no-unused-vars expectedAllowedCredentials, authChallengeId }: { diff --git a/apps/web-app/app.config.ts b/apps/web-app/app.config.ts index caae5bb9..f4fc2759 100644 --- a/apps/web-app/app.config.ts +++ b/apps/web-app/app.config.ts @@ -537,6 +537,7 @@ export default defineAppConfig({ } }); +// eslint-disable-next-line @typescript-eslint/no-unused-vars const sand = [ 'bg-sand-1', 'bg-sand-2', @@ -563,6 +564,8 @@ const sand = [ 'ring-sand-9', 'active:ring-sand-9' ]; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars const bronze = [ 'bg-bronze-1', 'bg-bronze-2', @@ -589,6 +592,8 @@ const bronze = [ 'ring-bronze-9', 'active:ring-bronze-9' ]; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars const green = [ 'bg-green-1', 'bg-green-2', @@ -615,6 +620,8 @@ const green = [ 'ring-green-9', 'active:ring-green-9' ]; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars const red = [ 'bg-red-1', 'bg-red-2', @@ -641,6 +648,8 @@ const red = [ 'ring-red-9', 'active:ring-red-9' ]; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars const amber = [ 'bg-amber-1', 'bg-amber-2', @@ -667,6 +676,8 @@ const amber = [ 'ring-amber-9', 'active:ring-amber-9' ]; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars const blue = [ 'bg-blue-1', 'bg-blue-2', diff --git a/apps/web-app/app.vue b/apps/web-app/app.vue index 55dc2b61..e9aded31 100644 --- a/apps/web-app/app.vue +++ b/apps/web-app/app.vue @@ -1,11 +1,6 @@ - - diff --git a/apps/web-app/components/convos/convoMessageItem.vue b/apps/web-app/components/convos/convoMessageItem.vue index 33d96601..70a26b7b 100644 --- a/apps/web-app/components/convos/convoMessageItem.vue +++ b/apps/web-app/components/convos/convoMessageItem.vue @@ -13,9 +13,12 @@ type ConvoEntryItem = NonNullable[number]; type Props = { entry: ConvoEntryItem; + isReplyTo: boolean; }; const props = defineProps(); + const emits = defineEmits(['set-as-reply-to']); + const participantPublicId = inject('participantPublicId'); const convoParticipants = inject('convoParticipants'); @@ -45,7 +48,7 @@ const typeClasses = computed(() => { switch (tempColor) { case 'message': - return 'bg-white dark:bg-black'; + return 'bg-base-2 dark:bg-base-2'; default: return 'bg-gray-100 dark:bg-gray-900'; } @@ -57,12 +60,16 @@ const convoBubbleClasses = computed(() => { return `${typeClasses.value}`; }); + + function setAsReplyTo() { + emits('set-as-reply-to'); + }