Fix conflicts

This commit is contained in:
Oswaldo Acauan 2016-07-12 14:02:48 +00:00
commit 306ef94237
16 changed files with 312 additions and 86 deletions

1
bigbluebutton-html5/client/main.html Normal file → Executable file
View File

@ -1,4 +1,5 @@
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BBB - HTML5 Client</title>
</head>
<body>

View File

@ -29,7 +29,7 @@ export function clearCollections() {
refreshes. Related to: https://github.com/meteor/meteor/issues/6576
*/
if (process.env.NODE_ENV === "development") {
if (process.env.NODE_ENV === 'development') {
return;
}

View File

@ -4,41 +4,48 @@ import Slides from '/imports/api/slides';
export function addSlideToCollection(meetingId, presentationId, slideObject) {
const url = Npm.require('url');
const http = Npm.require('http');
const imageUri = slideObject.svg_uri != null ? slideObject.svg_uri : slideObject.png_uri;
if (Slides.findOne({
meetingId: meetingId,
'slide.id': slideObject.id,
}) == null) {
const options = url.parse(slideObject.svg_uri);
const options = url.parse(imageUri);
http.get(options, Meteor.bindEnvironment(function (response) {
let chunks = [];
response.on('data', Meteor.bindEnvironment(function (chunk) {
chunks.push(chunk);
})).on('end', Meteor.bindEnvironment(function () {
let buffer = Buffer.concat(chunks);
const dimensions = sizeOf(buffer);
const entry = {
meetingId: meetingId,
presentationId: presentationId,
slide: {
height_ratio: slideObject.height_ratio,
y_offset: slideObject.y_offset,
num: slideObject.num,
x_offset: slideObject.x_offset,
current: slideObject.current,
img_uri: slideObject.svg_uri != null ? slideObject.svg_uri : slideObject.png_uri,
txt_uri: slideObject.txt_uri,
id: slideObject.id,
width_ratio: slideObject.width_ratio,
swf_uri: slideObject.swf_uri,
thumb_uri: slideObject.thumb_uri,
width: dimensions.width,
height: dimensions.height,
},
};
Slides.insert(entry);
let contentType = response.headers['content-type'];
if (contentType.match(/svg/gi) || contentType.match(/png/gi)) {
let chunks = [];
response.on('data', Meteor.bindEnvironment(function (chunk) {
chunks.push(chunk);
})).on('end', Meteor.bindEnvironment(function () {
let buffer = Buffer.concat(chunks);
const dimensions = sizeOf(buffer);
const entry = {
meetingId: meetingId,
presentationId: presentationId,
slide: {
height_ratio: slideObject.height_ratio,
y_offset: slideObject.y_offset,
num: slideObject.num,
x_offset: slideObject.x_offset,
current: slideObject.current,
img_uri: slideObject.svg_uri != null ? slideObject.svg_uri : slideObject.png_uri,
txt_uri: slideObject.txt_uri,
id: slideObject.id,
width_ratio: slideObject.width_ratio,
swf_uri: slideObject.swf_uri,
thumb_uri: slideObject.thumb_uri,
width: dimensions.width,
height: dimensions.height,
},
};
Slides.insert(entry);
}));
} else {
console.log(`Slide file is not accessible or not ready yet`);
console.log(`response content-type`, response.headers['content-type']);
}
}));
}));
//logger.info "added slide id =[#{id}]:#{slideObject.id} in #{meetingId}. Now there
// are #{Slides.find({meetingId: meetingId}).count()} slides in the meeting"

View File

@ -18,8 +18,9 @@ export const renderRoutes = () => (
<Router history={browserHistory}>
<Route path="/join/:meetingID/:userID/:authToken" onEnter={setCredentials} />
<Route path="/" onEnter={() => {
subscribeToCollections()
}}
subscribeToCollections();
}}
getComponent={(nextState, cb) => {
subscribeToCollections(() => cb(null, AppContainer));
}}>

View File

@ -117,7 +117,7 @@ export default class App extends Component {
}
render() {
if(this.props.isLoading) {
if (this.props.isLoading) {
return <Loader/>;
}

View File

@ -40,7 +40,7 @@ let loading = true;
const loadingDep = new Tracker.Dependency;
const getLoading = () => {
loadingDep.depend()
loadingDep.depend();
return loading;
};
@ -60,7 +60,7 @@ export default createContainer(() => {
return {
isLoading: getLoading(),
actionsbar: <ActionsBarContainer />
actionsbar: <ActionsBarContainer />,
};
}, AppContainer);

View File

@ -22,7 +22,7 @@
}
.avatar {
flex-basis: 1.7rem;
flex-basis: 2.2rem;
flex-shrink: 0;
flex-grow: 0;
}

View File

@ -24,6 +24,10 @@ const ScrollCollection = new Mongo.Collection(null);
const mapUser = (user) => ({
id: user.userid,
name: user.name,
emoji: {
status: user.emoji_status,
changedAt: user.set_emoji_time,
},
isPresenter: user.presenter,
isModerator: user.role === 'MODERATOR',
isCurrent: user.userid === Auth.userID,

View File

@ -0,0 +1,108 @@
//This is the code to generate the colors for the user avatar when no image is provided
const stringToPastelColour = (str) => {
str = str.trim().toLowerCase();
let baseRed = 128;
let baseGreen = 128;
let baseBlue = 128;
let seed = 0;
for (let i = 0; i < str.length; seed = str.charCodeAt(i++) + ((seed << 5) - seed));
let a = Math.abs((Math.sin(seed++) * 10000)) % 256;
let b = Math.abs((Math.sin(seed++) * 10000)) % 256;
let c = Math.abs((Math.sin(seed++) * 10000)) % 256;
//build colour
let red = Math.round((a + baseRed) / 2);
let green = Math.round((b + baseGreen) / 2);
let blue = Math.round((c + baseBlue) / 2);
return {
r: red,
g: green,
b: blue,
};
};
// https://www.w3.org/TR/WCAG20/#relativeluminancedef
// http://entropymine.com/imageworsener/srgbformula/
const relativeLuminance = (rgb) => {
let tmp = {};
Object.keys(rgb).forEach((i) => {
let c = rgb[i] / 255;
if (c <= 0.03928) {
tmp[i] = c / 12.92;
} else {
tmp[i] = Math.pow(((c + 0.055) / 1.055), 2.4);
}
});
return (0.2126 * tmp.r + 0.7152 * tmp.g + 0.0722 * tmp.b);
};
/**
* Calculate contrast ratio acording to WCAG 2.0 formula
* Will return a value between 1 (no contrast) and 21 (max contrast)
* @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
*/
const contrastRatio = (a, b) => {
let c;
a = relativeLuminance(a);
b = relativeLuminance(b);
//Arrange so a is lightest
if (a < b) {
c = a;
a = b;
b = c;
}
return (a + 0.05) / (b + 0.05);
};
const shadeColor = (rgb, amt) => {
let r = rgb.r + amt;
if (r > 255) r = 255;
else if (r < 0) r = 0;
let b = rgb.b + amt;
if (b > 255) b = 255;
else if (b < 0) b = 0;
let g = rgb.g + amt;
if (g > 255) g = 255;
else if (g < 0) g = 0;
return {
r: r,
g: g,
b: b,
};
};
const addShadeIfNoContrast = (rgb) => {
let base = {
r: 255,
g: 255,
b: 255,
}; // white
let cr = contrastRatio(base, rgb);
if (cr > 4.5) {
return rgb;
}
return addShadeIfNoContrast(shadeColor(rgb, -25));
};
const getColor = (username) => {
let rgb = stringToPastelColour(username);
rgb = addShadeIfNoContrast(rgb);
return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
};
export default getColor;

View File

@ -1,6 +1,8 @@
import React, { Component, PropTypes } from 'react';
import Icon from '/imports/ui/components/icon/component';
import styles from './styles.scss';
import cx from 'classnames';
import getColor from './color-generator';
const propTypes = {
user: React.PropTypes.shape({
@ -17,21 +19,73 @@ const defaultProps = {
export default class UserAvatar extends Component {
render() {
let user = this.props.user;
const {
user
} = this.props;
let avatarClasses = {};
avatarClasses[styles.presenter] = user.isPresenter;
avatarClasses[styles.voiceUser] = user.isVoiceUser;
avatarClasses[styles.moderator] = user.isModerator;
let avatarStyles = {
'backgroundColor': getColor(user.name),
};
return (
<div className={cx(avatarClasses, styles.userAvatar)}>
<div className={styles.userAvatar} style={avatarStyles}>
<span>
{user.name.slice(0, 2)}
{this.renderAvatarContent()}
</span>
{this.renderUserStatus()}
{this.renderUserMediaStatus()}
</div>
);
}
renderAvatarContent() {
const user = this.props.user;
let content = user.name.slice(0, 2);
if (user.emoji.status !== "none") {
content = <Icon iconName={user.emoji.status}/>
}
return content;
}
renderUserStatus() {
const user = this.props.user;
let userStatus;
let userStatusClasses = {};
userStatusClasses[styles.moderator] = user.isModerator;
userStatusClasses[styles.presenter] = user.isPresenter;
if (user.isModerator || user.isPresenter) {
userStatus = (
<span className={cx(styles.userStatus, userStatusClasses)}>
</span>
);
}
return userStatus;
}
renderUserMediaStatus() {
const user = this.props.user;
let userMediaStatus;
let userMediaClasses = {};
userMediaClasses[styles.voiceOnly] = user.isListenOnly;
userMediaClasses[styles.microphone] = user.isVoiceUser;
if (user.isListenOnly || user.isVoiceUser) {
userMediaStatus = (
<span className={cx(styles.userMediaStatus, userMediaClasses)}>
{user.isMuted ? <div className={styles.microphoneMuted}/> : null}
</span>
);
}
return userMediaStatus;
}
}
UserAvatar.propTypes = propTypes;

View File

@ -13,34 +13,77 @@ $moderator-text: $color-white;
$moderator-bg: $color-primary;
.userAvatar {
@extend .flex-column;
transition: all 0.3s;
flex-basis: 1.7rem;
// @extend .flex-column;
flex-basis: 2.2rem;
height: 2.2rem;
flex-shrink: 0;
height: 1.7rem;
line-height: 2.2rem;
justify-content: center;
position: relative;
display: flex;
flex-flow: column;
font-size: 1rem;
font-size: 1.1rem;
text-align: center;
border-radius: 50%;
border: 2px solid $user-avatar-border;
color: $user-avatar-text;
color: $color-white;
text-transform: capitalize;
}
.voiceUser {
background-color: $voice-user-bg;
color: $voice-user-text;
border: none;
}
.presenter {
.userStatus {
position: absolute;
background-color: $color-white;
width: 0.625rem;
height: 0.625rem;
border-radius: 2px;
top: 0;
left: 0;
-webkit-box-shadow: 0 0 0 1px $color-white;
-moz-box-shadow: 0 0 0 1px $color-white;
box-shadow: 0 0 0 1px $color-white;
transition: all 0.3s;
}
.moderator {
border: 1px solid $color-gray-light;
background-color: $color-white;
}
.presenter {
background-color: $moderator-bg;
color: $moderator-text;
border: none;
}
.userMediaStatus {
display: flex;
justify-content: center;
position: absolute;
border-radius: 50%;
width: 0.625rem;
height: 0.625rem;
top: 1.575rem;
left: 1.575rem;
-webkit-box-shadow: 0 0 0 1px $color-white;
-moz-box-shadow: 0 0 0 1px $color-white;
box-shadow: 0 0 0 1px $color-white;
transition: all 0.3s;
background-color: $color-success;
}
.voiceOnly {
border: 1px solid $color-gray-light;
background-color: $color-white;
}
.microphone {
}
.microphoneMuted {
margin-top: 0.05rem;
width: 2px;
transform: rotate(45deg);
height: 0.5rem;
background-color: $color-white;
}
// 10px x
// 16px 1

View File

@ -32,8 +32,8 @@ class ChatListItem extends Component {
linkClasses[styles.active] = chat.id === openChat;
return (
<li {...this.props}>
<Link to={linkPath} className={cx(styles.chatListItem, linkClasses)}>
<li className={cx(styles.chatListItem, linkClasses)} {...this.props}>
<Link to={linkPath} className={styles.chatListItemLink}>
{chat.icon ? this.renderChatIcon() : this.renderChatAvatar()}
<div className={styles.chatName}>
<h3 className={styles.chatNameMain}>{chat.name}</h3>

View File

@ -2,9 +2,17 @@
.chatListItem {
@extend %list-item;
padding-right: 1.2rem;
padding: 0;
cursor: pointer;
}
.chatListItemLink {
display: flex;
flex-grow: 1;
text-decoration: none;
padding: 0.3rem;
padding-left: 0.5rem;
padding-right: 0.3rem;
}
.unreadMessages {

View File

@ -202,12 +202,9 @@ const getOpenChats = chatID => {
getCurrentUser = () => {
let currentUserId = Auth.userID;
let currentUser = Users.findOne({ 'user.userid': currentUserId });
return mapUser(
Users
.findOne({ 'user.userid': currentUserId })
.user
);
return (currentUser) ? mapUser(currentUser.user) : null;
};
const userActions = {

View File

@ -83,8 +83,10 @@ class UserListItem extends Component {
handleClickOutsideDropdown(e) {
const node = findDOMNode(this);
if (e.target !== node && !node.contains(e.target)) {
const shouldUpdateState = e.target !== node &&
!node.contains(e.target) &&
this.state.visibleActions;
if (shouldUpdateState) {
this.setState({ visibleActions: false });
}
}
@ -129,23 +131,14 @@ class UserListItem extends Component {
userNameSub = userNameSub.join(' ');
return (
<ReactCSSTransitionGroup
className={styles.userName}
transitionName={userNameSubTransition}
transitionAppear={true}
transitionEnter={true}
transitionLeave={true}
transitionAppearTimeout={0}
transitionEnterTimeout={0}
transitionLeaveTimeout={0}
component='div'>
<div className={styles.userName}>
<h3 className={styles.userNameMain}>
{user.name}
</h3>
<p className={styles.userNameSub}>
{userNameSub}
</p>
</ReactCSSTransitionGroup>
</div>
);
}

View File

@ -9,13 +9,23 @@ export default class Slide extends React.Component {
return (
<g>
{this.props.currentSlide ?
<image x="0" y="0"
width={this.props.currentSlide.slide.width}
height={this.props.currentSlide.slide.height}
xlinkHref={this.props.currentSlide.slide.img_uri}
strokeWidth="0.8"
>
</image>
<g>
<rect
x="0"
y="0"
width={this.props.currentSlide.slide.width}
height={this.props.currentSlide.slide.height}
fill="white"
>
</rect>
<image x="0" y="0"
width={this.props.currentSlide.slide.width}
height={this.props.currentSlide.slide.height}
xlinkHref={this.props.currentSlide.slide.img_uri}
strokeWidth="0.8"
>
</image>
</g>
: null }
</g>
);