Skip to content

Commit 96d4c0e

Browse files
committed
feat: enable option to wrap CD in CATransaction
1 parent 4114bd3 commit 96d4c0e

2 files changed

Lines changed: 97 additions & 34 deletions

File tree

packages/angular/src/lib/nativescript-renderer.ts

Lines changed: 86 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,46 @@
1-
import { Inject, Injectable, Injector, NgZone, Optional, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2, ViewEncapsulation, inject, runInInjectionContext } from '@angular/core';
2-
import { addTaggedAdditionalCSS, Application, ContentView, Device, getViewById, Observable, profile, Utils, View } from '@nativescript/core';
3-
import { getViewClass, isKnownView } from './element-registry';
4-
import { getFirstNativeLikeView, NgView, TextNode } from './views';
5-
6-
import { NamespaceFilter, NAMESPACE_FILTERS } from './property-filter';
7-
import { APP_ROOT_VIEW, ENABLE_REUSABE_VIEWS, NATIVESCRIPT_ROOT_MODULE_ID, PREVENT_SPECIFIC_EVENTS_DURING_CD } from './tokens';
1+
import {
2+
inject,
3+
Injectable,
4+
Injector,
5+
Renderer2,
6+
RendererFactory2,
7+
RendererStyleFlags2,
8+
RendererType2,
9+
runInInjectionContext,
10+
ViewEncapsulation,
11+
} from '@angular/core';
12+
import {
13+
addTaggedAdditionalCSS,
14+
Application,
15+
ContentView,
16+
getViewById,
17+
Observable,
18+
profile,
19+
View,
20+
} from '@nativescript/core';
21+
import { isKnownView } from './element-registry';
22+
import { NAMESPACE_FILTERS } from './property-filter';
23+
import {
24+
APP_ROOT_VIEW,
25+
ENABLE_REUSABE_VIEWS,
26+
NATIVESCRIPT_ROOT_MODULE_ID,
27+
PREVENT_SPECIFIC_EVENTS_DURING_CD,
28+
WRAP_CD_IN_TRANSACTION,
29+
} from './tokens';
830
import { NativeScriptDebug } from './trace';
931
import { ViewUtil } from './view-util';
32+
import { getFirstNativeLikeView, NgView, TextNode } from './views';
1033

11-
const addStyleToCss = profile('"renderer".addStyleToCss', function addStyleToCss(style: string, tag?: string | number): void {
12-
if (tag) {
13-
addTaggedAdditionalCSS(style, tag);
14-
} else {
15-
Application.addCss(style);
16-
}
17-
});
34+
const addStyleToCss = profile(
35+
'"renderer".addStyleToCss',
36+
function addStyleToCss(style: string, tag?: string | number): void {
37+
if (tag) {
38+
addTaggedAdditionalCSS(style, tag);
39+
} else {
40+
Application.addCss(style);
41+
}
42+
},
43+
);
1844

1945
function runInRootZone<T>(fn: () => T): T {
2046
if (typeof Zone === 'undefined') {
@@ -88,7 +114,9 @@ export class NativeScriptRendererFactory implements RendererFactory2 {
88114
private reuseViews = inject(ENABLE_REUSABE_VIEWS, {
89115
optional: true,
90116
});
117+
private wrapCdInTransaction = __APPLE__ && inject(WRAP_CD_IN_TRANSACTION);
91118
private injector = inject(Injector);
119+
private cdDepth = 0;
92120
private viewUtil = new ViewUtil(this.namespaceFilters, this.reuseViews);
93121

94122
constructor() {
@@ -99,7 +127,9 @@ export class NativeScriptRendererFactory implements RendererFactory2 {
99127
}
100128
createRenderer(hostElement: any, type: RendererType2): Renderer2 {
101129
if (NativeScriptDebug.enabled) {
102-
NativeScriptDebug.rendererLog(`NativeScriptRendererFactory.createRenderer ${hostElement}. type.id: ${type.id} type.encapsulation: ${type.encapsulation}`);
130+
NativeScriptDebug.rendererLog(
131+
`NativeScriptRendererFactory.createRenderer ${hostElement}. type.id: ${type.id} type.encapsulation: ${type.encapsulation}`,
132+
);
103133
}
104134
if (!hostElement || !type) {
105135
return this.defaultRenderer;
@@ -137,12 +167,25 @@ export class NativeScriptRendererFactory implements RendererFactory2 {
137167
this.componentRenderers.set(type.id, renderer);
138168
return renderer;
139169
}
140-
// begin?(): void {
141-
// throw new Error("Method not implemented.");
142-
// }
143-
// end?(): void {
144-
// throw new Error("Method not implemented.");
145-
// }
170+
begin() {
171+
if (__APPLE__ && this.wrapCdInTransaction) {
172+
if (this.cdDepth > 0) {
173+
// previous tick threw between begin and end; flush it
174+
while (this.cdDepth > 0) {
175+
CATransaction.commit();
176+
this.cdDepth--;
177+
}
178+
}
179+
CATransaction.begin();
180+
this.cdDepth++;
181+
}
182+
}
183+
end() {
184+
if (__APPLE__ && this.wrapCdInTransaction) {
185+
CATransaction.commit();
186+
this.cdDepth--;
187+
}
188+
}
146189
whenRenderingDone(): Promise<any> {
147190
if (!this.rootView) {
148191
return Promise.resolve();
@@ -258,15 +301,19 @@ class NativeScriptRenderer implements Renderer2 {
258301
@modifiesDom()
259302
insertBefore(parent: any, newChild: any, refChild: any): void {
260303
if (NativeScriptDebug.enabled) {
261-
NativeScriptDebug.rendererLog(`NativeScriptRenderer.insertBefore child: ${newChild} ` + `parent: ${parent} refChild: ${refChild}`);
304+
NativeScriptDebug.rendererLog(
305+
`NativeScriptRenderer.insertBefore child: ${newChild} ` + `parent: ${parent} refChild: ${refChild}`,
306+
);
262307
}
263308
this.viewUtil.insertBefore(parent, newChild, refChild);
264309
}
265310
@inRootZone()
266311
@modifiesDom()
267312
removeChild(parent: any, oldChild: any, isHostElement?: boolean): void {
268313
if (NativeScriptDebug.enabled) {
269-
NativeScriptDebug.rendererLog(`NativeScriptRenderer.removeChild child: ${oldChild} parent: ${parent} oldChild.parentNode: ${oldChild?.parentNode}`);
314+
NativeScriptDebug.rendererLog(
315+
`NativeScriptRenderer.removeChild child: ${oldChild} parent: ${parent} oldChild.parentNode: ${oldChild?.parentNode}`,
316+
);
270317
}
271318
this.viewUtil.removeChild(parent ?? oldChild.parentNode, oldChild);
272319
}
@@ -319,13 +366,17 @@ class NativeScriptRenderer implements Renderer2 {
319366
@modifiesDom()
320367
setAttribute(el: any, name: string, value: string, namespace?: string): void {
321368
if (NativeScriptDebug.enabled) {
322-
NativeScriptDebug.rendererLog(`NativeScriptRenderer.setAttribute ${namespace ? namespace + ':' : ''}${el}.${name} = ${value}`);
369+
NativeScriptDebug.rendererLog(
370+
`NativeScriptRenderer.setAttribute ${namespace ? namespace + ':' : ''}${el}.${name} = ${value}`,
371+
);
323372
}
324373
this.viewUtil.setProperty(el, name, value, namespace);
325374
}
326375
removeAttribute(el: any, name: string, namespace?: string): void {
327376
if (NativeScriptDebug.enabled) {
328-
NativeScriptDebug.rendererLog(`NativeScriptRenderer.removeAttribute ${namespace ? namespace + ':' : ''}${el}.${name}`);
377+
NativeScriptDebug.rendererLog(
378+
`NativeScriptRenderer.removeAttribute ${namespace ? namespace + ':' : ''}${el}.${name}`,
379+
);
329380
}
330381
}
331382
@inRootZone()
@@ -418,13 +469,16 @@ const replaceNgAttribute = function (input: string, componentId: string): string
418469
return input.replace(COMPONENT_REGEX, componentId);
419470
};
420471

421-
const addScopedStyleToCss = profile(`"renderer".addScopedStyleToCss`, function addScopedStyleToCss(style: string, tag?: number | string): void {
422-
if (tag) {
423-
addTaggedAdditionalCSS(style, tag);
424-
} else {
425-
Application.addCss(style);
426-
}
427-
});
472+
const addScopedStyleToCss = profile(
473+
`"renderer".addScopedStyleToCss`,
474+
function addScopedStyleToCss(style: string, tag?: number | string): void {
475+
if (tag) {
476+
addTaggedAdditionalCSS(style, tag);
477+
} else {
478+
Application.addCss(style);
479+
}
480+
},
481+
);
428482

429483
@Injectable()
430484
export class EmulatedRenderer extends NativeScriptRenderer {

packages/angular/src/lib/tokens.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ export const APP_ROOT_VIEW = new InjectionToken<View>('NativeScriptAppRootView')
55
export const NATIVESCRIPT_ROOT_MODULE_ID = new InjectionToken<string | number>('NativeScriptRootModuleId');
66

77
export const START_PATH = new InjectionToken<Promise<string> | string>('NativeScriptStartPath');
8-
export const ENABLE_REUSABE_VIEWS = new InjectionToken<boolean>('NativeScriptEnableReusableViews');
8+
export const ENABLE_REUSABE_VIEWS = new InjectionToken<boolean>('NativeScriptEnableReusableViews', {
9+
providedIn: 'root',
10+
factory: () => false,
11+
});
12+
export const WRAP_CD_IN_TRANSACTION = new InjectionToken<boolean>('NativeScriptWrapChangeDetectionInTransaction', {
13+
providedIn: 'root',
14+
factory: () => false,
15+
});
916

1017
export type PageFactory = (options: PageFactoryOptions) => Page;
1118
export interface PageFactoryOptions {
@@ -24,4 +31,6 @@ export const defaultPageFactory: PageFactory = function (_opts: PageFactoryOptio
2431
};
2532

2633
export const PREVENT_CHANGE_EVENTS_DURING_CD = new InjectionToken<boolean>('NativeScriptPreventChangeEventsDuringCd');
27-
export const PREVENT_SPECIFIC_EVENTS_DURING_CD = new InjectionToken<string[]>('NativeScriptPreventSpecificEventsDuringCd');
34+
export const PREVENT_SPECIFIC_EVENTS_DURING_CD = new InjectionToken<string[]>(
35+
'NativeScriptPreventSpecificEventsDuringCd',
36+
);

0 commit comments

Comments
 (0)