From 2dc5885c88c88a324841f0bdacb80be78c1ceb6c Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 5 Mar 2020 10:43:48 +1100 Subject: [PATCH] Microphone timer --- _locales/en/messages.json | 4 + js/background.js | 1 + package.json | 1 + preload.js | 1 + stylesheets/_session_conversation.scss | 28 ++++- .../session/conversation/SessionRecording.tsx | 105 +++++++++++++----- yarn.lock | 17 ++- 7 files changed, 120 insertions(+), 37 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index a440679c1..e163547a7 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -876,6 +876,10 @@ "description": "Shown in toast if user clicks on quote references messages not loaded in view, but in database" }, + "recording": { + "message": "Recording", + "description": "Shown on SessionRecording when recording is active." + }, "voiceNoteMustBeOnlyAttachment": { "message": "A voice note must be the only attachment included in a message.", diff --git a/js/background.js b/js/background.js index d8aba5f6e..42c1a444c 100644 --- a/js/background.js +++ b/js/background.js @@ -942,6 +942,7 @@ .toString(36) .substring(3); + window.toasts = new Map(); window.pushToast = options => { // Setting toasts with the same ID can be used to prevent identical diff --git a/package.json b/package.json index 78019ed96..abc35e504 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@journeyapps/sqlcipher": "https://github.com/scottnonnenberg-signal/node-sqlcipher.git#2e28733b61640556b0272a3bfc78b0357daf71e6", "@sindresorhus/is": "0.8.0", "@types/dompurify": "^2.0.0", + "@types/moment": "^2.13.0", "@types/rc-slider": "^8.6.5", "@types/react-mic": "^12.4.1", "backbone": "1.3.3", diff --git a/preload.js b/preload.js index 6083dcc04..bc3d4188c 100644 --- a/preload.js +++ b/preload.js @@ -79,6 +79,7 @@ window.CONSTANTS = { // at which more messages should be loaded MESSAGE_CONTAINER_BUFFER_OFFSET_PX: 30, MESSAGE_FETCH_INTERVAL: 1, + MAX_VOICE_MESSAGE_DURATION: 600, }; window.versionInfo = { diff --git a/stylesheets/_session_conversation.scss b/stylesheets/_session_conversation.scss index 694c41656..d872d0af4 100644 --- a/stylesheets/_session_conversation.scss +++ b/stylesheets/_session_conversation.scss @@ -266,11 +266,10 @@ $composition-container-height: 60px; canvas { width: 100%; padding: 0px $session-margin-lg; - max-width: 700px; } } - &--delete { + &--status { display: flex; justify-content: center; position: absolute; @@ -278,7 +277,7 @@ $composition-container-height: 60px; right: 0; bottom: $composition-container-height + $session-margin-md; - .session-button.danger-alt{ + .session-button{ display: flex; justify-content: center; align-items: center; @@ -286,6 +285,29 @@ $composition-container-height: 60px; font-weight: 300; font-family: "SF Pro Text"; + &.primary { + cursor: default; + user-select: none; + &:hover{ + filter: brightness(100%); + } + } + } + } + + &--timer { + display: inline-flex; + align-items: center; + font-family: "SF Pro Text"; + font-weight: bold; + font-size: 14px; + + &-light{ + height: $session-margin-sm; + width: $session-margin-sm; + border-radius: 50%; + background-color: $session-color-danger-alt; + margin-left: $session-margin-sm; } } } \ No newline at end of file diff --git a/ts/components/session/conversation/SessionRecording.tsx b/ts/components/session/conversation/SessionRecording.tsx index 523b2783b..70926bb96 100644 --- a/ts/components/session/conversation/SessionRecording.tsx +++ b/ts/components/session/conversation/SessionRecording.tsx @@ -1,5 +1,5 @@ import React from 'react'; - +import moment from 'moment'; import { SessionIconButton, SessionIconSize, SessionIconType } from '../icon'; import { SessionButton, SessionButtonType, SessionButtonColor } from '../SessionButton'; @@ -17,6 +17,9 @@ interface State { actionHover: boolean; mediaSetting?: boolean; volumeArray?: Array; + startTimestamp: number; + nowTimestamp: number; + updateTimerInterval: NodeJS.Timeout; } export class SessionRecording extends React.Component { @@ -26,15 +29,6 @@ export class SessionRecording extends React.Component { constructor(props: any) { super(props); - this.state = { - recordDuration: 0, - isRecording: true, - isPaused: false, - actionHover: false, - mediaSetting: undefined, - volumeArray: undefined, - }; - this.handleHoverActions = this.handleHoverActions.bind(this); this.handleUnhoverActions = this.handleUnhoverActions.bind(this); @@ -44,25 +38,52 @@ export class SessionRecording extends React.Component { this.onSendVoiceMessage = this.onSendVoiceMessage.bind(this); this.onDeleteVoiceMessage = this.onDeleteVoiceMessage.bind(this); + this.timerUpdate = this.timerUpdate.bind(this); this.onStream = this.onStream.bind(this); this.visualisationRef = React.createRef(); this.visualisationCanvas = React.createRef(); + + const now = moment().unix(); + const updateTimerInterval = setInterval(this.timerUpdate, 1000); + + this.state = { + recordDuration: 0, + isRecording: true, + isPaused: false, + actionHover: false, + mediaSetting: undefined, + volumeArray: undefined, + startTimestamp: now, + nowTimestamp: now, + updateTimerInterval: updateTimerInterval, + }; + + } public async componentWillMount(){ // This turns on the microphone on the system. Later we need to turn it off. - this.initiateStream(); - } + public componentWillUnmount(){ + clearInterval(this.state.updateTimerInterval); + } render() { const actionPause = (this.state.actionHover && this.state.isRecording); const actionPlay = (!this.state.isRecording || this.state.isPaused); const actionDefault = !actionPause && !actionPlay; + const { isRecording, startTimestamp, nowTimestamp } = this.state; + + const elapsedTime = nowTimestamp - startTimestamp; + const displayTimeString = moment(elapsedTime).format('mm:ss'); + + console.log(`[vince][time] Elapsed time: `, this.state.nowTimestamp - this.state.startTimestamp); + console.log(`[vince][time] Elapsed time calculated: `, elapsedTime); + return (
@@ -104,23 +125,43 @@ export class SessionRecording extends React.Component {
-
- -
+ { isRecording ? ( +
+ { displayTimeString } +
+ +
+
+ ) : ( +
+ +
+ )} -
- + { isRecording ? ( + + buttonColor={SessionButtonColor.Primary} + /> + ) : ( + + )} +
); @@ -140,6 +181,12 @@ export class SessionRecording extends React.Component { } } + private timerUpdate(){ + this.setState({ + nowTimestamp: moment().unix() + }); + } + private handleUnhoverActions() { if (this.state.isRecording && this.state.actionHover) { this.setState({ @@ -245,7 +292,6 @@ export class SessionRecording extends React.Component { // Chop off values which exceed the bouinds of the container volumeArray = volumeArray.slice(0, numBars); console.log(`[vince][mic] Width: `, VISUALISATION_WIDTH); - canvas && (canvas.height = CANVAS_HEIGHT); canvas && (canvas.width = CANVAS_WIDTH); @@ -286,6 +332,8 @@ export class SessionRecording extends React.Component { } + + private onStreamError(error: any) { return error; } @@ -306,4 +354,3 @@ export class SessionRecording extends React.Component { } - diff --git a/yarn.lock b/yarn.lock index d57583ff2..003b1fc88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -281,6 +281,13 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.0.0.tgz#a3014921991066193f6c8e47290d4d598dfd19e6" integrity sha512-ZS0vBV7Jn5Z/Q4T3VXauEKMDCV8nWOtJJg90OsDylkYJiQwcWtKuLzohWzrthBkerUF7DLMmJcwOPEP0i/AOXw== +"@types/moment@^2.13.0": + version "2.13.0" + resolved "https://registry.yarnpkg.com/@types/moment/-/moment-2.13.0.tgz#604ebd189bc3bc34a1548689404e61a2a4aac896" + integrity sha1-YE69GJvDvDShVIaJQE5hoqSqyJY= + dependencies: + moment "*" + "@types/node@*": version "13.1.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.6.tgz#076028d0b0400be8105b89a0a55550c86684ffec" @@ -6609,16 +6616,16 @@ modify-filename@^1.1.0: resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" integrity sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE= +moment@*, moment@^2.10.6: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + moment@2.21.0: version "2.21.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" integrity sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ== -moment@^2.10.6: - version "2.24.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" - integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"