diff --git a/bigbluebutton-html5/client/stylesheets/bbb-icons.css b/bigbluebutton-html5/client/stylesheets/bbb-icons.css index d990c5ecf71d7eb8eda424bf1af063f2a52aa8c1..0cbb5e007996546a57cbb8b5343f0f41c1fb798f 100755 --- a/bigbluebutton-html5/client/stylesheets/bbb-icons.css +++ b/bigbluebutton-html5/client/stylesheets/bbb-icons.css @@ -53,10 +53,10 @@ .icon-bbb-up-arrow:before { content: "\e907"; } -.icon-bbb-undecided:before { +.icon-bbb-neutral:before { content: "\e908"; } -.icon-bbb-time:before { +.icon-bbb-away:before { content: "\e909"; } .icon-bbb-sad:before { @@ -77,7 +77,7 @@ .icon-bbb-happy:before { content: "\e90f"; } -.icon-bbb-hand:before { +.icon-bbb-raiseHand:before { content: "\e910"; } .icon-bbb-group-chat:before { @@ -89,7 +89,7 @@ .icon-bbb-close:before { content: "\e913"; } -.icon-bbb-clear-status:before { +.icon-bbb-none:before { content: "\e914"; } .icon-bbb-circle:before { diff --git a/bigbluebutton-html5/imports/locales/en.json b/bigbluebutton-html5/imports/locales/en.json index be78a5d8bc2a35c25ff8acce48836e1555bc978d..1d1ca682474e6a82a37e166d0a79d6777bb49ebd 100755 --- a/bigbluebutton-html5/imports/locales/en.json +++ b/bigbluebutton-html5/imports/locales/en.json @@ -45,5 +45,20 @@ "app.actionsBar.actionsDropdown.desktopShareLabel": "Share your screen", "app.actionsBar.actionsDropdown.presentationDesc": "Upload your presentation", "app.actionsBar.actionsDropdown.initPollDesc": "Initiate a poll", - "app.actionsBar.actionsDropdown.desktopShareDesc": "Share your screen with others" + "app.actionsBar.actionsDropdown.desktopShareDesc": "Share your screen with others", + "app.actionsBar.emojiMenu.statusTriggerLabel": "Status", + "app.actionsBar.emojiMenu.awayLabel": "Away", + "app.actionsBar.emojiMenu.awayDesc": "Change your status to away", + "app.actionsBar.emojiMenu.raiseLabel": "Raise", + "app.actionsBar.emojiMenu.raiseDesc": "Raise your hand to ask a question", + "app.actionsBar.emojiMenu.undecidedLabel": "Undecided", + "app.actionsBar.emojiMenu.undecidedDesc": "Change your status to undecided", + "app.actionsBar.emojiMenu.confusedLabel": "Confused", + "app.actionsBar.emojiMenu.confusedDesc": "Change your status to confused", + "app.actionsBar.emojiMenu.sadLabel": "Sad", + "app.actionsBar.emojiMenu.sadDesc": "Change your status to sad", + "app.actionsBar.emojiMenu.happyLabel": "Happy", + "app.actionsBar.emojiMenu.happyDesc": "Change your status to happy", + "app.actionsBar.emojiMenu.clearLabel": "Clear", + "app.actionsBar.emojiMenu.clearDesc": "Clear your status" } diff --git a/bigbluebutton-html5/imports/startup/server/logger.js b/bigbluebutton-html5/imports/startup/server/logger.js index 45ffc85109a2706af0e4e76680854dead5f0dc53..b5d786cebf6d3268347d7a404e2b96602c2693c6 100755 --- a/bigbluebutton-html5/imports/startup/server/logger.js +++ b/bigbluebutton-html5/imports/startup/server/logger.js @@ -5,9 +5,9 @@ let Logger = new Winston.Logger(); // Write logs to console Logger.add(Winston.transports.Console, { - prettyPrint: true, - humanReadableUnhandledException: true, - colorize: true, + prettyPrint: true, + humanReadableUnhandledException: true, + colorize: true, }); Meteor.startup(() => { @@ -22,8 +22,8 @@ Meteor.startup(() => { } Logger.add(Winston.transports.File, { - filename: filename, - prettyPrint: true, + filename: filename, + prettyPrint: true, }); } }); diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx index 9240c2ddd7faf90c6378eb016ddd5fb1c5cf40ae..5ca57ce65b0e9ac481efa1ee68f117cb95477f86 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/component.jsx @@ -2,6 +2,7 @@ import React, { Component, PropTypes } from 'react'; import styles from './styles.scss'; import Button from '../button/component'; +import EmojiContainer from './emoji-menu/container'; import ActionsDropdown from './actions-dropdown/component'; @@ -36,14 +37,7 @@ export default class ActionsBar extends Component { size={'lg'} circle={true} /> - <Button - onClick={this.handleClick} - label={'Raise'} - color={'primary'} - icon={'hand'} - size={'lg'} - circle={true} - /> + <EmojiContainer /> </div> <div className={styles.right}> </div> diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/component.jsx new file mode 100755 index 0000000000000000000000000000000000000000..9a3636e0292ae3a5fb5ba6380dcafc8bffe3367a --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/component.jsx @@ -0,0 +1,179 @@ +import React, { Component, PropTypes } from 'react'; +import { defineMessages, injectIntl } from 'react-intl'; + +import Button from '/imports/ui/components/button/component'; +import Dropdown from '/imports/ui/components/dropdown/component'; +import DropdownTrigger from '/imports/ui/components/dropdown/trigger/component'; +import DropdownContent from '/imports/ui/components/dropdown/content/component'; +import DropdownList from '/imports/ui/components/dropdown/list/component'; +import DropdownListItem from '/imports/ui/components/dropdown/list/item/component'; +import DropdownListSeparator from '/imports/ui/components/dropdown/list/separator/component'; + +const propTypes = { + // Emoji status of the current user + userEmojiStatus: PropTypes.string.isRequired, + actions: PropTypes.object.isRequired, +}; + +class EmojiMenu extends Component { + constructor(props) { + super(props); + } + + render() { + const { + userEmojiStatus, + actions, + intl, + } = this.props; + + return ( + <Dropdown ref="dropdown"> + <DropdownTrigger> + <Button + role="button" + label={intl.formatMessage(intlMessages.statusTriggerLabel)} + icon="raiseHand" + ghost={false} + circle={true} + hideLabel={false} + color="primary" + size="lg" + + // FIXME: Without onClick react proptypes keep warning + // even after the DropdownTrigger inject an onClick handler + onClick={() => null} + /> + </DropdownTrigger> + <DropdownContent placement="top left"> + <DropdownList> + <DropdownListItem + icon="away" + label={intl.formatMessage(intlMessages.awayLabel)} + description={intl.formatMessage(intlMessages.awayDesc)} + onClick={() => actions.setEmojiHandler('away')} + /> + <DropdownListItem + icon="raiseHand" + label={intl.formatMessage(intlMessages.raiseLabel)} + description={intl.formatMessage(intlMessages.raiseDesc)} + onClick={() => actions.setEmojiHandler('raiseHand')} + /> + <DropdownListItem + icon="neutral" + label={intl.formatMessage(intlMessages.undecidedLabel)} + description={intl.formatMessage(intlMessages.undecidedDesc)} + onClick={() => actions.setEmojiHandler('neutral')} + + /> + <DropdownListItem + icon="confused" + label={intl.formatMessage(intlMessages.confusedLabel)} + description={intl.formatMessage(intlMessages.confusedDesc)} + onClick={() => actions.setEmojiHandler('confused')} + /> + <DropdownListItem + icon="sad" + label={intl.formatMessage(intlMessages.sadLabel)} + description={intl.formatMessage(intlMessages.sadDesc)} + onClick={() => actions.setEmojiHandler('sad')} + /> + <DropdownListItem + icon="happy" + label={intl.formatMessage(intlMessages.happyLabel)} + description={intl.formatMessage(intlMessages.happyDesc)} + onClick={() => actions.setEmojiHandler('happy')} + /> + <DropdownListSeparator/> + <DropdownListItem + icon="none" + label={intl.formatMessage(intlMessages.clearLabel)} + description={intl.formatMessage(intlMessages.clearDesc)} + onClick={() => actions.setEmojiHandler('none')} + /> + </DropdownList> + </DropdownContent> + </Dropdown> + ); + } +} + +const intlMessages = defineMessages({ + statusTriggerLabel: { + id: 'app.actionsBar.emojiMenu.statusTriggerLabel', + defaultMessage: 'Status', + }, + + // For away status + awayLabel: { + id: 'app.actionsBar.emojiMenu.awayLabel', + defaultMessage: 'Away', + }, + awayDesc: { + id: 'app.actionsBar.emojiMenu.awayDesc', + defaultMessage: 'Change your status to away', + }, + + // For raise hand status + raiseLabel: { + id: 'app.actionsBar.emojiMenu.raiseLabel', + defaultMessage: 'Raise', + }, + raiseDesc: { + id: 'app.actionsBar.emojiMenu.raiseDesc', + defaultMessage: 'Raise your hand to ask a question', + }, + + // For undecided status + undecidedLabel: { + id: 'app.actionsBar.emojiMenu.undecidedLabel', + defaultMessage: 'Undecided', + }, + undecidedDesc: { + id: 'app.actionsBar.emojiMenu.undecidedDesc', + defaultMessage: 'Change your status to undecided', + }, + + // For confused status + confusedLabel: { + id: 'app.actionsBar.emojiMenu.confusedLabel', + defaultMessage: 'Confused', + }, + confusedDesc: { + id: 'app.actionsBar.emojiMenu.confusedDesc', + defaultMessage: 'Change your status to confused', + }, + + // For sad status + sadLabel: { + id: 'app.actionsBar.emojiMenu.sadLabel', + defaultMessage: 'Sad', + }, + sadDesc: { + id: 'app.actionsBar.emojiMenu.sadDesc', + defaultMessage: 'Change your status to sad', + }, + + // For happy status + happyLabel: { + id: 'app.actionsBar.emojiMenu.happyLabel', + defaultMessage: 'Happy', + }, + happyDesc: { + id: 'app.actionsBar.emojiMenu.happyDesc', + defaultMessage: 'Change your status to happy', + }, + + // For confused status + clearLabel: { + id: 'app.actionsBar.emojiMenu.clearLabel', + defaultMessage: 'Clear', + }, + clearDesc: { + id: 'app.actionsBar.emojiMenu.clearDesc', + defaultMessage: 'Clear your status', + }, +}); + +EmojiMenu.propTypes = propTypes; +export default injectIntl(EmojiMenu); diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/container.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/container.jsx new file mode 100755 index 0000000000000000000000000000000000000000..fe09bf78973cc0382ca9da58934b515287db30f1 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/container.jsx @@ -0,0 +1,50 @@ +import React, { Component, PropTypes } from 'react'; +import { createContainer } from 'meteor/react-meteor-data'; + +import EmojiService from './service'; +import EmojiMenu from './component.jsx'; + +const propTypes = { + // Emoji status of the current user + userEmojiStatus: PropTypes.string.isRequired, + actions: PropTypes.object.isRequired, +}; + +class EmojiContainer extends React.Component { + constructor(props) { + super(props); + } + + render() { + const { + userEmojiStatus, + actions, + } = this.props; + + return ( + <EmojiMenu userEmojiStatus={userEmojiStatus} actions={actions}/> + ); + } +} + +export default createContainer(() => { + const data = EmojiService.getEmojiData(); + + const { + userEmojiStatus, + credentials, + } = data; + + const { requesterUserId: userId } = credentials; + + return { + userEmojiStatus, + actions: { + setEmojiHandler: (status) => { + EmojiService.setEmoji(userId, status); + }, + }, + }; +}, EmojiContainer); + +EmojiContainer.propTypes = propTypes; diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/service.js b/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/service.js new file mode 100755 index 0000000000000000000000000000000000000000..4be72b17bdeec09cb6d7b0d01770995e792a423e --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/emoji-menu/service.js @@ -0,0 +1,31 @@ +import Auth from '/imports/ui/services/auth/index.js'; +import Users from '/imports/api/users'; +import { callServer } from '/imports/ui/services/api/index.js'; + +let getEmojiData = () => { + + // Get userId and meetingId + const credentials = Auth.getCredentials(); + const { requesterUserId: userId, meetingId } = credentials; + + // Find the Emoji Status of this specific meeting and userid + const userEmojiStatus = Users.findOne({ + meetingId: meetingId, + userId: userId, + }).user.emoji_status; + + return { + userEmojiStatus: userEmojiStatus, + credentials: credentials, + }; +}; + +// Below doesn't even need to receieve credentials +const setEmoji = (toRaiseUserId, status) => { + callServer('userSetEmoji', toRaiseUserId, status); +}; + +export default { + getEmojiData, + setEmoji, +}; diff --git a/bigbluebutton-html5/imports/ui/components/user-list/service.js b/bigbluebutton-html5/imports/ui/components/user-list/service.js index 46e031889b645db8c51b7f8999920303259c8a8f..1854486602031e38ffce98c71ed0857b21d332d7 100755 --- a/bigbluebutton-html5/imports/ui/components/user-list/service.js +++ b/bigbluebutton-html5/imports/ui/components/user-list/service.js @@ -2,13 +2,13 @@ import Users from '/imports/api/users'; import Chat from '/imports/api/chat'; import Auth from '/imports/ui/services/auth'; import UnreadMessages from '/imports/ui/services/unread-messages'; +import { EMOJI_STATUSES } from '/imports/utils/statuses.js'; import { callServer } from '/imports/ui/services/api'; const CHAT_CONFIG = Meteor.settings.public.chat; const USER_CONFIG = Meteor.settings.public.user; const ROLE_MODERATOR = USER_CONFIG.role_moderator; -const EMOJI_STATUSES = ['raiseHand', 'happy', 'smile', 'neutral', 'sad', 'confused', 'away']; const PRIVATE_CHAT_TYPE = CHAT_CONFIG.type_private; /* TODO: Same map is done in the chat/service we should share this someway */ @@ -72,7 +72,7 @@ const sortUsersByEmoji = (a, b) => { const sortUsersByModerator = (a, b) => { if (a.isModerator && b.isModerator) { - return sortUsersByName(a, b); + return sortUsersByEmoji(a, b); } else if (a.isModerator) { return -1; } else if (b.isModerator) { @@ -95,10 +95,10 @@ const sortUsersByPhoneUser = (a, b) => { }; const sortUsers = (a, b) => { - let sort = sortUsersByEmoji(a, b); + let sort = sortUsersByModerator(a, b); if (sort === 0) { - sort = sortUsersByModerator(a, b); + sort = sortUsersByEmoji(a, b); } if (sort === 0) { diff --git a/bigbluebutton-html5/imports/utils/statuses.js b/bigbluebutton-html5/imports/utils/statuses.js new file mode 100755 index 0000000000000000000000000000000000000000..15d060cbf7c523decdc99bcc5f541f349e88c9c0 --- /dev/null +++ b/bigbluebutton-html5/imports/utils/statuses.js @@ -0,0 +1,3 @@ +const EMOJI_STATUSES = ['away', 'raiseHand', 'neutral', 'confused', 'sad', 'happy']; + +export { EMOJI_STATUSES }; diff --git a/bigbluebutton-html5/lib/environment.js b/bigbluebutton-html5/lib/environment.js deleted file mode 100755 index 7f25318593e3bc83178ba09375d158159272b6db..0000000000000000000000000000000000000000 --- a/bigbluebutton-html5/lib/environment.js +++ /dev/null @@ -1,9 +0,0 @@ -// used in Flash and HTML to show a legitimate break in the line -this.BREAK_LINE = '<br/>'; - -// soft return in HTML to signify a broken line without -//displaying the escaped '<br/>' line break text -this.CARRIAGE_RETURN = '\r'; - -// handle this the same as carriage return, in case text copied has this -this.NEW_LINE = '\n'; diff --git a/bigbluebutton-html5/package.json b/bigbluebutton-html5/package.json index 7b10382b9d9bb637c529d9472a9751a6b9b9e70d..d93857ab195fb6460fd8ccc755267a41293d2026 100755 --- a/bigbluebutton-html5/package.json +++ b/bigbluebutton-html5/package.json @@ -11,10 +11,11 @@ "dependencies": { "classnames": "^2.2.3", "grunt-cli": "~1.2.0", + "hiredis": "^0.5.0", "history": "^2.1.2", "image-size": "~0.5.0", "meteor-node-stubs": "^0.2.3", - "node-sass": "^3.8.0", + "node-sass": "~3.8.0", "react": "~15.3.1", "react-addons-css-transition-group": "~15.3.1", "react-addons-pure-render-mixin": "~15.3.1",