Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
### Added

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

### Security

Expand Down
18 changes: 17 additions & 1 deletion projects/client-api-react/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import * as gAPI from '@graphistry/client-api';
import { ajax, catchError, first, forkJoin, map, of, switchMap, tap } from '@graphistry/client-api'; // avoid explicit rxjs dep
import { bg } from './bg';
import { bindings, panelNames, calls } from './bindings.js';
import { Client as ClientBase, ClientPKey as ClientPKeyBase, selectionUpdates, subscribeLabels } from '@graphistry/client-api';
import { Client as ClientBase, ClientPKey as ClientPKeyBase, selectionUpdates, subscribeLabels, playUpdates } from '@graphistry/client-api';

export const Client = ClientBase;
export const ClientPKey = ClientPKeyBase;
Expand Down Expand Up @@ -104,6 +104,7 @@ const propTypes = {
onUpdateObservableG: PropTypes.func,
onSelectionUpdate: PropTypes.func,
onLabelsUpdate: PropTypes.func,
onPlayComplete: PropTypes.func,
selectionUpdateOptions: PropTypes.object,

queryParamExtra: PropTypes.object
Expand Down Expand Up @@ -467,6 +468,7 @@ const Graphistry = forwardRef((props, ref) => {
onUpdateObservableG,
onSelectionUpdate,
onLabelsUpdate,
onPlayComplete,
selectionUpdateOptions
} = props;

Expand Down Expand Up @@ -558,6 +560,20 @@ const Graphistry = forwardRef((props, ref) => {
}
}, [g, onLabelsUpdate])

useEffect(() => {
if (g && onPlayComplete) {
const sub = playUpdates(g)
.subscribe(
(v) => onPlayComplete(undefined, v),
(error) => onPlayComplete(error)
);

return () => {
sub && sub.unsubscribe();
};
}
}, [g, onPlayComplete])

const playNormalized = typeof play === 'boolean' ? play : (play | 0) * 1000;
const optionalParams = (type ? `&type=${type}` : ``) +
(controls ? `&controls=${controls}` : ``) +
Expand Down
18 changes: 18 additions & 0 deletions projects/client-api-react/src/stories/Graphistry.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,24 @@ export const OnLabelUpdate = {
},
};

export const OnPlayUpdate = {
render: (args) => {
const [numPlayComplete, setNumPlayComplete] = useState(0);

const onPlayUpdate = () => {
console.log('onLabelsUpdate');
setNumPlayComplete(numPlayComplete + 1);
};

return (
<div>
{`Number of play completed: ${numPlayComplete}`}
<Graphistry {...defaultSettings} {...args} onPlayUpdate={onPlayUpdate} />
</div>
);
},
};

export const NoClusteringOnLoad = {
render: (args) => <Graphistry {...defaultSettings} {...args} play={0} />,
};
Expand Down
47 changes: 45 additions & 2 deletions projects/client-api/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ import {
mergeAll,
Observable,
of,
pairwise,
pipe,
ReplaySubject,
BehaviorSubject,
Expand Down Expand Up @@ -1510,7 +1511,7 @@ export function subscribeSelections({ onChange, g }) {
* The inner {@link Observable} for a label will complete if the label is removed from the screen.
* </p><p>
* @function labelUpdates
* @param {@link GraphistryState} [g] A {@link GraphistryState} {@link Observable} or depricated, cache an object.
* @param {@link GraphistryState} [g] A {@link GraphistryState} {@link Observable} or deprecated, cache an object.
* @return {Observable<Observable<LabelEvent>>} An {@link Observable} of inner {Observables}, where each
* inner {@link Observable} represents the lifetime of a label in the visualization.
* @example
Expand Down Expand Up @@ -1670,6 +1671,48 @@ export function subscribeLabels({ onChange, onExit, onError, g }) {
.subscribe({ error: onError });
}

/**
* Subscribe to graph simulation completion event
* @function playUpdates
* @param {@link GraphistryState} [g] A {@link GraphistryState} {@link Observable}
* @return {Subscription} A {@link Subscription} that can be used to react to the play updates
* @example
* GraphistryJS(document.getElementById('viz'))
* .pipe(
* map(playUpdates),
* tap(() => console.log('Play completed')),
* })
* .subscribe();
*/
export function playUpdates(g) {
if (!(g.subscriptionAPIVersion >= 1)) {
return throwError(() => new Error('playUpdates is not available the currently embedded graphistry viz.'));
}

const selectionPath = ".labels";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, do we want to use .labels or .selection.labels here? Or are we going to create a new path for simulations to handle play updates?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.labels already contains all the info required so thats the message thats being read

return (new BehaviorSubject('Initialize playUpdates stream')
.pipe(
tap(() => {
console.debug('postMessage subscription', '@client-api.playUpdate');
g.iFrame.contentWindow.postMessage({ type: 'graphistry-subscribe', agent: 'graphistryjs', path: selectionPath }, '*');
}),
finalize(() => {
console.debug('postMessage unsubscribe', '@client-api.playUpdate');
g.iFrame.contentWindow.postMessage({ type: 'graphistry-unsubscribe', agent: 'graphistryjs', path: selectionPath }, '*');
}),
switchMap(() =>
fromEvent(window, 'message').pipe(
map(o => o.data),
filter(o => o && o.type === 'graphistry-sub-update' && o.path === selectionPath),
map(o => o.data),
pairwise(),
filter(([{ simulating: prevSim }, { simulating: currSim }]) => prevSim && !currSim),
shareReplay({ bufferSize: 1, refCount: true })
),
)
));
}

class GraphistryState {

constructor(subscriptionAPIVersion, iFrame, models, result) {
Expand Down Expand Up @@ -1768,7 +1811,7 @@ function addCallbacks(obs, target) {
target.labelUpdates = labelUpdates;// lift(obs, labelUpdates);
target.subscribeLabels = subscribeLabels;//lift(obs, subscribeLabels);
target.selectionUpdates = selectionUpdates; // lift(obs, selectionUpdates);
target.subscribeLabels
target.playUpdates = playUpdates;
return target;
}

Expand Down
2 changes: 2 additions & 0 deletions projects/client-api/src/rxjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
map,
mergeMap,
mergeAll,
pairwise,
scan,
share,
shareReplay,
Expand Down Expand Up @@ -58,6 +59,7 @@ export {
mergeAll,
Observable,
of,
pairwise,
pipe,
ReplaySubject,
retryWhen,
Expand Down
Loading