Skip to content

Commit 89dfba1

Browse files
committed
Do not clear dispatcher in Subs, use isActive() instead
Fix RAF (no Cmds) Fix Doc events (timeout)
1 parent 1e38c57 commit 89dfba1

6 files changed

Lines changed: 183 additions & 181 deletions

File tree

core/src/TeaCup/Animation.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
*/
2525

2626
import { Sub } from './Sub';
27-
import { Cmd } from './Cmd';
28-
import { Dispatcher } from './Dispatcher';
2927

3028
class RafSub<M> extends Sub<M> {
3129
readonly mapper: (t: number) => M;
@@ -38,7 +36,7 @@ class RafSub<M> extends Sub<M> {
3836
protected onInit() {
3937
super.onInit();
4038
setTimeout(() => {
41-
requestAnimationFrame((t) => this.trigger(t));
39+
this.isActive() && requestAnimationFrame((t) => this.trigger(t));
4240
});
4341
}
4442

@@ -47,24 +45,6 @@ class RafSub<M> extends Sub<M> {
4745
}
4846
}
4947

50-
/**
51-
* @deprecated Use {@link rafCmd} and manage
52-
* @param mapper
53-
*/
5448
export function onAnimationFrame<M>(mapper: (t: number) => M): Sub<M> {
5549
return new RafSub(mapper);
5650
}
57-
58-
class RafCmd<Msg> extends Cmd<Msg> {
59-
constructor(private readonly mapper: (t: number) => Msg) {
60-
super();
61-
}
62-
63-
execute(dispatch: Dispatcher<Msg>): void {
64-
requestAnimationFrame((t) => dispatch(this.mapper(t)));
65-
}
66-
}
67-
68-
export function rafCmd<Msg>(mapper: (t: number) => Msg): Cmd<Msg> {
69-
return new RafCmd(mapper);
70-
}

core/src/TeaCup/Sub.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { Dispatcher } from './Dispatcher';
2727

2828
export abstract class Sub<Msg> {
2929
protected dispatcher: Dispatcher<Msg> | undefined;
30+
private active: boolean = false;
3031

3132
static none<Msg>(): Sub<Msg> {
3233
return new SubNone();
@@ -38,16 +39,21 @@ export abstract class Sub<Msg> {
3839

3940
init(dispatch: Dispatcher<Msg>): void {
4041
this.dispatcher = dispatch;
42+
this.active = true;
4143
this.onInit();
4244
}
4345

4446
release(): void {
45-
this.dispatcher = undefined;
47+
this.active = false;
4648
this.onRelease();
4749
}
4850

51+
isActive() {
52+
return this.active;
53+
}
54+
4955
protected dispatch(m: Msg): void {
50-
this.dispatcher && this.dispatcher(m);
56+
this.dispatcher?.(m);
5157
}
5258

5359
protected onInit() {}

samples/src/Samples/EventsSample.tsx

Lines changed: 67 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,41 @@
2525

2626
import * as React from 'react';
2727
import { Dispatcher, Cmd, Sub, noCmd, nothing, just, Maybe } from 'tea-cup-core';
28-
import {DocumentEvents, WindowEvents} from "react-tea-cup";
28+
import { DocumentEvents, WindowEvents } from 'react-tea-cup';
2929

3030
export type Model = {
31-
clicked: Maybe<MousePosition>
32-
moved: Maybe<MousePosition>
33-
scrolled: Maybe<Position>
34-
nbResizeLeft: number
35-
nbResizeRight: number
31+
clicked: Maybe<MousePosition>;
32+
moved: Maybe<MousePosition>;
33+
scrolled: Maybe<Position>;
34+
nbResizeLeft: number;
35+
nbResizeRight: number;
3636
};
3737

3838
type MousePosition = {
3939
pos: Position;
4040
page: Position;
4141
offset: Position;
42-
}
42+
};
4343

44-
type Position = [number, number]
44+
type Position = [number, number];
4545

46-
export type Msg = {
47-
type: 'clicked',
48-
position: MousePosition
49-
} | {
50-
type: 'moved',
51-
position: MousePosition
52-
} | {
53-
type: 'resized',
54-
left: boolean
55-
} | {
56-
type: 'scrolled',
57-
scroll: Position
58-
};
46+
export type Msg =
47+
| {
48+
type: 'clicked';
49+
position: MousePosition;
50+
}
51+
| {
52+
type: 'moved';
53+
position: MousePosition;
54+
}
55+
| {
56+
type: 'resized';
57+
left: boolean;
58+
}
59+
| {
60+
type: 'scrolled';
61+
scroll: Position;
62+
};
5963

6064
export function init(): [Model, Cmd<Msg>] {
6165
return noCmd({
@@ -70,18 +74,9 @@ export function init(): [Model, Cmd<Msg>] {
7074
export function view(dispatch: Dispatcher<Msg>, model: Model) {
7175
return (
7276
<div className="events">
73-
{model.clicked
74-
.map(viewMousePosition('Clicked'))
75-
.withDefault(<div>Waiting for click ...</div>)
76-
}
77-
{model.moved
78-
.map(viewMousePosition('Moved'))
79-
.withDefault(<div>Waiting for move ...</div>)
80-
}
81-
{model.scrolled
82-
.map(viewPosition('Scrolled'))
83-
.withDefault(<div>Waiting for move ...</div>)
84-
}
77+
{model.clicked.map(viewMousePosition('Clicked')).withDefault(<div>Waiting for click ...</div>)}
78+
{model.moved.map(viewMousePosition('Moved')).withDefault(<div>Waiting for move ...</div>)}
79+
{model.scrolled.map(viewPosition('Scrolled')).withDefault(<div>Waiting for move ...</div>)}
8580
<div>resized left : {model.nbResizeLeft}</div>
8681
<div>resized right : {model.nbResizeRight}</div>
8782
</div>
@@ -93,21 +88,21 @@ export function update(msg: Msg, model: Model): [Model, Cmd<Msg>] {
9388
case 'clicked': {
9489
const model1: Model = {
9590
...model,
96-
clicked: just(msg.position)
91+
clicked: just(msg.position),
9792
};
9893
return [model1, Cmd.none()];
9994
}
10095
case 'moved': {
10196
const model1: Model = {
10297
...model,
103-
moved: just(msg.position)
98+
moved: just(msg.position),
10499
};
105100
return [model1, Cmd.none()];
106101
}
107102
case 'scrolled': {
108103
const model1: Model = {
109104
...model,
110-
scrolled: just(msg.scroll)
105+
scrolled: just(msg.scroll),
111106
};
112107
return [model1, Cmd.none()];
113108
}
@@ -126,55 +121,62 @@ const windowEvents = new WindowEvents<Msg>();
126121

127122
export function subscriptions(model: Model): Sub<Msg> {
128123
return Sub.batch([
129-
documentEvents.on('click', (e: MouseEvent) => (
130-
{
131-
type: 'clicked',
132-
position: {
133-
pos: [e.x, e.y],
134-
page: [e.pageX, e.pageY],
135-
offset: [e.offsetX, e.offsetY]
136-
}
137-
} as Msg
138-
)),
139-
documentEvents.on('mousemove', (e: MouseEvent) => ({
140-
type: 'moved',
141-
position: {
142-
pos: [e.x, e.y],
143-
page: [e.pageX, e.pageY],
144-
offset: [e.offsetX, e.offsetY]
145-
}
146-
} as Msg)),
124+
documentEvents.on(
125+
'click',
126+
(e: MouseEvent) =>
127+
({
128+
type: 'clicked',
129+
position: {
130+
pos: [e.x, e.y],
131+
page: [e.pageX, e.pageY],
132+
offset: [e.offsetX, e.offsetY],
133+
},
134+
} as Msg),
135+
),
136+
documentEvents.on(
137+
'mousemove',
138+
(e: MouseEvent) =>
139+
({
140+
type: 'moved',
141+
position: {
142+
pos: [e.x, e.y],
143+
page: [e.pageX, e.pageY],
144+
offset: [e.offsetX, e.offsetY],
145+
},
146+
} as Msg),
147+
),
147148
windowEvents.on('scroll', (e: Event) => {
148149
return {
149150
type: 'scrolled',
150-
scroll: [window.scrollX, window.scrollY]
151+
scroll: [window.scrollX, window.scrollY],
151152
} as Msg;
152153
}),
153154
windowEvents.on('resize', () => {
154155
return {
155156
type: 'resized',
156-
left: true
157+
left: true,
157158
} as Msg;
158159
}),
159160
windowEvents.on('resize', () => {
160161
return {
161162
type: 'resized',
162-
left: false
163+
left: false,
163164
} as Msg;
164165
}),
165166
]);
166167
}
167168

168169
function viewMousePosition(title: string) {
169170
return (position: MousePosition) => {
170-
return (<div>
171-
<b>{title}: </b>
172-
{viewPosition('Position')(position.pos)}&nbsp;
173-
{viewPosition('Page')(position.page)}&nbsp;
174-
{viewPosition('Offset')(position.offset)}
175-
</div>
171+
return (
172+
<div>
173+
<b>{title}: </b>
174+
{viewPosition('Position')(position.pos)}&nbsp;
175+
{viewPosition('Page')(position.page)}&nbsp;
176+
{viewPosition('Offset')(position.offset)}
177+
</div>
176178
);
177-
}
179+
};
178180
}
179181

180182
function viewPosition(title: string) {
@@ -184,5 +186,5 @@ function viewPosition(title: string) {
184186
{title} {position[0]},{position[1]}&nbsp;
185187
</>
186188
);
187-
}
189+
};
188190
}

samples/src/Samples/Raf.tsx

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*
2424
*/
2525

26-
import { Cmd, Dispatcher, noCmd, rafCmd, Sub } from 'tea-cup-core';
26+
import { Cmd, Dispatcher, noCmd, onAnimationFrame, Sub } from 'tea-cup-core';
2727
import * as React from 'react';
2828

2929
export interface Model {
@@ -110,42 +110,22 @@ function viewAnim(text: String, t: number) {
110110
export function update(msg: Msg, model: Model): [Model, Cmd<Msg>] {
111111
switch (msg.type) {
112112
case 'toggle':
113-
const newModel: Model = {
114-
...model,
115-
started: !model.started,
116-
};
117-
return [
118-
newModel,
119-
newModel.started
120-
? Cmd.batch([
121-
rafCmd((t: number) => ({ type: 'raf', t } as Msg)),
122-
rafCmd((t: number) => ({ type: 'raf2', t } as Msg)),
123-
])
124-
: Cmd.none(),
125-
];
113+
return noCmd({ ...model, started: !model.started });
126114
case 'raf': {
127115
const delta = msg.t - model.t;
128116
const fps = delta === 0 ? model.fps : 1000 / delta;
129-
const cmd: Cmd<Msg> = model.started ? rafCmd((t: number) => ({ type: 'raf', t } as Msg)) : Cmd.none();
130-
return [
131-
{
132-
...model,
133-
t: msg.t,
134-
fps: fps,
135-
},
136-
cmd,
137-
];
117+
return noCmd({
118+
...model,
119+
t: msg.t,
120+
fps: fps,
121+
});
138122
}
139123

140124
case 'raf2': {
141-
const cmd: Cmd<Msg> = model.started ? rafCmd((t: number) => ({ type: 'raf2', t } as Msg)) : Cmd.none();
142-
return [
143-
{
144-
...model,
145-
t2: msg.t,
146-
},
147-
cmd,
148-
];
125+
return noCmd({
126+
...model,
127+
t2: msg.t,
128+
});
149129
}
150130
case 'text-changed':
151131
return noCmd({
@@ -156,5 +136,16 @@ export function update(msg: Msg, model: Model): [Model, Cmd<Msg>] {
156136
}
157137

158138
export function subscriptions(model: Model): Sub<Msg> {
159-
return Sub.none();
139+
if (model.started) {
140+
return Sub.batch([
141+
onAnimationFrame((t: number) => {
142+
return { type: 'raf', t: t } as Msg;
143+
}),
144+
onAnimationFrame((t: number) => {
145+
return { type: 'raf2', t: t } as Msg;
146+
}),
147+
]);
148+
} else {
149+
return Sub.none<Msg>();
150+
}
160151
}

0 commit comments

Comments
 (0)