Skip to content

Commit

Permalink
feat: zoom closed caption API integration (#640)
Browse files Browse the repository at this point in the history
* refactor(frontend/settings): create tools category
* feat(frontend/settings): add livestream settings option
* build(backend): add node-fetch
* feat(backend): util to post line data to zoom closed captions
* feat(backend) post line data received to zoom for closed captions
* feat(frontend/settings): create text field component
* feat(frontend/settings): use DynamicOptions
* feat(backend): get zoom api key from frontend and push to api
* feat(frontend/settings): rename Live Stream to Closed Captioning
* fix(backend): set zoom api token in session when getting client settings
* refactor(frontend/settings): rename TextInput option type to textInput
* feat(frontend/settings): change about icon to info
* refactor: rename livestream setting to closedCaptioning
* fix(frontend/settings): render known option groups
* fix(app): add default closedCaptionining setting group
* fix(frontend/settings): remove livestream settings component and add default dynamic options for tools
* perf(frontend/settings): use defaultValue for TextInputs for better UX
* refactor(backend): handle zoom functionality in separate controller
* refactor(frontend): move line utilities and auto jump utilities into separate files
* refactor(backend): fetch zoom api token directly from settings
* refactor(frontend): move Shabad OS data constants into separate file
* feat(frontend/settings): add theme-consistent styling to TextInput component
* fix(frontend/settings): autosave text inputs on blur
* refactor(frontend): use getTranslations in useTranslations hook
* refactor: return recommendedSources instead of recommended from db
* feat: add support for Zoom closed captioning options
* feat(frontend/settings): add custom text to closed captioning settings
* fix(backend): add larivaar to zoom transliterations
* refactor: rename closedCaptioning to closedCaptions
* feat(frontend/settings): add edit status check mark to text fields
* fix(backend): replace spaces in zoom controller with regex
* feat(frontent/settings): select text on focus
* feat: hide saved checkmark after 3 seconds
* fix: save icon animations

Co-authored-by: Bhajneet S.K. <bhajneet@gmail.com>
Co-authored-by: Harjot Singh <harjot@harkul.com>
  • Loading branch information
3 people authored Jun 12, 2021
1 parent 924e9d0 commit d6ddfbc
Show file tree
Hide file tree
Showing 33 changed files with 793 additions and 394 deletions.
39 changes: 36 additions & 3 deletions app/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion app/frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class App extends PureComponent {
// Get recommended sources and set as settings, if there are none
fetch( `${BACKEND_URL}/sources` )
.then( res => res.json() )
.then( ( { recommended: recommendedSources } ) => {
.then( ( { recommendedSources } ) => {
//* Update default options and settings with fetched recommended sources
DEFAULT_OPTIONS.local.sources = recommendedSources
//! Re-load settings since we've modified DEFAULT_OPTIONS directly
Expand Down
3 changes: 2 additions & 1 deletion app/frontend/src/Controller/Navigator.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import {
} from '@fortawesome/free-solid-svg-icons'

import { SEARCH_URL } from '../lib/consts'
import { getJumpLines, getNextJumpLine, findLineIndex } from '../lib/utils'
import controller from '../lib/controller'
import { LINE_HOTKEYS } from '../lib/keyMap'
import { ContentContext, HistoryContext } from '../lib/contexts'
import { useCurrentLines } from '../lib/hooks'
import { getJumpLines, getNextJumpLine } from '../lib/auto-jump'
import { findLineIndex } from '../lib/line'

import GlobalHotKeys from '../shared/GlobalHotKeys'
import { withNavigationHotkeys } from '../shared/NavigationHotkeys'
Expand Down
4 changes: 2 additions & 2 deletions app/frontend/src/Controller/Search/Result.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import classNames from 'classnames'
import { ListItem } from '@material-ui/core'

import controller from '../../lib/controller'
import { getTranslation, customiseLine } from '../../lib/utils'
import { WritersContext, RecommendedSourcesContext, SettingsContext } from '../../lib/contexts'
import { LANGUAGE_NAMES, SOURCE_ABBREVIATIONS, TRANSLITERATORS } from '../../lib/consts'
import { LANGUAGE_NAMES, SOURCE_ABBREVIATIONS, TRANSLITERATORS } from '../../lib/data'
import { customiseLine, getTranslation } from '../../lib/line'

/**
* Renders a single result, highlighting the match.
Expand Down
4 changes: 2 additions & 2 deletions app/frontend/src/Overlay/Line.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import React from 'react'
import { string, bool, objectOf, func } from 'prop-types'
import classNames from 'classnames'

import { LANGUAGE_NAMES, TRANSLATION_ORDER, TRANSLITERATION_ORDER } from '../lib/consts'
import { partitionLine, classifyWords } from '../lib/utils'
import { LANGUAGE_NAMES, TRANSLATION_ORDER, TRANSLITERATION_ORDER } from '../lib/data'
import { partitionLine, classifyWords } from '../lib/line'

import './Line.css'

Expand Down
4 changes: 2 additions & 2 deletions app/frontend/src/Overlay/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import Line from './Line'
import ThemeLoader from './ThemeLoader'

import { SettingsContext, StatusContext } from '../lib/contexts'
import { LANGUAGES } from '../lib/consts'
import { LANGUAGES } from '../lib/data'
import { useTranslations, useCurrentLine } from '../lib/hooks'
import { customiseLine, getTransliterators } from '../lib/utils'
import { customiseLine, getTransliterators } from '../lib/line'

import './index.css'

Expand Down
4 changes: 2 additions & 2 deletions app/frontend/src/Presenter/Display.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { shape, bool } from 'prop-types'
import classNames from 'classnames'
import { mapValues } from 'lodash'

import { LANGUAGES } from '../lib/consts'
import { LANGUAGES } from '../lib/data'
import { useTranslations, useCurrentLine, useCurrentLines } from '../lib/hooks'
import { customiseLine, getTransliterators } from '../lib/utils'
import { customiseLine, getTransliterators } from '../lib/line'
import Line from './Line'

import './Display.css'
Expand Down
4 changes: 2 additions & 2 deletions app/frontend/src/Presenter/Line.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { CSSTransition, TransitionGroup } from 'react-transition-group'
import classNames from 'classnames'
import { countSyllables, toSyllabicSymbols } from 'gurmukhi-utils'

import { partitionLine, classifyWords } from '../lib/utils'
import { partitionLine, classifyWords } from '../lib/line'
import { DEFAULT_OPTIONS } from '../lib/options'
import { LANGUAGES, LANGUAGE_NAMES, TRANSLATION_ORDER, TRANSLITERATION_ORDER } from '../lib/consts'
import { LANGUAGES, LANGUAGE_NAMES, TRANSLATION_ORDER, TRANSLITERATION_ORDER } from '../lib/data'

import './Line.css'

Expand Down
12 changes: 12 additions & 0 deletions app/frontend/src/Settings/ClosedCaptionSettings.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.closed-caption-settings {
display: flex;
flex-direction: column;
}

.closed-caption-settings .buttons {
margin: 30px 0;
}

.closed-caption-settings + .buttons {
margin-top: 0px;
}
34 changes: 34 additions & 0 deletions app/frontend/src/Settings/ClosedCaptionSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'

import { Grid, Typography } from '@material-ui/core'

import TutorialButton from './TutorialButton'
import DynamicOptions, { slotSizes, OptionGrid } from './DynamicOptions'

import './ClosedCaptionSettings.css'

const ClosedCaptionSettings = () => (
<div className="closed-caption-settings">
<OptionGrid container>
<Grid item {...slotSizes.single}>
<Typography>
Closed captioning integrates the currently active line of Shabad OS into the built-in
subtitle features of 3rd party services, such as YouTube, Facebook, or Zoom. Currently
only Zoom meetings are supported.
</Typography>
</Grid>
</OptionGrid>

<OptionGrid container align="center">
<Grid item {...slotSizes.single} className="buttons">
<TutorialButton href="https://docs.shabados.com/presenter/guides/integrating-closed-captioning-in-zoom-meetings">
Learn More
</TutorialButton>
</Grid>
</OptionGrid>

<DynamicOptions device="global" group="closedCaptions" />
</div>
)

export default ClosedCaptionSettings
12 changes: 3 additions & 9 deletions app/frontend/src/Settings/OverlaySettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,9 @@ const OverlaySettings = () => {
<OptionGrid container>
<Grid item {...slotSizes.single}>
<Typography>
Overlays provide a convenient second screen for projectors,
live streaming broadcasts, and vision impaired monitors.
</Typography>
<br />
<Typography>
When a line is activated in the controller and seen on the presenter,
the same information is used to generate the overlay.
Choose what to display and how to display it with the options below.
For added customization, create an overlay with the theme tool.
Use overlays to set up captions for popular live stream software, such as OBS, Wirecast,
and vMix. Or use them in full screened web browsers as an alternate presentation to the
main display. It is also possible to create overlay themes using the theme tool.
</Typography>
</Grid>
</OptionGrid>
Expand Down
56 changes: 54 additions & 2 deletions app/frontend/src/Settings/SettingComponents.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
background-color: var(--settings-setting-shadow) !important;
}

.slider .MuiSlider-thumb:hover, .slider .MuiSlider-thumb.MuiSlider-active {
.slider .MuiSlider-thumb:hover,
.slider .MuiSlider-thumb.MuiSlider-active {
box-shadow: 0px 0px 0px 8px var(--settings-setting-shadow) !important;
}

Expand Down Expand Up @@ -70,4 +71,55 @@
.button:hover {
color: var(--settings-background-color) !important;
background-color: var(--settings-setting-on-color) !important;
}
}

.option {
align-items: center;
}

.text-input {
position: relative;
width: fit-content;
}

.text-input .status-icon {
position: absolute;
right: -1.75em;
top: 0;
bottom: 0;
margin: auto;
}

.text-input .status-icon {
opacity: 0;
transition: all 200ms ease-in-out;
}

.text-input .status-icon.changed,
.text-input .status-icon.saved {
opacity: 1;
}

.text-input .status-icon.changed {
color: var(--settings-text-color) !important;
}

.text-input input {
color: var(--settings-text-color);
padding: 10.5px 14px;
background-color: var(--settings-setting-off-color);
}

.text-input fieldset {
border-color: var(--settings-text-color) !important;
opacity: 0.5;
}

.text-input .Mui-focused input {
background-color: var(--settings-setting-off-color);
}

.text-input .Mui-focused fieldset {
border-color: var(--settings-setting-on-color) !important;
opacity: 1;
}
62 changes: 62 additions & 0 deletions app/frontend/src/Settings/SettingComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { string, func, any, arrayOf, number, bool } from 'prop-types'

import classNames from 'classnames'

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCheck } from '@fortawesome/free-solid-svg-icons'
import {
Switch,
Select,
MenuItem,
Slider as MaterialSlider,
Button as MaterialButton,
TextField,
} from '@material-ui/core'

import { OPTION_TYPES } from '../lib/options'
Expand Down Expand Up @@ -124,11 +127,70 @@ UrlDropdown.propTypes = {
url: string.isRequired,
}

export const TextInput = ( { className, value, onChange, ...props } ) => {
const [ isChanged, setChanged ] = useState()
const [ isSaved, setSaved ] = useState()

const onFocus = event => {
event.target.select()
}

useEffect( () => {
const timer = setTimeout( () => {
setSaved( false )
}, 3000 )

return () => clearTimeout( timer )
}, [ isSaved ] )

const onBlur = ( ...params ) => {
onChange( ...params )

if ( isChanged ) {
setTimeout( () => {
setSaved( isChanged )
}, 500 )
}
setChanged( false )
}

return (
<div key={value} className={classNames( className, 'text-input' )}>
<TextField
className="text-field"
variant="outlined"
{...props}
onBlur={onBlur}
onChange={() => setChanged( true )}
onFocus={onFocus}
defaultValue={value}
/>

<FontAwesomeIcon
className={classNames( 'status-icon', { saved: isSaved } )}
icon={faCheck}
/>
</div>
)
}

TextInput.propTypes = {
className: string,
onChange: func,
value: string.isRequired,
}

TextInput.defaultProps = {
className: null,
onChange: () => {},
}

const typeComponents = {
[ OPTION_TYPES.dropdown ]: GeneralSettingEvent( Dropdown ),
[ OPTION_TYPES.toggle ]: GeneralSettingParam( Toggle ),
[ OPTION_TYPES.slider ]: GeneralSettingParam( Slider ),
[ OPTION_TYPES.urlDropdown ]: GeneralSettingEvent( UrlDropdown ),
[ OPTION_TYPES.textInput ]: GeneralSettingEvent( TextInput ),
}

export default type => typeComponents[ type ]
Loading

0 comments on commit d6ddfbc

Please sign in to comment.