Skip to content

Commit 08c7947

Browse files
Use twentyORM in Timeline messaging (#6595)
- Remove raw queries and replace them by using `twentyORM` - Refactor into services and utils --------- Co-authored-by: Charles Bochet <[email protected]>
1 parent 6927f46 commit 08c7947

File tree

19 files changed

+513
-559
lines changed

19 files changed

+513
-559
lines changed

packages/twenty-front/src/hooks/useIsMatchingLocation.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const useIsMatchingLocation = () => {
99
return useCallback(
1010
(path: string, basePath?: AppBasePath) => {
1111
const constructedPath = basePath
12-
? (new URL(basePath + path, document.location.origin).pathname ?? '')
12+
? new URL(basePath + path, document.location.origin).pathname ?? ''
1313
: path;
1414

1515
return !!matchPath(constructedPath, location.pathname);

packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersChip.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const MessageThreadSubscribersChip = ({
4545
? `+${numberOfMessageThreadSubscribers - MAX_NUMBER_OF_AVATARS}`
4646
: null;
4747

48-
const label = isPrivateThread ? privateLabel : (moreAvatarsLabel ?? '');
48+
const label = isPrivateThread ? privateLabel : moreAvatarsLabel ?? '';
4949

5050
return (
5151
<Chip

packages/twenty-front/src/modules/object-metadata/utils/generateDefaultRecordChipData.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord';
44
export const generateDefaultRecordChipData = (record: ObjectRecord) => {
55
const name = isFieldFullNameValue(record.name)
66
? record.name.firstName + ' ' + record.name.lastName
7-
: (record.name ?? '');
7+
: record.name ?? '';
88

99
return {
1010
name,

packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/MultiSelectFieldInput.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ export const MultiSelectFieldInput = ({
4242
const [searchFilter, setSearchFilter] = useState('');
4343
const containerRef = useRef<HTMLDivElement>(null);
4444

45-
const selectedOptions = fieldDefinition.metadata.options.filter((option) =>
46-
fieldValues?.includes(option.value),
45+
const selectedOptions = fieldDefinition.metadata.options.filter(
46+
(option) => fieldValues?.includes(option.value),
4747
);
4848

4949
const optionsInDropDown = fieldDefinition.metadata.options;

packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export const RecordDetailRelationSection = ({
6969
const relationRecords: ObjectRecord[] =
7070
fieldValue && isToOneObject
7171
? [fieldValue as ObjectRecord]
72-
: ((fieldValue as ObjectRecord[]) ?? []);
72+
: (fieldValue as ObjectRecord[]) ?? [];
7373

7474
const relationRecordIds = relationRecords.map(({ id }) => id);
7575

packages/twenty-front/src/modules/spreadsheet-import/utils/findUnmatchedRequiredFields.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ export const findUnmatchedRequiredFields = <T extends string>(
66
columns: Columns<T>,
77
) =>
88
fields
9-
.filter((field) =>
10-
field.fieldValidationDefinitions?.some(
11-
(validation) => validation.rule === 'required',
12-
),
9+
.filter(
10+
(field) =>
11+
field.fieldValidationDefinitions?.some(
12+
(validation) => validation.rule === 'required',
13+
),
1314
)
1415
.filter(
1516
(field) =>

packages/twenty-front/src/modules/ui/input/components/Toggle.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type ContainerProps = {
1616
const StyledContainer = styled.div<ContainerProps>`
1717
align-items: center;
1818
background-color: ${({ theme, isOn, color }) =>
19-
isOn ? (color ?? theme.color.blue) : theme.background.quaternary};
19+
isOn ? color ?? theme.color.blue : theme.background.quaternary};
2020
border-radius: 10px;
2121
cursor: pointer;
2222
display: flex;

packages/twenty-server/src/engine/core-modules/messaging/dtos/timeline-thread-participant.dto.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { ObjectType, Field } from '@nestjs/graphql';
1+
import { Field, ObjectType } from '@nestjs/graphql';
22

33
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
44

55
@ObjectType('TimelineThreadParticipant')
66
export class TimelineThreadParticipant {
77
@Field(() => UUIDScalarType, { nullable: true })
8-
personId: string;
8+
personId: string | null;
99

1010
@Field(() => UUIDScalarType, { nullable: true })
11-
workspaceMemberId: string;
11+
workspaceMemberId: string | null;
1212

1313
@Field()
1414
firstName: string;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Injectable } from '@nestjs/common';
2+
3+
import { TIMELINE_THREADS_DEFAULT_PAGE_SIZE } from 'src/engine/core-modules/messaging/constants/messaging.constants';
4+
import { TimelineThreadsWithTotal } from 'src/engine/core-modules/messaging/dtos/timeline-threads-with-total.dto';
5+
import { TimelineMessagingService } from 'src/engine/core-modules/messaging/services/timeline-messaging.service';
6+
import { formatThreads } from 'src/engine/core-modules/messaging/utils/format-threads.util';
7+
import { TwentyORMManager } from 'src/engine/twenty-orm/twenty-orm.manager';
8+
import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/person.workspace-entity';
9+
10+
@Injectable()
11+
export class GetMessagesService {
12+
constructor(
13+
private readonly twentyORMManager: TwentyORMManager,
14+
private readonly timelineMessagingService: TimelineMessagingService,
15+
) {}
16+
17+
async getMessagesFromPersonIds(
18+
workspaceMemberId: string,
19+
personIds: string[],
20+
page = 1,
21+
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
22+
): Promise<TimelineThreadsWithTotal> {
23+
const offset = (page - 1) * pageSize;
24+
25+
const { messageThreads, totalNumberOfThreads } =
26+
await this.timelineMessagingService.getAndCountMessageThreads(
27+
personIds,
28+
offset,
29+
pageSize,
30+
);
31+
32+
if (!messageThreads) {
33+
return {
34+
totalNumberOfThreads: 0,
35+
timelineThreads: [],
36+
};
37+
}
38+
39+
const messageThreadIds = messageThreads.map(
40+
(messageThread) => messageThread.id,
41+
);
42+
43+
const threadParticipantsByThreadId =
44+
await this.timelineMessagingService.getThreadParticipantsByThreadId(
45+
messageThreadIds,
46+
);
47+
48+
const threadVisibilityByThreadId =
49+
await this.timelineMessagingService.getThreadVisibilityByThreadId(
50+
messageThreadIds,
51+
workspaceMemberId,
52+
);
53+
54+
return {
55+
totalNumberOfThreads,
56+
timelineThreads: formatThreads(
57+
messageThreads,
58+
threadParticipantsByThreadId,
59+
threadVisibilityByThreadId,
60+
),
61+
};
62+
}
63+
64+
async getMessagesFromCompanyId(
65+
workspaceMemberId: string,
66+
companyId: string,
67+
page = 1,
68+
pageSize: number = TIMELINE_THREADS_DEFAULT_PAGE_SIZE,
69+
): Promise<TimelineThreadsWithTotal> {
70+
const personRepository =
71+
await this.twentyORMManager.getRepository<PersonWorkspaceEntity>(
72+
'person',
73+
);
74+
const personIds = (
75+
await personRepository.find({
76+
where: {
77+
companyId,
78+
},
79+
select: {
80+
id: true,
81+
},
82+
})
83+
).map((person) => person.id);
84+
85+
if (personIds.length === 0) {
86+
return {
87+
totalNumberOfThreads: 0,
88+
timelineThreads: [],
89+
};
90+
}
91+
92+
const messageThreads = await this.getMessagesFromPersonIds(
93+
workspaceMemberId,
94+
personIds,
95+
page,
96+
pageSize,
97+
);
98+
99+
return messageThreads;
100+
}
101+
}

0 commit comments

Comments
 (0)