Skip to content

Commit

Permalink
fix(spawn): for spawn machines stored on nested context data, support…
Browse files Browse the repository at this point in the history
… reactivity
  • Loading branch information
NullVoxPopuli committed Jan 8, 2022
1 parent 30e2d5d commit 3a1ca73
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 15 deletions.
1 change: 1 addition & 0 deletions ember-statechart-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"dependencies": {
"@embroider/addon-shim": "^0.50.0",
"babel-plugin-ember-template-compilation": "^1.0.1",
"ember-deep-tracked": "^1.3.7",
"ember-tracked-storage-polyfill": "^1.0.0"
},
"peerDependencies": {
Expand Down
32 changes: 31 additions & 1 deletion ember-statechart-component/src/-private/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function reactiveInterpreter(interpreter: Interpreter<unknown>) {

getValue(storage);

return Reflect.get(target, key, receiver);
return wrapObject(Reflect.get(target, key, receiver));
}
}

Expand All @@ -96,3 +96,33 @@ function dirtyState(interpreter: Interpreter<unknown>) {

setValue(storage, null);
}

/**
* Lazy deep reactivity that specifically only looks for
* interpreters stored on the context
*/
function wrapObject(unwrapped: object): object {
return new Proxy(unwrapped, {
get(target, key, receiver) {
let value = Reflect.get(target, key, receiver);

if (typeof key !== 'string') {
return value;
}

if (value instanceof Interpreter) {
return reactiveInterpreter(value);
}

if (Array.isArray(value)) {
return wrapObject(value);
}

if (value && typeof value === 'object' && value.prototype === undefined) {
return wrapObject(value);
}

return value;
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import { hbs } from 'ember-cli-htmlbars';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';

import { assign, createMachine, spawn } from 'xstate';
import { assign, createMachine, send, spawn } from 'xstate';

import type { Interpreter } from 'xstate';

type Send = Interpreter<unknown>['send'];

/**
* All of these tests are based off real-world usages in one way or another
* (even if simplified)
*/
module('Dynamic Machines', function (hooks) {
setupRenderingTest(hooks);

Expand Down Expand Up @@ -127,6 +131,77 @@ module('Dynamic Machines', function (hooks) {
assert.strictEqual(active, 1);
assert.strictEqual(inactive, 2);
});

test('a dynamically spawned machined, can have delayed events', async function (assert) {
const postMachine = createMachine({
initial: 'loading',
states: {
loading: {
entry: [send('LOADED', { delay: 100 })],
on: {
LOADED: 'loaded',
},
},
loaded: {},
},
});

const DetailLoadingState = createMachine({
initial: 'active',
context: {
posts: {},
},
states: {
active: {
on: {
LOAD_DETAILS: [
{
actions: [
assign({
posts: (context: Record<string, any>, { postId }: any) => {
let posts = context.posts || {};

if (!postId) return posts;

posts[postId] = spawn(postMachine);

return posts;
},
}),
],
},
],
},
},
},
});

this.setProperties({ DetailLoadingState, id: 1, loadPayload: { postId: 1 } });

render(hbs`
<this.DetailLoadingState as |state send|>
{{ (send 'LOAD_DETAILS' this.loadPayload) }}
{{#let (get state.context.posts this.id) as |machine|}}
<out>
{{#if (machine.state.matches 'loaded')}}
Details are loaded
{{else}}
Loading...
{{/if}}
</out>
{{/let}}
</this.DetailLoadingState>
`);

await new Promise((resolve) => setTimeout(resolve, 50));

assert.dom('out').hasText('Loading...');

await new Promise((resolve) => setTimeout(resolve, 200));

assert.dom('out').hasText('Details are loaded');
});
});

module('xstate#invoke', function () {
Expand Down
51 changes: 38 additions & 13 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2237,6 +2237,19 @@
resolve "^1.20.0"
semver "^7.3.2"

"@embroider/macros@^0.50.0":
version "0.50.0"
resolved "https://registry.npmjs.org/@embroider/macros/-/macros-0.50.0.tgz#7a3676f2fe983316b9470c5422c099d7cfeebfb2"
integrity sha512-9yek4uRrWPd/pvQg1amAr8GJSTv1tcGhsbvLXjUnAaJfHZQ4OlycfpXUsSGujqPI3DkOOL6TvPl0jQaEooE6cQ==
dependencies:
"@embroider/shared-internals" "0.50.0"
assert-never "^1.2.1"
ember-cli-babel "^7.26.6"
find-up "^5.0.0"
lodash "^4.17.21"
resolve "^1.20.0"
semver "^7.3.2"

"@embroider/shared-internals@0.49.0":
version "0.49.0"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-0.49.0.tgz#c9e179b33b3e036b064e72b69639e48d53984229"
Expand All @@ -2250,6 +2263,19 @@
semver "^7.3.5"
typescript-memoize "^1.0.1"

"@embroider/shared-internals@0.50.0", "@embroider/shared-internals@^0.50.0":
version "0.50.0"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-0.50.0.tgz#e8f7474af6b8896a91da4ec700810587fa61f7c2"
integrity sha512-pu4YQGyoTsz+KWGTKwMyCMzhoJkUI9PuWiivRilKsywiFt8Ke4ns+nFNnHtPqPKChR72xY2qFjWOP5yAqZq3kw==
dependencies:
babel-import-util "^1.1.0"
ember-rfc176-data "^0.3.17"
fs-extra "^9.1.0"
lodash "^4.17.21"
resolve-package-path "^4.0.1"
semver "^7.3.5"
typescript-memoize "^1.0.1"

"@embroider/shared-internals@^0.40.0":
version "0.40.0"
resolved "https://registry.npmjs.org/@embroider/shared-internals/-/shared-internals-0.40.0.tgz#2f768c60f4f35ba5f9228f046f70324851e8bfe2"
Expand All @@ -2263,19 +2289,6 @@
semver "^7.3.2"
typescript-memoize "^1.0.0-alpha.3"

"@embroider/shared-internals@^0.50.0":
version "0.50.0"
resolved "https://registry.yarnpkg.com/@embroider/shared-internals/-/shared-internals-0.50.0.tgz#e8f7474af6b8896a91da4ec700810587fa61f7c2"
integrity sha512-pu4YQGyoTsz+KWGTKwMyCMzhoJkUI9PuWiivRilKsywiFt8Ke4ns+nFNnHtPqPKChR72xY2qFjWOP5yAqZq3kw==
dependencies:
babel-import-util "^1.1.0"
ember-rfc176-data "^0.3.17"
fs-extra "^9.1.0"
lodash "^4.17.21"
resolve-package-path "^4.0.1"
semver "^7.3.5"
typescript-memoize "^1.0.1"

"@embroider/test-setup@^0.50.0":
version "0.50.0"
resolved "https://registry.yarnpkg.com/@embroider/test-setup/-/test-setup-0.50.0.tgz#7394ae3edd100eb2293d9f0888d2d91b55ac45d3"
Expand Down Expand Up @@ -7306,6 +7319,18 @@ ember-data@~4.1.0:
ember-cli-typescript "^4.1.0"
ember-inflector "^4.0.1"

ember-deep-tracked@^1.3.7:
version "1.3.7"
resolved "https://registry.npmjs.org/ember-deep-tracked/-/ember-deep-tracked-1.3.7.tgz#d45564a54e18fc5f328d554b355488ef27c929ba"
integrity sha512-PFYa55cQYoXJlIpK0+BTZyeqh8i7Rl/h3ij1sGzVJOHU5XGP1xOL4y6Ahd/lQF5ttgBo6WgUwj5fxEiBfjCoXg==
dependencies:
"@embroider/macros" "^0.50.0"
"@glimmer/tracking" "^1.0.4"
ember-cli-babel "^7.26.11"
ember-cli-htmlbars "^6.0.1"
ember-cli-typescript "^4.2.1"
ember-tracked-storage-polyfill "^1.0.0"

ember-destroyable-polyfill@^2.0.3:
version "2.0.3"
resolved "https://registry.npmjs.org/ember-destroyable-polyfill/-/ember-destroyable-polyfill-2.0.3.tgz#1673ed66609a82268ef270a7d917ebd3647f11e1"
Expand Down

0 comments on commit 3a1ca73

Please sign in to comment.