Skip to content

Commit 7f7d20d

Browse files
authored
Merge pull request #187 from graphistry/dev/onPlayComplete
add `onPlayComplete`
2 parents 7b46076 + 0a5f108 commit 7f7d20d

5 files changed

Lines changed: 84 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
1010
### Added
1111

1212
* **client-api-react**: Add `pointStrokeWidth` and `showCollections` bindings
13+
* **client-api**: Add `playUpdates` observer which creates event when graph simulation completes
14+
* **client-api-react**: Add `onPlayComplete` callback property
1315

1416
### Security
1517

projects/client-api-react/src/index.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as gAPI from '@graphistry/client-api';
99
import { ajax, catchError, first, forkJoin, map, of, switchMap, tap } from '@graphistry/client-api'; // avoid explicit rxjs dep
1010
import { bg } from './bg';
1111
import { bindings, panelNames, calls } from './bindings.js';
12-
import { Client as ClientBase, ClientPKey as ClientPKeyBase, selectionUpdates, subscribeLabels } from '@graphistry/client-api';
12+
import { Client as ClientBase, ClientPKey as ClientPKeyBase, selectionUpdates, subscribeLabels, playUpdates } from '@graphistry/client-api';
1313

1414
export const Client = ClientBase;
1515
export const ClientPKey = ClientPKeyBase;
@@ -102,6 +102,7 @@ const propTypes = {
102102
onUpdateObservableG: PropTypes.func,
103103
onSelectionUpdate: PropTypes.func,
104104
onLabelsUpdate: PropTypes.func,
105+
onPlayComplete: PropTypes.func,
105106
selectionUpdateOptions: PropTypes.object,
106107

107108
queryParamExtra: PropTypes.object
@@ -465,6 +466,7 @@ const Graphistry = forwardRef((props, ref) => {
465466
onUpdateObservableG,
466467
onSelectionUpdate,
467468
onLabelsUpdate,
469+
onPlayComplete,
468470
selectionUpdateOptions
469471
} = props;
470472

@@ -556,6 +558,20 @@ const Graphistry = forwardRef((props, ref) => {
556558
}
557559
}, [g, onLabelsUpdate])
558560

561+
useEffect(() => {
562+
if (g && onPlayComplete) {
563+
const sub = playUpdates(g)
564+
.subscribe(
565+
(v) => onPlayComplete(undefined, v),
566+
(error) => onPlayComplete(error)
567+
);
568+
569+
return () => {
570+
sub && sub.unsubscribe();
571+
};
572+
}
573+
}, [g, onPlayComplete])
574+
559575
const playNormalized = typeof play === 'boolean' ? play : (play | 0) * 1000;
560576
const optionalParams = (type ? `&type=${type}` : ``) +
561577
(controls ? `&controls=${controls}` : ``) +

projects/client-api-react/src/stories/Graphistry.stories.jsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,24 @@ export const OnLabelUpdate = {
9797
},
9898
};
9999

100+
export const OnPlayUpdate = {
101+
render: (args) => {
102+
const [numPlayComplete, setNumPlayComplete] = useState(0);
103+
104+
const onPlayUpdate = () => {
105+
console.log('onLabelsUpdate');
106+
setNumPlayComplete(numPlayComplete + 1);
107+
};
108+
109+
return (
110+
<div>
111+
{`Number of play completed: ${numPlayComplete}`}
112+
<Graphistry {...defaultSettings} {...args} onPlayUpdate={onPlayUpdate} />
113+
</div>
114+
);
115+
},
116+
};
117+
100118
export const NoClusteringOnLoad = {
101119
render: (args) => <Graphistry {...defaultSettings} {...args} play={0} />,
102120
};

projects/client-api/src/index.js

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ import {
133133
mergeAll,
134134
Observable,
135135
of,
136+
pairwise,
136137
pipe,
137138
ReplaySubject,
138139
BehaviorSubject,
@@ -1510,7 +1511,7 @@ export function subscribeSelections({ onChange, g }) {
15101511
* The inner {@link Observable} for a label will complete if the label is removed from the screen.
15111512
* </p><p>
15121513
* @function labelUpdates
1513-
* @param {@link GraphistryState} [g] A {@link GraphistryState} {@link Observable} or depricated, cache an object.
1514+
* @param {@link GraphistryState} [g] A {@link GraphistryState} {@link Observable} or deprecated, cache an object.
15141515
* @return {Observable<Observable<LabelEvent>>} An {@link Observable} of inner {Observables}, where each
15151516
* inner {@link Observable} represents the lifetime of a label in the visualization.
15161517
* @example
@@ -1670,6 +1671,48 @@ export function subscribeLabels({ onChange, onExit, onError, g }) {
16701671
.subscribe({ error: onError });
16711672
}
16721673

1674+
/**
1675+
* Subscribe to graph simulation completion event
1676+
* @function playUpdates
1677+
* @param {@link GraphistryState} [g] A {@link GraphistryState} {@link Observable}
1678+
* @return {Subscription} A {@link Subscription} that can be used to react to the play updates
1679+
* @example
1680+
* GraphistryJS(document.getElementById('viz'))
1681+
* .pipe(
1682+
* map(playUpdates),
1683+
* tap(() => console.log('Play completed')),
1684+
* })
1685+
* .subscribe();
1686+
*/
1687+
export function playUpdates(g) {
1688+
if (!(g.subscriptionAPIVersion >= 1)) {
1689+
return throwError(() => new Error('playUpdates is not available the currently embedded graphistry viz.'));
1690+
}
1691+
1692+
const selectionPath = ".labels";
1693+
return (new BehaviorSubject('Initialize playUpdates stream')
1694+
.pipe(
1695+
tap(() => {
1696+
console.debug('postMessage subscription', '@client-api.playUpdate');
1697+
g.iFrame.contentWindow.postMessage({ type: 'graphistry-subscribe', agent: 'graphistryjs', path: selectionPath }, '*');
1698+
}),
1699+
finalize(() => {
1700+
console.debug('postMessage unsubscribe', '@client-api.playUpdate');
1701+
g.iFrame.contentWindow.postMessage({ type: 'graphistry-unsubscribe', agent: 'graphistryjs', path: selectionPath }, '*');
1702+
}),
1703+
switchMap(() =>
1704+
fromEvent(window, 'message').pipe(
1705+
map(o => o.data),
1706+
filter(o => o && o.type === 'graphistry-sub-update' && o.path === selectionPath),
1707+
map(o => o.data),
1708+
pairwise(),
1709+
filter(([{ simulating: prevSim }, { simulating: currSim }]) => prevSim && !currSim),
1710+
shareReplay({ bufferSize: 1, refCount: true })
1711+
),
1712+
)
1713+
));
1714+
}
1715+
16731716
class GraphistryState {
16741717

16751718
constructor(subscriptionAPIVersion, iFrame, models, result) {
@@ -1768,7 +1811,7 @@ function addCallbacks(obs, target) {
17681811
target.labelUpdates = labelUpdates;// lift(obs, labelUpdates);
17691812
target.subscribeLabels = subscribeLabels;//lift(obs, subscribeLabels);
17701813
target.selectionUpdates = selectionUpdates; // lift(obs, selectionUpdates);
1771-
target.subscribeLabels
1814+
target.playUpdates = playUpdates;
17721815
return target;
17731816
}
17741817

projects/client-api/src/rxjs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
map,
2929
mergeMap,
3030
mergeAll,
31+
pairwise,
3132
scan,
3233
share,
3334
shareReplay,
@@ -58,6 +59,7 @@ export {
5859
mergeAll,
5960
Observable,
6061
of,
62+
pairwise,
6163
pipe,
6264
ReplaySubject,
6365
retryWhen,

0 commit comments

Comments
 (0)