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 { TimelineMessagingService } from 'src/core/messaging/timeline-messaging.service';
import { WorkspaceDataSourceModule } from 'src/workspace/workspace-datasource/workspace-datasource.module';
import { UserModule } from 'src/core/user/user.module';
@Module({
imports: [WorkspaceDataSourceModule],
imports: [WorkspaceDataSourceModule, UserModule],
exports: [],
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 { 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 { 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()
class GetTimelineThreadsFromPersonIdArgs {
@ -49,15 +52,24 @@ class GetTimelineThreadsFromCompanyIdArgs {
export class TimelineMessagingResolver {
constructor(
private readonly timelineMessagingService: TimelineMessagingService,
private readonly userService: UserService,
) {}
@Query(() => TimelineThreadsWithTotal)
async getTimelineThreadsFromPersonId(
@AuthWorkspace() { id: workspaceId }: Workspace,
@AuthUser() user: User,
@Args() { personId, page, pageSize }: GetTimelineThreadsFromPersonIdArgs,
) {
const workspaceMember = await this.userService.loadWorkspaceMember(user);
if (!workspaceMember) {
return;
}
const timelineThreads =
await this.timelineMessagingService.getMessagesFromPersonIds(
workspaceMember.id,
workspaceId,
[personId],
page,
@ -70,10 +82,18 @@ export class TimelineMessagingResolver {
@Query(() => TimelineThreadsWithTotal)
async getTimelineThreadsFromCompanyId(
@AuthWorkspace() { id: workspaceId }: Workspace,
@AuthUser() user: User,
@Args() { companyId, page, pageSize }: GetTimelineThreadsFromCompanyIdArgs,
) {
const workspaceMember = await this.userService.loadWorkspaceMember(user);
if (!workspaceMember) {
return;
}
const timelineThreads =
await this.timelineMessagingService.getMessagesFromCompanyId(
workspaceMember.id,
workspaceId,
companyId,
page,

View File

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