Skip to content

Commit

Permalink
collector
Browse files Browse the repository at this point in the history
  • Loading branch information
alexreardon committed May 3, 2018
1 parent eab420d commit 6c7dad0
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 318 deletions.
62 changes: 62 additions & 0 deletions src/state/dimension-marshal/buffer-marshal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// @flow
import invariant from 'tiny-invariant';

type Args = {|
collector: Collector,
publisher: Publisher,
|}

type Phase = 'IDLE' | 'RUNNING';

const rafWait = () => new Promise(resolve => requestAnimationFrame(resolve));

export default ({ collector, publisher }: Args) => {
let phase: Phase = 'IDLE';
let isRunQueued: boolean = false;

const reset = () => {
// forcing phase to IDLE
phase = 'IDLE';
isRunQueued = false;
};

const stopIfIdle = () => (phase === 'IDLE' ? Promise.reject() : Promise.resolve());

const run = () => {
phase = 'RUNNING';

// This would be easier to read with async/await but the runtime is 10kb

rafWait()
.then(stopIfIdle)
.then(collector.perform)
.then(rafWait)
.then(stopIfIdle)
.then(publisher.perform)
// collection was stopped - we can just exit
.catch()
.then(() => {
if (isRunQueued) {
run();
return;
}
reset();
});
};

const execute = () => {
// A run is already queued
if (isRunQueued) {
return;
}

// We are already performing a run
if (phase === 'RUNNING') {
return;
}

run();
};

return { execute, reset };
};
118 changes: 118 additions & 0 deletions src/state/dimension-marshal/collector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// @flow
import invariant from 'tiny-invariant';
import * as timings from '../../debug/timings';
import type {
DraggableId,
DroppableId,
DraggableDimension,
DroppableDimension,
ScrollOptions,
} from '../../types';
import type { ToBeCollected } from './dimension-marshal-types';

type Collected = {|
draggables: DraggableDimension[],
droppables: DroppableDimension[],
|}

type Args = {|
getToBeCollected: () => ToBeCollected,
getDraggable: (id: DraggableId) => DraggableDimension,
getDroppable: (id: DroppableId) => DroppableDimension,
publish: (droppables: DroppableDimension[], draggables: DraggableDimension[]) => void,
|}

export type Collector = {|
start: (options: ScrollOptions) => void,
stop: () => void,
collect: () => void,
|}

export default ({
publish,
getDraggable,
getDroppable,
getToBeCollected,
}: Args): Collector => {
let isActive: boolean = false;
let frameId: ?AnimationFrameID = null;
let isQueued: boolean = false;
let isRunning: boolean = false;

const collectFromDOM = (toBeCollected: ToBeCollected): Collected => {
const droppables: DroppableDimension[] = toBeCollected.droppables
.map((id: DroppableId): DroppableDimension => getDroppable(id));

const draggables: DraggableDimension[] = toBeCollected.draggables
.map((id: DraggableId): DraggableDimension => getDraggable(id));

return { draggables, droppables };
};

const run = () => {
invariant(isRunning, 'Cannot start a new run when a run is already occurring');

isRunning = true;

// Perform DOM collection in next frame
frameId = requestAnimationFrame(() => {
timings.start('DOM collection');
const toBeCollected: ToBeCollected = getToBeCollected();
const collected: Collected = collectFromDOM(toBeCollected);
timings.finish('DOM collection');

// Perform publish in next frame
frameId = requestAnimationFrame(() => {
timings.start('Bulk dimension publish');
publish(collected.droppables, collected.draggables);
timings.finish('Bulk dimension publish');

// TODO: what if publish caused collection?

frameId = null;
isRunning = false;

if (isQueued) {
isQueued = false;
run();
}
});
});
};

const start = () => {
invariant(!isActive, 'Collector has already been started');
isActive = true;
};

const collect = () => {
invariant(isActive, 'Can only collect when active');
// A run is already queued
if (isQueued) {
return;
}

// We are running and a collection is not queued
// Queue another run
if (isRunning) {
isQueued = true;
}

run();
};

const stop = () => {
if (frameId) {
cancelAnimationFrame(frameId);
}
isRunning = false;
isQueued = false;
isActive = false;
};

return {
start,
stop,
collect,
};
};
6 changes: 4 additions & 2 deletions src/state/dimension-marshal/dimension-marshal-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ export type DroppableEntryMap = {
[key: DroppableId]: DroppableEntry,
}

export type UnknownDescriptorType = DraggableDescriptor | DroppableDescriptor;
export type UnknownDimensionType = DraggableDimension | DroppableDimension;
export type ToBeCollected = {|
draggables: DraggableId[],
droppables: DroppableId[],
|}

export type DimensionMarshal = {|
// Draggable
Expand Down
Loading

0 comments on commit 6c7dad0

Please sign in to comment.