Skip to content

Commit

Permalink
Merge pull request #1045 from cboard-org/feature/live-mode
Browse files Browse the repository at this point in the history
Live mode Feature - support for users who can write to quickly talk using the output bar
  • Loading branch information
martinbedouret authored Oct 12, 2021
2 parents fbbc196 + 696c673 commit aa74edc
Show file tree
Hide file tree
Showing 19 changed files with 306 additions and 49 deletions.
3 changes: 2 additions & 1 deletion src/components/App/App.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ const initialState = {
caBackButtonActive: false,
quickUnlockActive: false,
removeOutputActive: false,
vocalizeFolders: false
vocalizeFolders: false,
liveMode: false
},
userData: {}
};
Expand Down
2 changes: 2 additions & 0 deletions src/components/App/__tests__/App.reducer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe('reducer', () => {
navigationSettings: {
active: false,
caBackButtonActive: false,
liveMode: false,
shareShowActive: false,
quickUnlockActive: false,
removeOutputActive: false,
Expand All @@ -58,6 +59,7 @@ describe('reducer', () => {
navigationSettings: {
active: false,
caBackButtonActive: false,
liveMode: false,
shareShowActive: false,
quickUnlockActive: false,
removeOutputActive: false,
Expand Down
7 changes: 7 additions & 0 deletions src/components/Board/Board.actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
CLICK_SYMBOL,
CLICK_OUTPUT,
CHANGE_OUTPUT,
CHANGE_LIVE_MODE,
REPLACE_BOARD,
HISTORY_REMOVE_BOARD,
UNMARK_BOARD,
Expand Down Expand Up @@ -186,6 +187,12 @@ export function changeOutput(output) {
};
}

export function changeLiveMode() {
return {
type: CHANGE_LIVE_MODE
};
}

export function getApiMyBoardsSuccess(boards) {
return {
type: GET_API_MY_BOARDS_SUCCESS,
Expand Down
1 change: 1 addition & 0 deletions src/components/Board/Board.constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const FOCUS_TILE = 'cboard/Board/FOCUS_TILE';
export const CLICK_SYMBOL = 'cboard/Board/CLICK_SYMBOL';
export const CLICK_OUTPUT = 'cboard/Board/CLICK_OUTPUT';
export const CHANGE_OUTPUT = 'cboard/Board/CHANGE_OUTPUT';
export const CHANGE_LIVE_MODE = 'cboard/Board/CHANGE_LIVE_MODE';
export const HISTORY_REMOVE_BOARD = 'cboard/Board/HISTORY_REMOVE_BOARD';
export const UNMARK_BOARD = 'cboard/Board/UNMARK_BOARD';
export const CREATE_API_BOARD_SUCCESS = 'cboard/Board/CREATE_API_BOARD_SUCCESS';
Expand Down
21 changes: 18 additions & 3 deletions src/components/Board/Board.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ export class BoardContainer extends Component {
downloadImages: PropTypes.func,
lang: PropTypes.string,
isRootBoardTourEnabled: PropTypes.bool,
disableTour: PropTypes.func
disableTour: PropTypes.func,
isLiveMode: PropTypes.bool
};

state = {
Expand Down Expand Up @@ -815,7 +816,8 @@ export class BoardContainer extends Component {
intl,
boards,
showNotification,
navigationSettings
navigationSettings,
isLiveMode
} = this.props;
const hasAction = tile.action && tile.action.startsWith('+');

Expand Down Expand Up @@ -846,9 +848,21 @@ export class BoardContainer extends Component {
showNotification(intl.formatMessage(messages.boardMissed));
}
} else {
changeOutput([...this.props.output, tile]);
clickSymbol(tile.label);
say();
if (isLiveMode) {
const liveTile = {
backgroundColor: 'rgb(255, 241, 118)',
id: shortid.generate(),
image: '',
label: '',
labelKey: '',
type: 'live'
};
changeOutput([...this.props.output, tile, liveTile]);
} else {
changeOutput([...this.props.output, tile]);
}
}
};

Expand Down Expand Up @@ -1493,6 +1507,7 @@ const mapStateToProps = ({
board: board.boards.find(board => board.id === activeBoardId),
boards: board.boards,
output: board.output,
isLiveMode: board.isLiveMode,
scannerSettings: scanner,
navHistory: board.navHistory,
displaySettings,
Expand Down
4 changes: 4 additions & 0 deletions src/components/Board/Board.messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,9 @@ export default defineMessages({
walkthroughNext: {
id: 'cboard.components.Board.walkthroughNext',
defaultMessage: 'Next'
},
live: {
id: 'cboard.components.Board.live',
defaultMessage: 'LIVE'
}
});
9 changes: 8 additions & 1 deletion src/components/Board/Board.reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
REPLACE_BOARD,
HISTORY_REMOVE_BOARD,
UNMARK_BOARD,
CHANGE_LIVE_MODE,
CREATE_API_BOARD_SUCCESS,
CREATE_API_BOARD_FAILURE,
CREATE_API_BOARD_STARTED,
Expand All @@ -46,7 +47,8 @@ const initialState = {
navHistory: [],
isFetching: false,
images: [],
isFixed: false
isFixed: false,
isLiveMode: false
};

function reconcileBoards(localBoard, remoteBoard) {
Expand Down Expand Up @@ -286,6 +288,11 @@ function boardReducer(state = initialState, action) {
...state,
output: [...action.output]
};
case CHANGE_LIVE_MODE:
return {
...state,
isLiveMode: !state.isLiveMode
};
case CREATE_API_BOARD_SUCCESS:
const creadBoards = [...state.boards];
for (let i = 0; i < creadBoards.length; i++) {
Expand Down
95 changes: 73 additions & 22 deletions src/components/Board/Output/Output.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { injectIntl, intlShape } from 'react-intl';
import { connect } from 'react-redux';
import keycode from 'keycode';
import shortid from 'shortid';
import messages from '../Board.messages';
import { showNotification } from '../../Notifications/Notifications.actions';
import { isAndroid } from '../../../cordova-util';
Expand All @@ -12,7 +13,7 @@ import {
speak
} from '../../../providers/SpeechProvider/SpeechProvider.actions';

import { changeOutput, clickOutput } from '../Board.actions';
import { changeOutput, clickOutput, changeLiveMode } from '../Board.actions';
import SymbolOutput from './SymbolOutput';

function translateOutput(output, intl) {
Expand Down Expand Up @@ -149,25 +150,29 @@ export class OutputContainer extends Component {
}
}

async play() {
const outputFrames = this.groupOutputByType();

await this.asyncForEach(outputFrames, async frame => {
if (!frame[0].sound) {
const text = frame.reduce(this.outputReducer, '');
await this.speakOutput(text);
} else {
await new Promise(resolve => {
this.asyncForEach(frame, async ({ sound }, index) => {
await this.playAudio(sound);

if (frame.length - 1 === index) {
resolve();
}
async play(liveText = '') {
if (liveText) {
await this.speakOutput(liveText);
} else {
const outputFrames = this.groupOutputByType();

await this.asyncForEach(outputFrames, async frame => {
if (!frame[0].sound) {
const text = frame.reduce(this.outputReducer, '');
await this.speakOutput(text);
} else {
await new Promise(resolve => {
this.asyncForEach(frame, async ({ sound }, index) => {
await this.playAudio(sound);

if (frame.length - 1 === index) {
resolve();
}
});
});
});
}
});
}
});
}
}

handleBackspaceClick = () => {
Expand Down Expand Up @@ -221,12 +226,53 @@ export class OutputContainer extends Component {

handleOutputKeyDown = event => {
if (event.keyCode === keycode('enter')) {
this.play();
const targetEl = event.target;
if (targetEl.tagName.toLowerCase() === 'div') {
this.play();
} else if (targetEl.tagName.toLowerCase() === 'textarea') {
this.play(event.target.value);
this.addLiveOutputTile();
}
}
};

addLiveOutputTile() {
const { changeOutput } = this.props;
const tile = {
backgroundColor: 'rgb(255, 241, 118)',
id: shortid.generate(),
image: '',
label: '',
labelKey: '',
type: 'live'
};
changeOutput([...this.state.translatedOutput, tile]);
}

handleSwitchLiveMode = event => {
const { changeLiveMode, isLiveMode } = this.props;

if (!isLiveMode) {
this.addLiveOutputTile();
}
changeLiveMode();
};

handleWriteSymbol = index => event => {
const { changeOutput, intl } = this.props;
const output = [...this.props.output];
const newEl = {
...output[index],
label: event.target.value
};
output.splice(index, 1, newEl);
changeOutput(output);
const translated = translateOutput(output, intl);
this.setState({ translatedOutput: translated });
};

render() {
const { output, navigationSettings } = this.props;
const { output, navigationSettings, isLiveMode } = this.props;

const tabIndex = output.length ? '0' : '-1';

Expand All @@ -238,10 +284,13 @@ export class OutputContainer extends Component {
onRemoveClick={this.handleRemoveClick}
onClick={this.handleOutputClick}
onKeyDown={this.handleOutputKeyDown}
onSwitchLiveMode={this.handleSwitchLiveMode}
symbols={this.state.translatedOutput}
isLiveMode={isLiveMode}
tabIndex={tabIndex}
navigationSettings={navigationSettings}
phrase={this.handlePhraseToShare()}
onWriteSymbol={this.handleWriteSymbol}
/>
);
}
Expand All @@ -250,6 +299,7 @@ export class OutputContainer extends Component {
const mapStateToProps = ({ board, app }) => {
return {
output: board.output,
isLiveMode: board.isLiveMode,
navigationSettings: app.navigationSettings
};
};
Expand All @@ -259,7 +309,8 @@ const mapDispatchToProps = {
changeOutput,
clickOutput,
speak,
showNotification
showNotification,
changeLiveMode
};

export default connect(
Expand Down
9 changes: 9 additions & 0 deletions src/components/Board/Output/SymbolOutput/SymbolOutput.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,12 @@
top: -10px;
right: -10px;
}

.SymbolOutput__right__btns {
display: flex;
flex-direction: column;
flex-wrap: wrap-reverse;
height: 100%;
overflow: visible;
justify-content: center;
}
38 changes: 31 additions & 7 deletions src/components/Board/Output/SymbolOutput/SymbolOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import PropTypes from 'prop-types';

import ClearIcon from '@material-ui/icons/Clear';
import IconButton from '@material-ui/core/IconButton';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';

import Symbol from '../../Symbol';
import BackspaceButton from './BackspaceButton';
Expand Down Expand Up @@ -60,9 +62,12 @@ class SymbolOutput extends PureComponent {
getPhraseToShare,
onCopyClick,
onRemoveClick,
onSwitchLiveMode,
onWriteSymbol,
symbols,
navigationSettings,
phrase,
isLiveMode,
...other
} = this.props;

Expand All @@ -85,13 +90,15 @@ class SymbolOutput extends PureComponent {
return (
<div className="SymbolOutput">
<Scroll {...other}>
{symbols.map(({ image, label }, index) => (
{symbols.map(({ image, label, type }, index) => (
<div className="SymbolOutput__value" key={index}>
<Symbol
className="SymbolOutput__symbol"
image={image}
label={label}
type={type}
labelpos="Below"
onWrite={onWriteSymbol(index)}
/>
<div className="SymbolOutput__value__IconButton">
<IconButton
Expand Down Expand Up @@ -121,12 +128,6 @@ class SymbolOutput extends PureComponent {
hidden={!symbols.length}
/>
)}
<ClearButton
color="inherit"
onClick={onClearClick}
style={clearButtonStyle}
hidden={!symbols.length}
/>
{!navigationSettings.removeOutputActive && (
<BackspaceButton
color="inherit"
Expand All @@ -135,6 +136,29 @@ class SymbolOutput extends PureComponent {
hidden={navigationSettings.removeOutputActive}
/>
)}
<div className="SymbolOutput__right__btns">
{navigationSettings.liveMode && (
<FormControlLabel
value="bottom"
control={
<Switch
size="small"
checked={isLiveMode}
color="primary"
onChange={onSwitchLiveMode}
/>
}
label={intl.formatMessage(messages.live)}
labelPlacement="bottom"
/>
)}
<ClearButton
color="inherit"
onClick={onClearClick}
style={clearButtonStyle}
hidden={!symbols.length}
/>
</div>
</div>
);
}
Expand Down
Loading

0 comments on commit aa74edc

Please sign in to comment.