Fix view picker small bugs (#16987)

This PR solves small bugs around the view picker.

- Couldn’t obtain optimistic update after a re-order of a view by drag
and drop, we needed to refresh the page
- Picking a new icon wouldn’t trigger optimistic update (same problem)
- Picking a new icon would change the view (difficult to understand
behavior)
- Picking a new icon would trigger left drawer collapse (z-index
problem)

Since core views are not being handled by object metadata items anymore,
and that all view logic is plugged on coreViewsState, this PR
implemented optimistic effect by upserting into this state.

Fixes https://github.com/twentyhq/twenty/issues/15422
Fixes https://github.com/twentyhq/twenty/issues/16986

# Before


https://github.com/user-attachments/assets/64099c21-df9f-4772-ab0d-9ea449aed761

# After


https://github.com/user-attachments/assets/f4e844b3-6530-4178-abdb-b7a10d2327b8

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2026-01-08 12:05:45 +01:00
committed by GitHub
parent 71fd05315c
commit e9b7ad21d2
28 changed files with 404 additions and 187 deletions
@@ -1,4 +1,4 @@
import { upsertIntoArrayOfObjectsComparingId } from '@/utils/array/upsertIntoArrayOfObjectComparingId';
import { upsertIntoArrayOfObjectsComparingId } from '@/utils/array/upsertIntoArrayOfObjectsComparingId';
type TestObject = {
id: string;
@@ -0,0 +1,107 @@
import { upsertPropertiesOfItemIntoArrayOfObjectsComparingId } from '@/utils/array/upsertPropertiesOfItemIntoArrayOfObjectsComparingId';
type TestObject = {
id: string;
name: string;
value: number;
};
const mockTestObjects: TestObject[] = [
{
id: '1',
name: 'Test 1',
value: 100,
},
{
id: '2',
name: 'Test 2',
value: 200,
},
{
id: '3',
name: 'Test 3',
value: 300,
},
];
describe('upsertPropertiesOfItemIntoArrayOfObjectsComparingId', () => {
it('should insert partial properties into empty array', () => {
const partialItem = { id: '1', name: 'New Item' };
expect(
upsertPropertiesOfItemIntoArrayOfObjectsComparingId<TestObject>(
[],
partialItem,
),
).toStrictEqual([partialItem]);
});
it('should append item when id does not exist', () => {
const newItem: TestObject = {
id: '4',
name: 'Test 4',
value: 400,
};
expect(
upsertPropertiesOfItemIntoArrayOfObjectsComparingId(
mockTestObjects,
newItem,
),
).toStrictEqual([...mockTestObjects, newItem]);
});
it('should merge partial properties into existing item', () => {
const partialUpdate = { id: '2', name: 'Updated Test 2' };
expect(
upsertPropertiesOfItemIntoArrayOfObjectsComparingId(
mockTestObjects,
partialUpdate,
),
).toStrictEqual([
mockTestObjects[0],
{ id: '2', name: 'Updated Test 2', value: 200 },
mockTestObjects[2],
]);
});
it('should replace all properties when full item is provided', () => {
const fullUpdate: TestObject = {
id: '1',
name: 'Replaced Test 1',
value: 999,
};
expect(
upsertPropertiesOfItemIntoArrayOfObjectsComparingId(
mockTestObjects,
fullUpdate,
),
).toStrictEqual([fullUpdate, mockTestObjects[1], mockTestObjects[2]]);
});
it('should not mutate original array when updating', () => {
const originalArray = [...mockTestObjects];
const partialUpdate = { id: '2', value: 999 };
upsertPropertiesOfItemIntoArrayOfObjectsComparingId(
mockTestObjects,
partialUpdate,
);
expect(mockTestObjects).toStrictEqual(originalArray);
});
it('should not mutate original array when inserting', () => {
const originalArray = [...mockTestObjects];
const newItem: TestObject = { id: '5', name: 'Test 5', value: 500 };
upsertPropertiesOfItemIntoArrayOfObjectsComparingId(
mockTestObjects,
newItem,
);
expect(mockTestObjects).toStrictEqual(originalArray);
});
});
@@ -0,0 +1,31 @@
import { findById } from '@/utils/array/findById';
export const upsertPropertiesOfItemIntoArrayOfObjectsComparingId = <
T extends { id: string },
>(
arrayToUpsertInto: T[],
propertiesToUpsert: Partial<T> & { id: string },
): T[] => {
const alreadyExistingItemIndex = arrayToUpsertInto.findIndex(
findById(propertiesToUpsert.id),
);
const shouldReplaceItem = alreadyExistingItemIndex > -1;
if (shouldReplaceItem) {
const newArray = [...arrayToUpsertInto];
const itemToUpsert = {
...arrayToUpsertInto[alreadyExistingItemIndex],
...propertiesToUpsert,
} as T;
newArray.splice(alreadyExistingItemIndex, 1, itemToUpsert);
return newArray;
} else {
return arrayToUpsertInto.concat({
...propertiesToUpsert,
} as T);
}
};