Skip to content

Commit 5eb0d09

Browse files
authored
fix(ui): skip ribbon republish when inputs are unchanged (#7144)
1 parent 9873065 commit 5eb0d09

2 files changed

Lines changed: 80 additions & 5 deletions

File tree

packages/ui/src/services/ribbon/__tests__/ribbon.service.spec.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ import { IMenuManagerService, MenuManagerService } from '../../menu/menu-manager
3333
import { MenuManagerPosition, RibbonPosition } from '../../menu/types';
3434
import { DesktopRibbonService, IRibbonService } from '../ribbon.service';
3535

36-
function createService() {
36+
function createService(univerInstanceService?: { focused$: BehaviorSubject<string | null> }) {
3737
const injector = new Injector();
3838
injector.add([IConfigService, { useClass: ConfigService }]);
3939
injector.add([IContextService, { useClass: ContextService }]);
4040
injector.add([ILogService, { useClass: DesktopLogService }]);
41-
injector.add([IUniverInstanceService, { useClass: UniverInstanceService }]);
41+
injector.add(univerInstanceService
42+
? [IUniverInstanceService, univerInstanceService as never]
43+
: [IUniverInstanceService, { useClass: UniverInstanceService }]);
4244
injector.add([IMenuManagerService, { useClass: MenuManagerService }]);
4345
injector.add([IRibbonService, { useClass: DesktopRibbonService }]);
4446
return {
@@ -149,4 +151,70 @@ describe('DesktopRibbonService', () => {
149151
ribbonSub.unsubscribe();
150152
activeSub.unsubscribe();
151153
});
154+
155+
it('does not republish the ribbon when hidden states emit unchanged values', () => {
156+
const { service, menuManagerService } = createService();
157+
const hidden$ = new BehaviorSubject(false);
158+
const ribbons: IMenuSchema[][] = [];
159+
const ribbonSub = service.ribbon$.subscribe((ribbon) => ribbons.push(ribbon));
160+
161+
menuManagerService.appendRootMenu({
162+
[MenuManagerPosition.RIBBON]: {
163+
[RibbonPosition.START]: {
164+
order: 0,
165+
[`${RibbonPosition.START}.stable-group`]: {
166+
order: 0,
167+
stableCommand: {
168+
order: 0,
169+
menuItemFactory: () => ({ id: 'stable-command', type: MenuItemType.BUTTON, hidden$ }),
170+
},
171+
},
172+
},
173+
},
174+
} as MenuSchemaType);
175+
176+
const publishCountAfterInitialHiddenState = ribbons.length;
177+
hidden$.next(false);
178+
179+
expect(ribbons.length).toBe(publishCountAfterInitialHiddenState);
180+
181+
hidden$.next(true);
182+
183+
expect(ribbons.length).toBe(publishCountAfterInitialHiddenState + 1);
184+
185+
ribbonSub.unsubscribe();
186+
});
187+
188+
it('does not republish the ribbon when focus emits the same unit id', () => {
189+
const focused$ = new BehaviorSubject<string | null>('unit-1');
190+
const { service, menuManagerService } = createService({ focused$ });
191+
const ribbons: IMenuSchema[][] = [];
192+
const ribbonSub = service.ribbon$.subscribe((ribbon) => ribbons.push(ribbon));
193+
194+
menuManagerService.appendRootMenu({
195+
[MenuManagerPosition.RIBBON]: {
196+
[RibbonPosition.START]: {
197+
order: 0,
198+
[`${RibbonPosition.START}.focus-group`]: {
199+
order: 0,
200+
focusCommand: {
201+
order: 0,
202+
menuItemFactory: () => ({ id: 'focus-command', type: MenuItemType.BUTTON }),
203+
},
204+
},
205+
},
206+
},
207+
} as MenuSchemaType);
208+
209+
const publishCountAfterMenuLoad = ribbons.length;
210+
focused$.next('unit-1');
211+
212+
expect(ribbons.length).toBe(publishCountAfterMenuLoad);
213+
214+
focused$.next('unit-2');
215+
216+
expect(ribbons.length).toBe(publishCountAfterMenuLoad + 1);
217+
218+
ribbonSub.unsubscribe();
219+
});
152220
});

packages/ui/src/services/ribbon/ribbon.service.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ import type { Observable, Subscription } from 'rxjs';
1818
import type { IMenuSchema } from '../menu/menu-manager.service';
1919
import { createIdentifier, Disposable, IUniverInstanceService } from '@univerjs/core';
2020
import { BehaviorSubject, combineLatest } from 'rxjs';
21-
import { startWith } from 'rxjs/operators';
21+
import { distinctUntilChanged, startWith } from 'rxjs/operators';
2222
import { IMenuManagerService } from '../menu/menu-manager.service';
2323
import { MenuManagerPosition, RibbonPosition } from '../menu/types';
2424

2525
export const IRibbonService = createIdentifier<IRibbonService>('univer.ribbon-service');
2626

27+
function isSameHiddenMap(previous: boolean[], current: boolean[]): boolean {
28+
return previous.length === current.length && previous.every((hidden, index) => hidden === current[index]);
29+
}
30+
2731
export interface IRibbonService {
2832
ribbon$: Observable<IMenuSchema[]>;
2933
activatedTab$: Observable<string>;
@@ -110,7 +114,7 @@ export class DesktopRibbonService extends Disposable implements IRibbonService {
110114
this.disposeWithMe(
111115
combineLatest([
112116
this._menuManagerService.menuChanged$.pipe(startWith(undefined)),
113-
this._univerInstanceService.focused$.pipe(startWith(undefined)),
117+
this._univerInstanceService.focused$.pipe(startWith(undefined), distinctUntilChanged()),
114118
]).subscribe(() => {
115119
this._updateRibbon();
116120
})
@@ -148,7 +152,10 @@ export class DesktopRibbonService extends Disposable implements IRibbonService {
148152
this._hiddenSubscription?.unsubscribe();
149153

150154
this._hiddenSubscription = combineLatest(hiddenObservableMap)
151-
.pipe(startWith(new Array(hiddenObservableMap.length).fill(false)))
155+
.pipe(
156+
startWith(new Array(hiddenObservableMap.length).fill(false)),
157+
distinctUntilChanged(isSameHiddenMap)
158+
)
152159
.subscribe((hiddenMap) => {
153160
const newRibbon: IMenuSchema[] = [];
154161

0 commit comments

Comments
 (0)