Skip to content

Commit

Permalink
Add drag and drop for the speaker feed (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxwellBo authored Mar 19, 2019
1 parent 1f38033 commit a9af56b
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 29 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
"@types/lodash": "^4.14.116",
"@types/node": "10.12.12",
"@types/react": "^16.8.4",
"@types/react-beautiful-dnd": "^10.1.0",
"@types/react-dom": "^16.8.2",
"@types/react-router-dom": "^4.3.1",
"file-saver": "^2.0.0-rc.3",
"firebase": "^5.7.0",
"lodash": "^4.17.11",
"react": "16.8.3",
"react-beautiful-dnd": "^10.0.4",
"react-dom": "16.8.3",
"react-ga": "^2.5.3",
"react-router-dom": "^4.3.1",
Expand Down
2 changes: 1 addition & 1 deletion src/components/caucus/CaucusNextSpeaking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export class CaucusNextSpeaking extends React.Component<Props, {}> {
/>
<SpeakerFeed
data={caucus ? caucus.queue : undefined}
fref={props.fref.child('queue')}
queueFref={props.fref.child('queue')}
speaking={caucus ? caucus.speaking : undefined}
speakerTimer={props.speakerTimer}
/>
Expand Down
132 changes: 105 additions & 27 deletions src/components/caucus/SpeakerFeed.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
/**
* This is truly the most fucked up file in the entire codebase.
* Mercy up on all those who must modify this.
*/

import { TimerData } from '../Timer';
import * as React from 'react';
import { Feed, Icon, Flag, Label } from 'semantic-ui-react';
import { Feed, Icon, Flag, Label, FeedContent, FeedEvent } from 'semantic-ui-react';
import { runLifecycle, Lifecycle } from '../../actions/caucusActions';
import { parseFlagName } from '../Member';
import { Dictionary } from '../../types';
import { DragDropContext, Droppable, Draggable, DraggableProvided, DropResult } from 'react-beautiful-dnd';

export enum Stance {
For = 'For',
Expand All @@ -12,7 +18,7 @@ export enum Stance {
}

export interface SpeakerEvent {
who: string; // FIXME: @mbo you dumb fuck, this was meant to be MemberID, not their fucking name
who: string;
stance: Stance;
duration: number;
}
Expand All @@ -28,16 +34,17 @@ const StanceIcon = (props: { stance: Stance }) => {
}
};

export const SpeakerFeedEntry = (props: {
data?: SpeakerEvent,
export class SpeakerFeedEntry extends React.PureComponent<{
data?: SpeakerEvent,
speaking?: SpeakerEvent,
fref: firebase.database.Reference,
speakerTimer: TimerData
}) => {
fref: firebase.database.Reference,
speakerTimer: TimerData,
draggableProvided?: DraggableProvided
}> {

const { data, speaking, fref, speakerTimer } = props;
yieldHandler = () => {
const { fref, data, speakerTimer, speaking } = this.props;

const yieldHandler = () => {
const queueHeadDetails = {
queueHeadData: data,
queueHead: fref
Expand All @@ -63,8 +70,10 @@ export const SpeakerFeedEntry = (props: {
runLifecycle({ ...lifecycle, ...queueHeadDetails });
};

return data ? (
<Feed.Event>
renderContent() {
const { data, speaking, fref } = this.props;

return data ? (
<Feed.Content>
<Feed.Summary>
<Feed.User>
Expand All @@ -81,40 +90,109 @@ export const SpeakerFeedEntry = (props: {
<Label size="mini" as="a" onClick={() => fref.remove()}>
Remove
</Label>
{speaking && (<Label size="mini" as="a" onClick={yieldHandler}>
{speaking && (<Label size="mini" as="a" onClick={this.yieldHandler}>
Yield
</Label>)}
</Feed.Meta>
</Feed.Content>
</Feed.Event>
) : <Feed.Event />;
) : <FeedContent />
}

render() {
const { draggableProvided } = this.props;

return draggableProvided ? (
<div
className="event" // XXX: quite possibly the most bullshit hack known to man
ref={draggableProvided.innerRef}
{...draggableProvided.dragHandleProps}
{...draggableProvided.draggableProps}>
{this.renderContent()}
</div>
) : <FeedEvent>
{this.renderContent()}
</FeedEvent>
}
};

function reorder<T>(list: T[], startIndex: number, endIndex: number) {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);

return result;
};

export const SpeakerFeed = (props: {
data?: Dictionary<string, SpeakerEvent>,
fref: firebase.database.Reference,
queueFref: firebase.database.Reference,
speaking?: SpeakerEvent,
speakerTimer: TimerData
}) => {
const { data, fref, speaking, speakerTimer } = props;
const { data, queueFref, speaking, speakerTimer } = props;

const events = data || {};

const eventItems = Object.keys(events).map(key =>
const eventItems = Object.keys(events).map((key, index) =>
(
<SpeakerFeedEntry
key={key}
data={events[key]}
fref={fref.child(key)}
speaking={speaking}
speakerTimer={speakerTimer}
/>
<Draggable key={key} draggableId={key} index={index}>
{(provided, snapshot) =>
<SpeakerFeedEntry
draggableProvided={provided}
key={key}
data={events[key]}
fref={queueFref.child(key)}
speaking={speaking}
speakerTimer={speakerTimer}
/>
}
</Draggable>
)
);

const onDragEnd = (result: DropResult) => {
// dropped outside the list
if (!result.destination) {
return;
}

const events = data || {};

const reorderedKeys = reorder(
Object.keys(events),
result.source.index,
result.destination.index
);

queueFref.set({});

reorderedKeys.forEach(key => {
const se = (data || {})[key]

if (se) {
queueFref.push().set(se);
}
});
}

return (
<Feed size="large">
{eventItems}
</Feed>
<DragDropContext
onDragEnd={onDragEnd}
>
<Droppable droppableId="droppable">
{(provided, snapshot) =>
<div
ref={provided.innerRef}
{...provided.droppableProps}
>
<Feed
size="large"
>
{eventItems}
</Feed>
</div>
}
</Droppable>
</DragDropContext>
);
};
Loading

0 comments on commit a9af56b

Please sign in to comment.