Skip to content

Commit

Permalink
Added support to show pulse within channel widget
Browse files Browse the repository at this point in the history
  • Loading branch information
juhamust committed Oct 16, 2016
1 parent d492d40 commit 31a7960
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 85 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ module.exports = {
{
type: 'slack.channel',
channel: 'general',
showPulse: true,
columns: 1, rows: 1,
x: 0, y: 0
},
Expand Down Expand Up @@ -112,6 +113,8 @@ key | required | description
`title` | no | *Textual title to show. Example: '#mychannel'.*
`channel` | no | *Name of the channel to follow. Defaults to all public channels where token has permissions to*
`imageSize` | no | Scaling of image: initial, cover, contain. Default to `initial`
`showImages` | no | Show images or not. Defaults to `true`
`showPulse` | no | Show pulse visualisation on each message or not. Defaults to `false`
`config` | no | Override default pulse config parameters or colors. See source code for details
### usage
Expand Down Expand Up @@ -155,6 +158,10 @@ Distributed under the MIT license
## Changelog
### Release 0.6.0
- Add support to show pulse within channel widget
### Release 0.5.0
- Adjust font size automatically: Based on given size and length of text
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mozaik-ext-slack",
"version": "0.5.0",
"version": "0.6.0",
"description": "Slack widgets to Mozaik dashboard",
"main": "./components.js",
"scripts": {
Expand Down
7 changes: 6 additions & 1 deletion src/components/Channel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Mozaik from 'mozaik/browser';
import classNames from 'classnames';
import moment from 'moment';
import Since from './Since.jsx';
import Impulse from './Impulse.jsx';

class Channel extends Component {
constructor(props) {
Expand Down Expand Up @@ -49,7 +50,6 @@ class Channel extends Component {
}

this.setState({ message: message });
console.log('State changed', this.state);
}

getFontSize(width, height, textLength = 1) {
Expand Down Expand Up @@ -99,6 +99,8 @@ class Channel extends Component {
'slack-channel__footer--image': message ? message.image : false
});

const pulse = this.props.showPulse ? <Impulse className="slack-channel__impulse" message={content.text}></Impulse> : null;

return (<div>
<div className="widget__header">
<span className="widget__header__subject">{content.title}</span>
Expand All @@ -108,6 +110,7 @@ class Channel extends Component {
<div className={content.bodyClass}>
<div style={content.style} className="slack-channel__message--value">{content.text}</div>
</div>
{pulse}
<div className={content.footerClass}>
<div className="slack-channel__footer--avatar"><img src={content.avatar} /></div>
<div className="slack-channel__footer--meta">
Expand All @@ -124,12 +127,14 @@ Channel.propTypes = {
title: React.PropTypes.string,
channel: React.PropTypes.string,
showImages: React.PropTypes.bool,
showPulse: React.PropTypes.bool,
imageSize: React.PropTypes.oneOf(['initial', 'contain', 'cover'])
};

Channel.defaultProps = {
channel: null,
showImages: true,
showPulse: false,
imageSize: 'initial',
};

Expand Down
120 changes: 120 additions & 0 deletions src/components/Impulse.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { Component } from 'react';
import reactMixin from 'react-mixin';
import { ListenerMixin } from 'reflux';
import Mozaik from 'mozaik/browser';
var d3 = require('d3');
var ease = require('d3-ease');
import _ from 'lodash';


function pulse(opts) {
for (var i = 1; i < opts.count; ++i) {
d3.select(opts.element)
.append('circle')
.attr('cx', opts.position.x)
.attr('cy', opts.position.y)
.attr('r', 30)
.attr('stroke-width', opts.strokeWidth / (i))
.attr('fill', 'transparent')
.attr('stroke', `rgba(${opts.color.r}, ${opts.color.g}, ${opts.color.b}, 255)`)
.transition()
.delay(Math.pow(i, 2.5) * opts.delay)
.duration(opts.duration)
.ease(ease.easeQuadIn)
.attr('stroke-width', 2)
.attr('stroke', `rgba(${opts.color.r}, ${opts.color.g}, ${opts.color.b}, 0)`)
.attr('r', opts.radius)
.remove();
}
}

function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}

class Impulse extends Component {
constructor(props) {
super(props);
this.mounted = false;
this.state = {
colorIndex: 0,
height: 400,
width: 400
};
this.config = _.defaultsDeep(this.props.config || {}, {
delay: 10,
count: 8,
duration: 4000,
strokeWidth: 20,
// Defaults to Slack colours
colors: [
{ r: 112, g: 204, b: 220 }, // blue
{ r: 223, g: 168, b: 35 }, // orange
{ r: 225, g: 22, b: 101 }, // red
{ r: 61, g: 186, b: 145 } // green
]
});
}

componentDidMount() {
this.mounted = true;

// Get area size
const bodyElement = this._body.getDOMNode();
this.setState({
element: this._svg.getDOMNode(),
height: bodyElement.clientHeight,
width: bodyElement.clientWidth
});
}

componentWillUnmount() {
this.mounted = false;
}

componentWillReceiveProps() {
// Increase / reset the color index on each update
const nextColorIndex = this.state.colorIndex < (this.config.colors.length - 1) ? this.state.colorIndex + 1 : 0;
this.setState({ colorIndex: nextColorIndex });
}

render() {
const title = this.props.title;
//const nextColorIndex = this.state.colorIndex < (this.config.colors.length - 1) ? this.state.colorIndex + 1 : 0;

// NOTE: Modifying DOM with D3 is not ideal, consider
// using https://github.com/Olical/react-faux-dom later on
pulse(_.extend({
height: this.state.height,
width: this.state.width,
radius: _.max([this.state.height, this.state.width]) + 100,
element: this.state.element,
color: this.config.colors[this.state.colorIndex || 0],
position: {
x: getRandom(0, this.state.width),
y: getRandom(0, this.state.height)
}
}, this.config));

return (
<div className="slack__impulse--body widget__body" ref={(c) => this._body = c}>
<svg ref={(c) => this._svg = c} height={this.state.height} width={this.state.width}></svg>
</div>
);
}
}

Impulse.propTypes = {
title: React.PropTypes.string,
channel: React.PropTypes.string,
config: React.PropTypes.object,
message: React.PropTypes.string
};

Impulse.defaultProps = {
title: 'Slack',
channel: null
};


export default Impulse;
87 changes: 4 additions & 83 deletions src/components/Pulse.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,16 @@ import Mozaik from 'mozaik/browser';
var d3 = require('d3');
var ease = require('d3-ease');
import _ from 'lodash';
import Impulse from './Impulse.jsx';


function pulse(opts) {
for (var i = 1; i < opts.count; ++i) {
d3.select(opts.element)
.append('circle')
.attr('cx', opts.position.x)
.attr('cy', opts.position.y)
.attr('r', 30)
.attr('stroke-width', opts.strokeWidth / (i))
.attr('fill', 'transparent')
.attr('stroke', `rgba(${opts.color.r}, ${opts.color.g}, ${opts.color.b}, 255)`)
.transition()
.delay(Math.pow(i, 2.5) * opts.delay)
.duration(opts.duration)
.ease(ease.easeQuadIn)
.attr('stroke-width', 2)
.attr('stroke', `rgba(${opts.color.r}, ${opts.color.g}, ${opts.color.b}, 0)`)
.attr('r', opts.radius)
.remove();
}
}

function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}

class Pulse extends Component {
constructor(props) {
super(props);
this.mounted = false;
this.state = {
colorIndex: 0,
height: 400,
width: 400
message: null
};
this.config = _.defaultsDeep(this.props.config || {}, {
delay: 10,
count: 8,
duration: 4000,
strokeWidth: 20,
// Defaults to Slack colours
colors: [
{ r: 112, g: 204, b: 220 }, // blue
{ r: 223, g: 168, b: 35 }, // orange
{ r: 225, g: 22, b: 101 }, // red
{ r: 61, g: 186, b: 145 } // green
]
});
}

getApiRequest() {
Expand All @@ -67,46 +28,9 @@ class Pulse extends Component {
}

onApiData(data) {
const nextColorIndex = this.state.colorIndex < (this.config.colors.length - 1) ? this.state.colorIndex + 1 : 0;
const nextPositionIndex = this.state.positionIndex < (this.config.colors.length - 1) ? this.state.positionIndex + 1 : 0;

this.setState({
colorIndex: nextColorIndex,
positionIndex: nextPositionIndex,
message: data
});

if (!this.mounted) {
return;
}

// Get area size
const bodyElement = this._body.getDOMNode();
this.setState({
height: bodyElement.clientHeight,
width: bodyElement.clientWidth
});

// NOTE: Modifying DOM with D3 is not ideal, consider
// using https://github.com/Olical/react-faux-dom later on
pulse(_.extend({
height: this.state.height,
width: this.state.width,
radius: _.max([this.state.height, this.state.width]) + 100,
element: this._svg.getDOMNode(),
color: this.config.colors[this.state.colorIndex || 0],
position: {
x: getRandom(0, this.state.width),
y: getRandom(0, this.state.height)
}
}, this.config));
}

componentDidMount() {
this.mounted = true;
}

componentWillUnmount() {
this.mounted = false;
}

render() {
Expand All @@ -118,9 +42,7 @@ class Pulse extends Component {
<span className="widget__header__subject">{title}</span>
<i className="fa fa-comment-o" />
</div>
<div className="slack__pulse--body widget__body" ref={(c) => this._body = c}>
<svg ref={(c) => this._svg = c} height={this.state.height} width={this.state.width}></svg>
</div>
<Impulse message={this.state.message}></Impulse>
</div>
);
}
Expand All @@ -129,7 +51,6 @@ class Pulse extends Component {
Pulse.propTypes = {
title: React.PropTypes.string,
channel: React.PropTypes.string,
config: React.PropTypes.object
};

Pulse.defaultProps = {
Expand Down
14 changes: 14 additions & 0 deletions styl/_slack.styl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
text-align: center
font-size 2rem
line-height 2.5rem
z-index 5000

.slack-channel__message--value
color white
Expand Down Expand Up @@ -65,3 +66,16 @@
overflow hidden
height 100%
width 100%


.slack-channel__impulse
z-index 0
position absolute
top 0
left 0

.slack__impulse--body
top 0
overflow hidden
height 100%
width 100%

0 comments on commit 31a7960

Please sign in to comment.