Skip to content
This repository was archived by the owner on Feb 24, 2026. It is now read-only.

Commit c1c8a0d

Browse files
committed
feat: refresh task selector based on real-time task updates
A new socket signal is emitted whenever a task is updated, allowing the UI to detect changes immediately. This enables the task selector to refresh its list as soon as a task is modified, ensuring that employees see updated assignments without having to reload the page. This behavior is especially important when an employee is added to or removed from a task, as the selector now reflects these changes instantly. The logic to exclude tasks with a "done" status remains unchanged.
1 parent 9c54625 commit c1c8a0d

4 files changed

Lines changed: 62 additions & 13 deletions

File tree

packages/core/src/lib/tasks/task.service.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -173,17 +173,7 @@ export class TaskService extends TenantAwareCrudService<Task> {
173173
if (updatedTask.status === TaskStatusEnum.DONE && task.status !== TaskStatusEnum.DONE) {
174174
try {
175175
this.logger.debug(`Task ${updatedTask.id} changed status to DONE`);
176-
const taskMembers = updatedTask.members?.length ? updatedTask.members : task.members;
177-
178-
if (taskMembers?.length) {
179-
await Promise.all(
180-
taskMembers.map(async (member) => {
181-
// Send a real-time event to the specified user via socket.
182-
// No error is thrown if the user is not currently connected.
183-
this._socketService.sendTimerChanged(member?.id);
184-
})
185-
);
186-
}
176+
this._socketService.sendTimerChangedMany([...existingMemberIdSet])
187177
} catch (error) {
188178
this.logger.error(`Error emitting DONE status event for task ${updatedTask.id}: ${error}`);
189179
}
@@ -250,6 +240,18 @@ export class TaskService extends TenantAwareCrudService<Task> {
250240
}
251241
}
252242

243+
try {
244+
[
245+
...existingMemberIdSet,
246+
...newMembers.map((member) => member.id)
247+
].forEach((memberId) => {
248+
this._socketService.emitToClient(memberId, 'tasks:changed', null);
249+
});
250+
251+
} catch (error) {
252+
this.logger.error(`Error while sending tasks changed event: ${error}`);
253+
}
254+
253255
// Generate the activity log
254256
this._activityLogService.logActivity<Task>(
255257
BaseEntityEnum.Task,

packages/ui-core/core/src/lib/services/tasks/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export * from './sizes.service';
44
export * from './statuses.service';
55
export * from './tasks-store.service';
66
export * from './tasks.service';
7-
export * from './team-tasks-store.service';
7+
export * from './team-tasks-store.service';
8+
export * from './task-socket.service';
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Injectable } from "@angular/core";
2+
import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
3+
import { SocketConnectionService } from "../socket-connection";
4+
import { BehaviorSubject, filter, fromEvent, switchMap, tap } from "rxjs";
5+
6+
@UntilDestroy()
7+
@Injectable({
8+
providedIn: 'root'
9+
})
10+
export class TaskSocketService {
11+
public tasksChanged$ = new BehaviorSubject<boolean>(false);
12+
13+
constructor(private readonly socketConnection: SocketConnectionService) {
14+
this.listenToTasksChanges();
15+
}
16+
17+
/**
18+
* Listen to tasks changes from the socket connection
19+
*/
20+
private listenToTasksChanges() {
21+
console.log(this.socketConnection?.socket);
22+
this.socketConnection.connected$
23+
.pipe(
24+
filter((connected) => connected === true),
25+
switchMap(() =>
26+
fromEvent(this.socketConnection.socket, 'tasks:changed').pipe(
27+
tap(() => {
28+
console.log('[Socket] tasks:changed event');
29+
this.tasksChanged$.next(true);
30+
})
31+
)
32+
),
33+
untilDestroyed(this)
34+
)
35+
.subscribe();
36+
}
37+
}

packages/ui-core/shared/src/lib/tasks/task-select/task/task.component.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
66
import { IOrganization, ITask, PermissionsEnum, TaskStatusEnum } from '@gauzy/contracts';
77
import { distinctUntilChange } from '@gauzy/ui-core/common';
88
import { AuthService, Store, TasksService, ToastrService } from '@gauzy/ui-core/core';
9+
import { TaskSocketService } from '@gauzy/ui-core/core';
910

1011
@UntilDestroy({ checkProperties: true })
1112
@Component({
@@ -136,7 +137,8 @@ export class TaskSelectorComponent implements OnInit, ControlValueAccessor {
136137
private readonly tasksService: TasksService,
137138
private readonly toastrService: ToastrService,
138139
private readonly store: Store,
139-
private readonly authService: AuthService
140+
private readonly authService: AuthService,
141+
private readonly taskSocketService: TaskSocketService
140142
) {}
141143

142144
onChange: any = () => {};
@@ -154,6 +156,13 @@ export class TaskSelectorComponent implements OnInit, ControlValueAccessor {
154156
untilDestroyed(this)
155157
)
156158
.subscribe();
159+
this.taskSocketService.tasksChanged$
160+
.pipe(
161+
filter((changed) => changed === true && !!this.organization),
162+
tap(() => this.subject$.next(true)),
163+
untilDestroyed(this)
164+
)
165+
.subscribe();
157166
this.store.selectedOrganization$
158167
.pipe(
159168
distinctUntilChange(),

0 commit comments

Comments
 (0)