4150 i should be able to view my emails even if ive set my account visibility to metadata (#4156)

* improve timeline messaging to allow users to view the threads to which they participated

* working

* improvement

* improvements

* improvements

* fix

* remove unnecessary type
This commit is contained in:
bosiraphael
2024-02-23 17:07:49 +01:00
committed by GitHub
parent 4b22c0404e
commit 06c4665a44
3 changed files with 92 additions and 20 deletions

View File

@ -3,9 +3,9 @@ import { Module } from '@nestjs/common';
import { TimelineMessagingResolver } from 'src/core/messaging/timeline-messaging.resolver'; import { TimelineMessagingResolver } from 'src/core/messaging/timeline-messaging.resolver';
import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service'; import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service';
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module'; import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
import { UserModule } from 'src/core/user/user.module';
@Module({ @Module({
imports: [WorkspaceDataSourceModule], imports: [WorkspaceDataSourceModule, UserModule],
exports: [], exports: [],
providers: [TimelineMessagingResolver, TimelineMessagingService], providers: [TimelineMessagingResolver, TimelineMessagingService],
}) })

View File

@ -17,6 +17,9 @@ import { AuthWorkspace } from 'src/decorators/auth/auth-workspace.decorator';
import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service'; import { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service';
import { TIMELINE_THREADS_MAX_PAGE_SIZE } from 'src/core/messaging/constants/messaging.constants'; import { TIMELINE_THREADS_MAX_PAGE_SIZE } from 'src/core/messaging/constants/messaging.constants';
import { TimelineThreadsWithTotal } from 'src/core/messaging/dtos/timeline-threads-with-total.dto'; import { TimelineThreadsWithTotal } from 'src/core/messaging/dtos/timeline-threads-with-total.dto';
import { AuthUser } from 'src/decorators/auth/auth-user.decorator';
import { UserService } from 'src/core/user/services/user.service';
import { User } from 'src/core/user/user.entity';
@ArgsType() @ArgsType()
class GetTimelineThreadsFromPersonIdArgs { class GetTimelineThreadsFromPersonIdArgs {
@ -49,15 +52,24 @@ class GetTimelineThreadsFromCompanyIdArgs {
export class TimelineMessagingResolver { export class TimelineMessagingResolver {
constructor( constructor(
private readonly timelineMessagingService: TimelineMessagingService, private readonly timelineMessagingService: TimelineMessagingService,
private readonly userService: UserService,
) {} ) {}
@Query(() => TimelineThreadsWithTotal) @Query(() => TimelineThreadsWithTotal)
async getTimelineThreadsFromPersonId( async getTimelineThreadsFromPersonId(
@AuthWorkspace() { id: workspaceId }: Workspace, @AuthWorkspace() { id: workspaceId }: Workspace,
@AuthUser() user: User,
@Args() { personId, page, pageSize }: GetTimelineThreadsFromPersonIdArgs, @Args() { personId, page, pageSize }: GetTimelineThreadsFromPersonIdArgs,
) { ) {
const workspaceMember = await this.userService.loadWorkspaceMember(user);
if (!workspaceMember) {
return;
}
const timelineThreads = const timelineThreads =
await this.timelineMessagingService.getMessagesFromPersonIds( await this.timelineMessagingService.getMessagesFromPersonIds(
workspaceMember.id,
workspaceId, workspaceId,
[personId], [personId],
page, page,
@ -70,10 +82,18 @@ export class TimelineMessagingResolver {
@Query(() => TimelineThreadsWithTotal) @Query(() => TimelineThreadsWithTotal)
async getTimelineThreadsFromCompanyId( async getTimelineThreadsFromCompanyId(
@AuthWorkspace() { id: workspaceId }: Workspace, @AuthWorkspace() { id: workspaceId }: Workspace,
@AuthUser() user: User,
@Args() { companyId, page, pageSize }: GetTimelineThreadsFromCompanyIdArgs, @Args() { companyId, page, pageSize }: GetTimelineThreadsFromCompanyIdArgs,
) { ) {
const workspaceMember = await this.userService.loadWorkspaceMember(user);
if (!workspaceMember) {
return;
}
const timelineThreads = const timelineThreads =
await this.timelineMessagingService.getMessagesFromCompanyId( await this.timelineMessagingService.getMessagesFromCompanyId(
workspaceMember.id,
workspaceId, workspaceId,
companyId, companyId,
page, page,

View File

@ -21,6 +21,7 @@ export class TimelineMessagingService {
) {} ) {}
async getMessagesFromPersonIds( async getMessagesFromPersonIds(
workspaceMemberId: string,
workspaceId: string, workspaceId: string,
personIds: string[], personIds: string[],
page: number = 1, page: number = 1,
@ -178,13 +179,14 @@ export class TimelineMessagingService {
{}, {},
); );
const threadMessagesFromActiveParticipants: const threadMessagesParticipants:
| { | {
id: string; id: string;
messageId: string; messageId: string;
receivedAt: Date; receivedAt: Date;
body: string; body: string;
subject: string; subject: string;
role: string;
personId: string; personId: string;
workspaceMemberId: string; workspaceMemberId: string;
handle: string; handle: string;
@ -203,6 +205,7 @@ export class TimelineMessagingService {
message."receivedAt", message."receivedAt",
message.text, message.text,
message."subject", message."subject",
"messageParticipant"."role",
"messageParticipant"."personId", "messageParticipant"."personId",
"messageParticipant"."workspaceMemberId", "messageParticipant"."workspaceMemberId",
"messageParticipant".handle, "messageParticipant".handle,
@ -216,7 +219,7 @@ export class TimelineMessagingService {
FROM FROM
${dataSourceSchema}."message" message ${dataSourceSchema}."message" message
LEFT JOIN LEFT JOIN
(SELECT * FROM ${dataSourceSchema}."messageParticipant" WHERE "messageParticipant".role = 'from') "messageParticipant" ON "messageParticipant"."messageId" = message.id ${dataSourceSchema}."messageParticipant" "messageParticipant" ON "messageParticipant"."messageId" = message.id
LEFT JOIN LEFT JOIN
${dataSourceSchema}."person" person ON person."id" = "messageParticipant"."personId" ${dataSourceSchema}."person" person ON person."id" = "messageParticipant"."personId"
LEFT JOIN LEFT JOIN
@ -230,6 +233,11 @@ export class TimelineMessagingService {
workspaceId, workspaceId,
); );
const threadMessagesFromActiveParticipants =
threadMessagesParticipants?.filter(
(threadMessage) => threadMessage.role === 'from',
);
const totalNumberOfThreads = const totalNumberOfThreads =
await this.workspaceDataSourceService.executeRawQuery( await this.workspaceDataSourceService.executeRawQuery(
` `
@ -245,14 +253,14 @@ export class TimelineMessagingService {
workspaceId, workspaceId,
); );
const threadParticipantsByThreadId: { const threadActiveParticipantsByThreadId: {
[key: string]: TimelineThreadParticipant[]; [key: string]: TimelineThreadParticipant[];
} = messageThreadIds.reduce((messageThreadIdAcc, messageThreadId) => { } = messageThreadIds.reduce((messageThreadIdAcc, messageThreadId) => {
const threadMessages = threadMessagesFromActiveParticipants?.filter( const threadMessages = threadMessagesFromActiveParticipants?.filter(
(threadMessage) => threadMessage.id === messageThreadId, (threadMessage) => threadMessage.id === messageThreadId,
); );
const threadParticipants = threadMessages?.reduce( const threadActiveParticipants = threadMessages?.reduce(
( (
threadMessageAcc, threadMessageAcc,
threadMessage, threadMessage,
@ -296,13 +304,36 @@ export class TimelineMessagingService {
{}, {},
); );
messageThreadIdAcc[messageThreadId] = threadParticipants messageThreadIdAcc[messageThreadId] = threadActiveParticipants
? Object.values(threadParticipants) ? Object.values(threadActiveParticipants)
: []; : [];
return messageThreadIdAcc; return messageThreadIdAcc;
}, {}); }, {});
const messageThreadIdsForWhichWorkspaceMemberIsNotInParticipants =
messageThreadIds.reduce(
(
messageThreadIdsForWhichWorkspaceMemberIsInNotParticipantsAcc: string[],
messageThreadId,
) => {
const threadMessagesWithWorkspaceMemberInParticipants =
threadMessagesParticipants?.filter(
(threadMessage) =>
threadMessage.id === messageThreadId &&
threadMessage.workspaceMemberId === workspaceMemberId,
) ?? [];
if (threadMessagesWithWorkspaceMemberInParticipants.length === 0)
messageThreadIdsForWhichWorkspaceMemberIsInNotParticipantsAcc.push(
messageThreadId,
);
return messageThreadIdsForWhichWorkspaceMemberIsInNotParticipantsAcc;
},
[],
);
const threadVisibility: const threadVisibility:
| { | {
id: string; id: string;
@ -322,13 +353,13 @@ export class TimelineMessagingService {
WHERE WHERE
message."messageThreadId" = ANY($1) message."messageThreadId" = ANY($1)
`, `,
[messageThreadIds], [messageThreadIdsForWhichWorkspaceMemberIsNotInParticipants],
workspaceId, workspaceId,
); );
const visibilityValues = ['metadata', 'subject', 'share_everything']; const visibilityValues = ['metadata', 'subject', 'share_everything'];
const threadVisibilityByThreadId: const threadVisibilityByThreadIdForWhichWorkspaceMemberIsNotInParticipants:
| { | {
[key: string]: 'metadata' | 'subject' | 'share_everything'; [key: string]: 'metadata' | 'subject' | 'share_everything';
} }
@ -349,13 +380,30 @@ export class TimelineMessagingService {
{}, {},
); );
const threadVisibilityByThreadId: {
[key: string]: 'metadata' | 'subject' | 'share_everything';
} = messageThreadIds.reduce((threadVisibilityAcc, messageThreadId) => {
// If the workspace member is not in the participants of the thread, use the visibility value from the query
threadVisibilityAcc[messageThreadId] =
messageThreadIdsForWhichWorkspaceMemberIsNotInParticipants.includes(
messageThreadId,
)
? threadVisibilityByThreadIdForWhichWorkspaceMemberIsNotInParticipants?.[
messageThreadId
] ?? 'metadata'
: 'share_everything';
return threadVisibilityAcc;
}, {});
const timelineThreads = messageThreadIds.map((messageThreadId) => { const timelineThreads = messageThreadIds.map((messageThreadId) => {
const threadParticipants = threadParticipantsByThreadId[messageThreadId]; const threadActiveParticipants =
threadActiveParticipantsByThreadId[messageThreadId];
const firstParticipant = threadParticipants[0]; const firstParticipant = threadActiveParticipants[0];
const threadParticipantsWithoutFirstParticipant = const threadActiveParticipantsWithoutFirstParticipant =
threadParticipants.filter( threadActiveParticipants.filter(
(threadParticipant) => (threadParticipant) =>
threadParticipant.handle !== firstParticipant.handle, threadParticipant.handle !== firstParticipant.handle,
); );
@ -363,20 +411,22 @@ export class TimelineMessagingService {
const lastTwoParticipants: TimelineThreadParticipant[] = []; const lastTwoParticipants: TimelineThreadParticipant[] = [];
const lastParticipant = const lastParticipant =
threadParticipantsWithoutFirstParticipant.slice(-1)[0]; threadActiveParticipantsWithoutFirstParticipant.slice(-1)[0];
if (lastParticipant) { if (lastParticipant) {
lastTwoParticipants.push(lastParticipant); lastTwoParticipants.push(lastParticipant);
const threadParticipantsWithoutFirstAndLastParticipants = const threadActiveParticipantsWithoutFirstAndLastParticipants =
threadParticipantsWithoutFirstParticipant.filter( threadActiveParticipantsWithoutFirstParticipant.filter(
(threadParticipant) => (threadParticipant) =>
threadParticipant.handle !== lastParticipant.handle, threadParticipant.handle !== lastParticipant.handle,
); );
if (threadParticipantsWithoutFirstAndLastParticipants.length > 0) if (threadActiveParticipantsWithoutFirstAndLastParticipants.length > 0)
lastTwoParticipants.push( lastTwoParticipants.push(
threadParticipantsWithoutFirstAndLastParticipants.slice(-1)[0], threadActiveParticipantsWithoutFirstAndLastParticipants.slice(
-1,
)[0],
); );
} }
@ -399,7 +449,7 @@ export class TimelineMessagingService {
visibility: threadVisibilityByThreadId?.[messageThreadId] ?? 'metadata', visibility: threadVisibilityByThreadId?.[messageThreadId] ?? 'metadata',
subject: threadSubject, subject: threadSubject,
numberOfMessagesInThread: numberOfMessages, numberOfMessagesInThread: numberOfMessages,
participantCount: threadParticipants.length, participantCount: threadActiveParticipants.length,
}; };
}); });
@ -410,6 +460,7 @@ export class TimelineMessagingService {
} }
async getMessagesFromCompanyId( async getMessagesFromCompanyId(
workspaceMemberId: string,
workspaceId: string, workspaceId: string,
companyId: string, companyId: string,
page: number = 1, page: number = 1,
@ -443,6 +494,7 @@ export class TimelineMessagingService {
); );
const messageThreads = await this.getMessagesFromPersonIds( const messageThreads = await this.getMessagesFromPersonIds(
workspaceMemberId,
workspaceId, workspaceId,
formattedPersonIds, formattedPersonIds,
page, page,