Skip to content

Commit 5669b97

Browse files
feat(overmind-angular): use ngmodule and directive instead
1 parent a76f8c8 commit 5669b97

File tree

8 files changed

+219
-214
lines changed

8 files changed

+219
-214
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// @ts-ignore
2+
import {
3+
InjectionToken,
4+
Directive,
5+
ViewContainerRef,
6+
TemplateRef,
7+
Renderer,
8+
OnInit,
9+
OnDestroy,
10+
Input,
11+
Inject,
12+
// @ts-ignore
13+
} from '@angular/core'
14+
import { OvermindService } from './service'
15+
import { EventType, Overmind } from 'overmind'
16+
import { IS_PROXY } from 'proxy-state-tree'
17+
18+
export const OVERMIND_INSTANCE = new InjectionToken('OVERMIND')
19+
20+
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
21+
22+
let nextComponentId = 0
23+
24+
@Directive({ selector: '[track]' })
25+
export class OvermindTrackDirective implements OnInit, OnDestroy {
26+
protected templateBindings = {}
27+
protected dispose: any
28+
protected view: any
29+
protected tree: any
30+
protected componentDetails: any
31+
protected component: any
32+
protected overmind: any
33+
@Input() track
34+
35+
constructor(
36+
protected templateRef: TemplateRef<any>,
37+
protected viewContainer: ViewContainerRef,
38+
protected renderer: Renderer,
39+
@Inject(OVERMIND_INSTANCE) overmind: Overmind<any>
40+
) {
41+
this.component = (this.viewContainer as any)._view.component
42+
this.overmind = overmind
43+
this.tree = this.overmind.proxyStateTree.getTrackStateTreeWithProxifier()
44+
}
45+
findService(target) {
46+
return Object.keys(target).find(
47+
(key) => target[key] instanceof OvermindService
48+
)
49+
}
50+
rescope() {
51+
let hasRescoped = false
52+
for (let key in this.component) {
53+
if (this.component[key] && this.component[key][IS_PROXY]) {
54+
this.component[key] = this.overmind.proxyStateTree.rescope(
55+
this.component[key],
56+
this.tree
57+
)
58+
hasRescoped = true
59+
}
60+
}
61+
62+
if (!hasRescoped) {
63+
throw new Error(
64+
'OVERMIND - You are tracking in a component that has nothing to track: ' +
65+
this.component.constructor.name
66+
)
67+
}
68+
}
69+
ngOnInit() {
70+
this.rescope()
71+
this.view = this.viewContainer.createEmbeddedView(this.templateRef)
72+
73+
this.tree.track(this.onUpdate)
74+
75+
if (IS_PRODUCTION) {
76+
return
77+
}
78+
79+
const Component = this.component.constructor
80+
81+
const componentId = nextComponentId++
82+
let componentInstanceId = 0
83+
84+
this.componentDetails = {
85+
componentId,
86+
componentInstanceId: componentInstanceId++,
87+
name: Component.name,
88+
}
89+
;(window['__zone_symbol__setTimeout'] || setTimeout)(() => {
90+
this.overmind.eventHub.emitAsync(EventType.COMPONENT_ADD, {
91+
componentId: this.componentDetails.componentId,
92+
componentInstanceId: this.componentDetails.componentInstanceId,
93+
name: this.componentDetails.name,
94+
paths: Array.from(this.tree.pathDependencies) as any,
95+
})
96+
})
97+
}
98+
99+
private onUpdate = (mutations, paths, flushId) => {
100+
this.tree.track(this.onUpdate)
101+
this.view.detectChanges()
102+
if (this.componentDetails) {
103+
;(window['__zone_symbol__setTimeout'] || setTimeout)(() => {
104+
this.overmind.eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
105+
componentId: this.componentDetails.componentId,
106+
componentInstanceId: this.componentDetails.componentInstanceId,
107+
name: this.componentDetails.name,
108+
paths: Array.from(this.tree.pathDependencies) as any,
109+
flushId,
110+
})
111+
})
112+
}
113+
}
114+
115+
ngOnDestroy() {
116+
this.overmind.proxyStateTree.disposeTree(this.tree)
117+
if (this.componentDetails) {
118+
this.overmind.eventHub.emitAsync(EventType.COMPONENT_REMOVE, {
119+
componentId: this.componentDetails.componentId,
120+
componentInstanceId: this.componentDetails.componentInstanceId,
121+
name: this.componentDetails.name,
122+
})
123+
}
124+
}
125+
}
Lines changed: 19 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,20 @@
1-
import { Overmind, EventType } from 'overmind'
2-
import { BehaviorSubject, Observable } from 'rxjs'
3-
import { map } from 'rxjs/operators'
4-
import { IS_PROXY } from 'proxy-state-tree'
5-
6-
class ServiceBase {}
7-
81
// @ts-ignore
9-
const IS_PRODUCTION = process.env.NODE_ENV === 'production'
10-
11-
let nextComponentId = 0
12-
13-
const Track = (Component) => {
14-
const componentId = nextComponentId++
15-
let componentInstanceId = 0
16-
17-
function findService(target) {
18-
return Object.keys(target).find((key) => target[key] instanceof ServiceBase)
19-
}
20-
21-
if (!IS_PRODUCTION) {
22-
const targetNgOnInit = Component.prototype.ngOnInit
23-
Component.prototype.ngOnInit = function() {
24-
if (
25-
!Component['__annotations__'][0] ||
26-
Component['__annotations__'][0].changeDetection !== 0
27-
) {
28-
throw new Error(
29-
'OVERMIND: You have to add ChangeDetectionStrategy.OnPush on component ' +
30-
Component.name
31-
)
32-
}
33-
targetNgOnInit && targetNgOnInit.call(this)
34-
}
35-
const targetNgAfterViewInit = Component.prototype.ngAfterViewInit
36-
Component.prototype.ngAfterViewInit = function() {
37-
const service = this[findService(this)]
38-
39-
if (!service) {
40-
throw new Error(
41-
'OVERMIND - You have not added the Overmind service to component ' +
42-
Component.name
43-
)
44-
}
45-
46-
service.addComponent({
47-
componentId,
48-
componentInstanceId: componentInstanceId++,
49-
name: Component.name,
50-
})
51-
52-
targetNgAfterViewInit && targetNgAfterViewInit.call(this)
53-
}
54-
}
55-
56-
const targetNgOnChanges = Component.prototype.ngOnChanges
57-
Component.prototype.ngOnChanges = function(changes) {
58-
const service = this[findService(this)]
59-
60-
for (let key in changes) {
61-
if (changes[key].currentValue[IS_PROXY]) {
62-
this[key] = service.tree.master.rescope(
63-
changes[key].currentValue,
64-
service.tree
65-
)
66-
}
67-
}
68-
targetNgOnChanges && targetNgOnChanges.call(this, changes)
69-
}
70-
}
71-
72-
interface IService<App extends Overmind<any>> {
73-
new (): {
74-
actions: App['actions']
75-
effects: App['effects']
76-
addMutationListener: App['addMutationListener']
77-
select<T>(expr: (state: App['state']) => T): Observable<T>
78-
select(): Observable<App['state']>
79-
}
80-
Track
81-
}
82-
83-
export function createService<App extends Overmind<any>>(
84-
overmind: App
85-
): IService<App> {
86-
return class Service extends ServiceBase {
87-
static Track = Track
88-
private tree: any
89-
private state$: Observable<any>
90-
private subject: BehaviorSubject<any>
91-
private overmind: App
92-
private componentDetails: any
93-
actions: App['actions']
94-
effects: App['effects']
95-
addMutationListener: App['addMutationListener']
96-
constructor() {
97-
super()
98-
this.tree = (overmind as any).proxyStateTree.getTrackStateTreeWithProxifier()
99-
this.subject = new BehaviorSubject(this.tree.state)
100-
this.state$ = this.subject.asObservable()
101-
this.overmind = overmind
102-
103-
this.actions = this.overmind.actions
104-
this.addMutationListener = this.overmind.addMutationListener
105-
106-
this.tree.track(this.onUpdate)
107-
108-
if (!IS_PRODUCTION) {
109-
;(window['__zone_symbol__setTimeout'] || setTimeout)(() => {
110-
if (!this.componentDetails) {
111-
throw new Error(
112-
'OVERMIND - You have added a service without adding the Track decorator'
113-
)
114-
}
115-
})
116-
}
117-
}
118-
private addComponent(componentDetails) {
119-
if (this.componentDetails) {
120-
throw new Error(
121-
'OVERMIND - This service is already instantiated, you have to provide it as well with providers: [OvermindService] on ' +
122-
this.componentDetails.name
123-
)
124-
}
125-
this.componentDetails = componentDetails
126-
this.overmind.eventHub.emitAsync(EventType.COMPONENT_ADD, {
127-
componentId: componentDetails.componentId,
128-
componentInstanceId: componentDetails.componentInstanceId,
129-
name: componentDetails.name,
130-
paths: Array.from(this.tree.pathDependencies) as any,
131-
})
132-
}
133-
private onUpdate = (mutations, paths, flushId) => {
134-
this.tree.track(this.onUpdate)
135-
this.subject.next(this.tree.state)
136-
if (this.componentDetails) {
137-
;(window['__zone_symbol__setTimeout'] || setTimeout)(() => {
138-
this.overmind.eventHub.emitAsync(EventType.COMPONENT_UPDATE, {
139-
componentId: this.componentDetails.componentId,
140-
componentInstanceId: this.componentDetails.componentInstanceId,
141-
name: this.componentDetails.name,
142-
paths: Array.from(this.tree.pathDependencies) as any,
143-
flushId,
144-
})
145-
})
146-
}
147-
}
148-
private ngOnDestroy() {
149-
;(this.overmind as any).proxyStateTree.disposeTree(this.tree)
150-
if (this.componentDetails) {
151-
this.overmind.eventHub.emitAsync(EventType.COMPONENT_REMOVE, {
152-
componentId: this.componentDetails.componentId,
153-
componentInstanceId: this.componentDetails.componentInstanceId,
154-
name: this.componentDetails.name,
155-
})
156-
}
157-
}
158-
select<T>(expr: (state: App['state']) => T): Observable<T>
159-
select(): Observable<App['state']>
160-
select() {
161-
const args = arguments
162-
return this.state$.pipe(
163-
map((value) => (args[0] ? args[0](value) : value))
164-
)
165-
}
166-
}
167-
}
2+
import { NgModule } from '@angular/core'
3+
import { OvermindTrackDirective, OVERMIND_INSTANCE } from './directive'
4+
import { OvermindService } from './service'
5+
6+
export { OVERMIND_INSTANCE }
7+
8+
@NgModule({
9+
declarations: [OvermindTrackDirective],
10+
exports: [OvermindTrackDirective],
11+
imports: [],
12+
providers: [
13+
{
14+
provide: OvermindService,
15+
useFactory: (overmind) => new OvermindService(overmind),
16+
deps: [OVERMIND_INSTANCE],
17+
},
18+
],
19+
})
20+
export class OvermindModule {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @ts-ignore
2+
import { Injectable } from '@angular/core'
3+
import { IConfiguration, Overmind } from 'overmind'
4+
5+
@Injectable()
6+
export class OvermindService<Config extends IConfiguration> {
7+
private overmind: any
8+
actions: Overmind<Config>['actions']
9+
effects: Overmind<Config>['effects']
10+
addMutationListener: Overmind<Config>['addMutationListener'] // Fix this!
11+
constructor(overmind: Overmind<Config>) {
12+
this.overmind = overmind
13+
this.actions = this.overmind.actions
14+
this.addMutationListener = this.overmind.addMutationListener
15+
}
16+
select<T>(expr: (state: Overmind<Config>['state']) => T): T
17+
select(): Overmind<Config>['state']
18+
select(...args) {
19+
return args[0] ? args[0](this.overmind.state) : this.overmind.state
20+
}
21+
}

0 commit comments

Comments
 (0)