Add support for seekTime and segments url parameters (#29)
* Add coverage script for convenience * Add support for seekTime url parameter * Support preset segment as well * Test props pumping through can explorer to explorermain
parent
5c83305260
commit
f205520532
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable */
|
||||||
const WorkerLoaderPlugin = require('craco-worker-loader');
|
const WorkerLoaderPlugin = require('craco-worker-loader');
|
||||||
const SentryPlugin = require('craco-sentry-plugin');
|
const SentryPlugin = require('craco-sentry-plugin');
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,7 @@
|
||||||
"build:staging": "env-cmd .env.staging craco build",
|
"build:staging": "env-cmd .env.staging craco build",
|
||||||
"test": "craco test --env=jsdom",
|
"test": "craco test --env=jsdom",
|
||||||
"test-ci": "CI=true craco test --env=jsdom",
|
"test-ci": "CI=true craco test --env=jsdom",
|
||||||
|
"test-coverage": "CI=true craco test --env=jsdom --coverage",
|
||||||
"netlify-sass": "node-sass src/index.scss > src/index.css",
|
"netlify-sass": "node-sass src/index.scss > src/index.css",
|
||||||
"sass": "node-sass src/index.scss -o src && node-sass -w src/index.scss -o src",
|
"sass": "node-sass src/index.scss -o src && node-sass -w src/index.scss -o src",
|
||||||
"deploy": "npm run build && gh-pages -d build"
|
"deploy": "npm run build && gh-pages -d build"
|
||||||
|
@ -123,7 +124,8 @@
|
||||||
"^@commaai/pandajs$": "<rootDir>/node_modules/@commaai/pandajs/lib/index.js",
|
"^@commaai/pandajs$": "<rootDir>/node_modules/@commaai/pandajs/lib/index.js",
|
||||||
"^@commaai/hls.js$": "<rootDir>/node_modules/@commaai/hls.js/dist/hls.js",
|
"^@commaai/hls.js$": "<rootDir>/node_modules/@commaai/hls.js/dist/hls.js",
|
||||||
"^@commaai/(.*comma.*)$": "<rootDir>/node_modules/@commaai/$1/dist/index.js",
|
"^@commaai/(.*comma.*)$": "<rootDir>/node_modules/@commaai/$1/dist/index.js",
|
||||||
"^capnp-split$": "<rootDir>/node_modules/capnp-split/dist/index.js"
|
"^capnp-split$": "<rootDir>/node_modules/capnp-split/dist/index.js",
|
||||||
|
"\\.worker": "<rootDir>/src/__mocks__/workerMock.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
|
|
|
@ -34,10 +34,10 @@ import * as ObjectUtils from './utils/object';
|
||||||
import { hash } from './utils/string';
|
import { hash } from './utils/string';
|
||||||
import { modifyQueryParameters } from './utils/url';
|
import { modifyQueryParameters } from './utils/url';
|
||||||
|
|
||||||
const RLogDownloader = require('./workers/rlog-downloader.worker.js');
|
const RLogDownloader = require('./workers/rlog-downloader.worker');
|
||||||
const LogCSVDownloader = require('./workers/dbc-csv-downloader.worker.js');
|
const LogCSVDownloader = require('./workers/dbc-csv-downloader.worker');
|
||||||
const MessageParser = require('./workers/message-parser.worker.js');
|
const MessageParser = require('./workers/message-parser.worker');
|
||||||
const CanStreamerWorker = require('./workers/CanStreamerWorker.worker.js');
|
const CanStreamerWorker = require('./workers/CanStreamerWorker.worker');
|
||||||
|
|
||||||
export default class CanExplorer extends Component {
|
export default class CanExplorer extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -65,7 +65,7 @@ export default class CanExplorer extends Component {
|
||||||
dbcText: props.dbc ? props.dbc.text() : new DBC().text(),
|
dbcText: props.dbc ? props.dbc.text() : new DBC().text(),
|
||||||
dbcFilename: props.dbcFilename ? props.dbcFilename : 'New_DBC',
|
dbcFilename: props.dbcFilename ? props.dbcFilename : 'New_DBC',
|
||||||
dbcLastSaved: null,
|
dbcLastSaved: null,
|
||||||
seekTime: 0,
|
seekTime: props.seekTime || 0,
|
||||||
seekIndex: 0,
|
seekIndex: 0,
|
||||||
maxByteStateChangeCount: 0,
|
maxByteStateChangeCount: 0,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
|
@ -120,7 +120,7 @@ export default class CanExplorer extends Component {
|
||||||
this.pandaReader.onMessage(this.processStreamedCanMessages);
|
this.pandaReader.onMessage(this.processStreamedCanMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
const { dongleId, name } = this.props;
|
const { dongleId, name } = this.props;
|
||||||
if (CommaAuth.isAuthenticated() && !name) {
|
if (CommaAuth.isAuthenticated() && !name) {
|
||||||
this.showOnboarding();
|
this.showOnboarding();
|
||||||
|
@ -995,6 +995,8 @@ export default class CanExplorer extends Component {
|
||||||
partsLoaded
|
partsLoaded
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
|
const { startTime, segments } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
id="cabana"
|
id="cabana"
|
||||||
|
@ -1061,6 +1063,8 @@ export default class CanExplorer extends Component {
|
||||||
canFrameOffset={canFrameOffset}
|
canFrameOffset={canFrameOffset}
|
||||||
firstCanTime={firstCanTime}
|
firstCanTime={firstCanTime}
|
||||||
seekTime={seekTime}
|
seekTime={seekTime}
|
||||||
|
startTime={startTime}
|
||||||
|
startSegments={segments}
|
||||||
seekIndex={seekIndex}
|
seekIndex={seekIndex}
|
||||||
currentParts={currentParts}
|
currentParts={currentParts}
|
||||||
selectedPart={currentPart}
|
selectedPart={currentPart}
|
||||||
|
@ -1125,5 +1129,7 @@ CanExplorer.propTypes = {
|
||||||
githubAuthToken: PropTypes.string,
|
githubAuthToken: PropTypes.string,
|
||||||
autoplay: PropTypes.bool,
|
autoplay: PropTypes.bool,
|
||||||
max: PropTypes.number,
|
max: PropTypes.number,
|
||||||
url: PropTypes.string
|
url: PropTypes.string,
|
||||||
|
startTime: PropTypes.number,
|
||||||
|
segments: PropTypes.array
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* eslint-env jest */
|
||||||
|
class Worker {
|
||||||
|
postMessage = jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Worker;
|
|
@ -1,10 +1,55 @@
|
||||||
|
/* eslint-env jest */
|
||||||
|
/* eslint-disable react/jsx-props-no-spreading */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow, mount, render } from 'enzyme';
|
import { shallow, mount, render } from 'enzyme';
|
||||||
import { StyleSheetTestUtils } from 'aphrodite';
|
import { StyleSheetTestUtils } from 'aphrodite';
|
||||||
import CanExplorer from '../../CanExplorer';
|
import CanExplorer from '../../CanExplorer';
|
||||||
|
import Explorer from '../../components/Explorer';
|
||||||
|
import AcuraDbc from '../../acura-dbc';
|
||||||
|
|
||||||
global.__JEST__ = 1;
|
jest.mock('aphrodite/lib/inject');
|
||||||
|
jest.mock('../../components/HLS.js');
|
||||||
|
jest.mock('hls.js');
|
||||||
|
|
||||||
test('CanExplorer renders', () => {
|
global.document.querySelector = jest.fn();
|
||||||
const canExplorer = shallow(<CanExplorer />);
|
|
||||||
|
describe('CanExplorer', () => {
|
||||||
|
const props = {
|
||||||
|
max: 12,
|
||||||
|
url: 'https://chffrprivate.blob.core.windows.net/chffrprivate3-permanent/v2/cb38263377b873ee/78392b99580c5920227cc5b43dff8a70_2017-06-12--18-51-47',
|
||||||
|
name: '2017-06-12--18-51-47',
|
||||||
|
dongleId: 'cb38263377b873ee',
|
||||||
|
dbc: AcuraDbc,
|
||||||
|
isDemo: true,
|
||||||
|
dbcFilename: 'acura_ilx_2016_can.dbc',
|
||||||
|
autoplay: true
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders', () => {
|
||||||
|
/*
|
||||||
|
dongleId: PropTypes.string,
|
||||||
|
name: PropTypes.string,
|
||||||
|
dbc: PropTypes.instanceOf(DBC),
|
||||||
|
dbcFilename: PropTypes.string,
|
||||||
|
githubAuthToken: PropTypes.string,
|
||||||
|
autoplay: PropTypes.bool,
|
||||||
|
max: PropTypes.number,
|
||||||
|
url: PropTypes.string,
|
||||||
|
startTime: PropTypes.number,
|
||||||
|
segments: PropTypes.array
|
||||||
|
*/
|
||||||
|
const canExplorer = mount(<CanExplorer {...props} />);
|
||||||
|
expect(canExplorer.exists()).toBe(true);
|
||||||
|
expect(canExplorer.find(Explorer).length).toBe(1);
|
||||||
|
canExplorer.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes props to Explorer', () => {
|
||||||
|
const canExplorer = mount(<CanExplorer {...props} segments={[123, 321]} startTime={150} />);
|
||||||
|
expect(canExplorer.find(Explorer).length).toBe(1);
|
||||||
|
expect(canExplorer.exists()).toBe(true);
|
||||||
|
expect(canExplorer.find(Explorer).prop('startSegments')).toBe(canExplorer.prop('segments'));
|
||||||
|
expect(canExplorer.find(Explorer).prop('startTime')).toBe(canExplorer.prop('startTime'));
|
||||||
|
canExplorer.unmount();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,38 +9,75 @@ import RouteVideoSync from './RouteVideoSync';
|
||||||
import CanLog from './CanLog';
|
import CanLog from './CanLog';
|
||||||
import Entries from '../models/can/entries';
|
import Entries from '../models/can/entries';
|
||||||
import debounce from '../utils/debounce';
|
import debounce from '../utils/debounce';
|
||||||
import PartSelector from './PartSelector';
|
|
||||||
import PlaySpeedSelector from './PlaySpeedSelector';
|
import PlaySpeedSelector from './PlaySpeedSelector';
|
||||||
|
|
||||||
|
function clipSegment(_segment, _segmentIndices, nextMessage) {
|
||||||
|
let segment = _segment;
|
||||||
|
let segmentIndices = _segmentIndices;
|
||||||
|
if (segment.length === 2) {
|
||||||
|
const segmentStartIdx = nextMessage.entries.findIndex(
|
||||||
|
(e) => e.relTime >= segment[0]
|
||||||
|
);
|
||||||
|
let segmentEndIdx = nextMessage.entries.findIndex(
|
||||||
|
(e) => e.relTime >= segment[1]
|
||||||
|
);
|
||||||
|
if (segmentStartIdx !== -1) {
|
||||||
|
if (segmentEndIdx === -1) {
|
||||||
|
// previous segment end is past bounds of this message
|
||||||
|
segmentEndIdx = nextMessage.entries.length - 1;
|
||||||
|
}
|
||||||
|
const segmentStartTime = nextMessage.entries[segmentStartIdx].relTime;
|
||||||
|
const segmentEndTime = nextMessage.entries[segmentEndIdx].relTime;
|
||||||
|
|
||||||
|
segment = [segmentStartTime, segmentEndTime];
|
||||||
|
segmentIndices = [segmentStartIdx, segmentEndIdx];
|
||||||
|
} else {
|
||||||
|
// segment times are out of boudns for this message
|
||||||
|
segment = [];
|
||||||
|
segmentIndices = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { segment, segmentIndices };
|
||||||
|
}
|
||||||
|
|
||||||
export default class Explorer extends Component {
|
export default class Explorer extends Component {
|
||||||
static propTypes = {
|
updateSegment = debounce((messageId, _segment) => {
|
||||||
selectedMessage: PropTypes.string,
|
let segment = _segment;
|
||||||
url: PropTypes.string,
|
const { messages, selectedMessage, currentParts } = this.props;
|
||||||
live: PropTypes.bool.isRequired,
|
const { entries } = messages[selectedMessage];
|
||||||
messages: PropTypes.objectOf(PropTypes.object),
|
let segmentIndices = Entries.findSegmentIndices(entries, segment, true);
|
||||||
onConfirmedSignalChange: PropTypes.func.isRequired,
|
|
||||||
canFrameOffset: PropTypes.number,
|
// console.log(this.state.segment, '->', segment, segmentIndices);
|
||||||
firstCanTime: PropTypes.number,
|
if (
|
||||||
onSeek: PropTypes.func.isRequired,
|
segment[0] === currentParts[0] * 60
|
||||||
autoplay: PropTypes.bool.isRequired,
|
&& segment[1] === (currentParts[1] + 1) * 60
|
||||||
onPartChange: PropTypes.func.isRequired,
|
) {
|
||||||
partsCount: PropTypes.number
|
segment = [];
|
||||||
};
|
segmentIndices = [];
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
segment,
|
||||||
|
segmentIndices,
|
||||||
|
userSeekIndex: segmentIndices[0],
|
||||||
|
userSeekTime: segment[0] || 0
|
||||||
|
});
|
||||||
|
}, 250);
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
plottedSignals: [],
|
plottedSignals: [],
|
||||||
segment: [],
|
segment: props.startSegments || [],
|
||||||
segmentIndices: [],
|
segmentIndices: [],
|
||||||
shouldShowAddSignal: true,
|
shouldShowAddSignal: true,
|
||||||
userSeekIndex: 0,
|
userSeekIndex: 0,
|
||||||
userSeekTime: 0,
|
userSeekTime: 0,
|
||||||
playing: props.autoplay,
|
playing: props.autoplay,
|
||||||
signals: {},
|
|
||||||
playSpeed: 1
|
playSpeed: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
this.onSignalPlotPressed = this.onSignalPlotPressed.bind(this);
|
this.onSignalPlotPressed = this.onSignalPlotPressed.bind(this);
|
||||||
this.onSignalUnplotPressed = this.onSignalUnplotPressed.bind(this);
|
this.onSignalUnplotPressed = this.onSignalUnplotPressed.bind(this);
|
||||||
this.onSegmentChanged = this.onSegmentChanged.bind(this);
|
this.onSegmentChanged = this.onSegmentChanged.bind(this);
|
||||||
|
@ -52,61 +89,33 @@ export default class Explorer extends Component {
|
||||||
this.onPause = this.onPause.bind(this);
|
this.onPause = this.onPause.bind(this);
|
||||||
this.onVideoClick = this.onVideoClick.bind(this);
|
this.onVideoClick = this.onVideoClick.bind(this);
|
||||||
this.onSignalPlotChange = this.onSignalPlotChange.bind(this);
|
this.onSignalPlotChange = this.onSignalPlotChange.bind(this);
|
||||||
this._onKeyDown = this._onKeyDown.bind(this);
|
this.onKeyDown = this.onKeyDown.bind(this);
|
||||||
this.mergePlots = this.mergePlots.bind(this);
|
this.mergePlots = this.mergePlots.bind(this);
|
||||||
this.toggleShouldShowAddSignal = this.toggleShouldShowAddSignal.bind(this);
|
this.toggleShouldShowAddSignal = this.toggleShouldShowAddSignal.bind(this);
|
||||||
this.changePlaySpeed = this.changePlaySpeed.bind(this);
|
this.changePlaySpeed = this.changePlaySpeed.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onKeyDown(e) {
|
componentDidMount() {
|
||||||
|
document.addEventListener('keydown', this.onKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('keydown', this.onKeyDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(e) {
|
||||||
if (e.keyCode === 27) {
|
if (e.keyCode === 27) {
|
||||||
// escape
|
// escape
|
||||||
this.resetSegment();
|
this.resetSegment();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
document.addEventListener('keydown', this._onKeyDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('keydown', this._onKeyDown);
|
|
||||||
}
|
|
||||||
|
|
||||||
clipSegment(segment, segmentIndices, nextMessage) {
|
|
||||||
if (segment.length === 2) {
|
|
||||||
const segmentStartIdx = nextMessage.entries.findIndex(
|
|
||||||
(e) => e.relTime >= segment[0]
|
|
||||||
);
|
|
||||||
let segmentEndIdx = nextMessage.entries.findIndex(
|
|
||||||
(e) => e.relTime >= segment[1]
|
|
||||||
);
|
|
||||||
if (segmentStartIdx !== -1) {
|
|
||||||
if (segmentEndIdx === -1) {
|
|
||||||
// previous segment end is past bounds of this message
|
|
||||||
segmentEndIdx = nextMessage.entries.length - 1;
|
|
||||||
}
|
|
||||||
const segmentStartTime = nextMessage.entries[segmentStartIdx].relTime;
|
|
||||||
const segmentEndTime = nextMessage.entries[segmentEndIdx].relTime;
|
|
||||||
|
|
||||||
segment = [segmentStartTime, segmentEndTime];
|
|
||||||
segmentIndices = [segmentStartIdx, segmentEndIdx];
|
|
||||||
} else {
|
|
||||||
// segment times are out of boudns for this message
|
|
||||||
segment = [];
|
|
||||||
segmentIndices = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { segment, segmentIndices };
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
const nextMessage = nextProps.messages[nextProps.selectedMessage];
|
const nextMessage = nextProps.messages[nextProps.selectedMessage];
|
||||||
const curMessage = this.props.messages[this.props.selectedMessage];
|
const curMessage = this.props.messages[this.props.selectedMessage];
|
||||||
let { plottedSignals } = this.state;
|
let { plottedSignals } = this.state;
|
||||||
|
|
||||||
if (Object.keys(nextProps.messages).length === 0) {
|
if (Object.keys(nextProps.messages).length === 0 && Object.keys(this.props.messages).length !== 0) {
|
||||||
this.resetSegment();
|
this.resetSegment();
|
||||||
}
|
}
|
||||||
if (nextMessage && nextMessage.frame && nextMessage !== curMessage) {
|
if (nextMessage && nextMessage.frame && nextMessage !== curMessage) {
|
||||||
|
@ -142,7 +151,7 @@ export default class Explorer extends Component {
|
||||||
// by finding a entry indices
|
// by finding a entry indices
|
||||||
// corresponding to old message segment/seek times.
|
// corresponding to old message segment/seek times.
|
||||||
|
|
||||||
const { segment, segmentIndices } = this.clipSegment(
|
const { segment, segmentIndices } = clipSegment(
|
||||||
this.state.segment,
|
this.state.segment,
|
||||||
this.state.segmentIndices,
|
this.state.segmentIndices,
|
||||||
nextMessage
|
nextMessage
|
||||||
|
@ -171,7 +180,7 @@ export default class Explorer extends Component {
|
||||||
&& curMessage
|
&& curMessage
|
||||||
&& nextMessage.entries.length !== curMessage.entries.length
|
&& nextMessage.entries.length !== curMessage.entries.length
|
||||||
) {
|
) {
|
||||||
const { segment, segmentIndices } = this.clipSegment(
|
const { segment, segmentIndices } = clipSegment(
|
||||||
this.state.segment,
|
this.state.segment,
|
||||||
this.state.segmentIndices,
|
this.state.segmentIndices,
|
||||||
nextMessage
|
nextMessage
|
||||||
|
@ -227,26 +236,6 @@ export default class Explorer extends Component {
|
||||||
this.setState({ plottedSignals: newPlottedSignals });
|
this.setState({ plottedSignals: newPlottedSignals });
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSegment = debounce((messageId, segment) => {
|
|
||||||
const { entries } = this.props.messages[this.props.selectedMessage];
|
|
||||||
let segmentIndices = Entries.findSegmentIndices(entries, segment, true);
|
|
||||||
|
|
||||||
// console.log(this.state.segment, '->', segment, segmentIndices);
|
|
||||||
if (
|
|
||||||
segment[0] === this.props.currentParts[0] * 60
|
|
||||||
&& segment[1] === (this.props.currentParts[1] + 1) * 60
|
|
||||||
) {
|
|
||||||
segment = [];
|
|
||||||
segmentIndices = [];
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
segment,
|
|
||||||
segmentIndices,
|
|
||||||
userSeekIndex: segmentIndices[0],
|
|
||||||
userSeekTime: segment[0] || 0
|
|
||||||
});
|
|
||||||
}, 250);
|
|
||||||
|
|
||||||
onSegmentChanged(messageId, segment) {
|
onSegmentChanged(messageId, segment) {
|
||||||
if (Array.isArray(segment)) {
|
if (Array.isArray(segment)) {
|
||||||
this.updateSegment(messageId, segment);
|
this.updateSegment(messageId, segment);
|
||||||
|
@ -255,7 +244,6 @@ export default class Explorer extends Component {
|
||||||
|
|
||||||
resetSegment() {
|
resetSegment() {
|
||||||
const { segment, segmentIndices } = this.state;
|
const { segment, segmentIndices } = this.state;
|
||||||
const { messages, selectedMessage } = this.props;
|
|
||||||
if (segment.length > 0 || segmentIndices.length > 0) {
|
if (segment.length > 0 || segmentIndices.length > 0) {
|
||||||
// console.log(this.state.segment, '->', segment, segmentIndices);
|
// console.log(this.state.segment, '->', segment, segmentIndices);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -460,7 +448,7 @@ export default class Explorer extends Component {
|
||||||
? 'is-expanded'
|
? 'is-expanded'
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const { thumbnails, messages } = this.props;
|
const { thumbnails, messages, startTime } = this.props;
|
||||||
|
|
||||||
let graphSegment = this.state.segment;
|
let graphSegment = this.state.segment;
|
||||||
if (!graphSegment.length && this.props.currentParts) {
|
if (!graphSegment.length && this.props.currentParts) {
|
||||||
|
@ -489,6 +477,7 @@ export default class Explorer extends Component {
|
||||||
<RouteVideoSync
|
<RouteVideoSync
|
||||||
message={messages[this.props.selectedMessage]}
|
message={messages[this.props.selectedMessage]}
|
||||||
segment={this.state.segment}
|
segment={this.state.segment}
|
||||||
|
startTime={startTime}
|
||||||
seekIndex={this.props.seekIndex}
|
seekIndex={this.props.seekIndex}
|
||||||
userSeekIndex={this.state.userSeekIndex}
|
userSeekIndex={this.state.userSeekIndex}
|
||||||
playing={this.state.playing}
|
playing={this.state.playing}
|
||||||
|
@ -532,3 +521,20 @@ export default class Explorer extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Explorer.propTypes = {
|
||||||
|
selectedMessage: PropTypes.string,
|
||||||
|
url: PropTypes.string,
|
||||||
|
live: PropTypes.bool.isRequired,
|
||||||
|
messages: PropTypes.objectOf(PropTypes.object),
|
||||||
|
thumbnails: PropTypes.array.isRequired,
|
||||||
|
onConfirmedSignalChange: PropTypes.func.isRequired,
|
||||||
|
canFrameOffset: PropTypes.number,
|
||||||
|
firstCanTime: PropTypes.number,
|
||||||
|
onSeek: PropTypes.func.isRequired,
|
||||||
|
autoplay: PropTypes.bool.isRequired,
|
||||||
|
currentParts: PropTypes.array.isRequired,
|
||||||
|
partsCount: PropTypes.number,
|
||||||
|
startTime: PropTypes.number,
|
||||||
|
startSegments: PropTypes.array
|
||||||
|
};
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default class GithubDbcList extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
this.props.openDbcClient.list(this.props.repo).then((paths) => {
|
this.props.openDbcClient.list(this.props.repo).then((paths) => {
|
||||||
paths = paths.filter((path) => path.indexOf('.dbc') !== -1);
|
paths = paths.filter((path) => path.indexOf('.dbc') !== -1);
|
||||||
this.setState({ paths });
|
this.setState({ paths });
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default class Meta extends Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentDidMount() {
|
||||||
this.lastSavedTimer = setInterval(() => {
|
this.lastSavedTimer = setInterval(() => {
|
||||||
if (this.props.dbcLastSaved !== null) {
|
if (this.props.dbcLastSaved !== null) {
|
||||||
this.setState({ lastSaved: this.props.dbcLastSaved.fromNow() });
|
this.setState({ lastSaved: this.props.dbcLastSaved.fromNow() });
|
||||||
|
|
|
@ -205,7 +205,8 @@ export default class RouteVideoSync extends Component {
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
shouldRestartHls,
|
shouldRestartHls,
|
||||||
shouldShowJpeg
|
shouldShowJpeg,
|
||||||
|
videoElement
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
userSeekTime,
|
userSeekTime,
|
||||||
|
@ -213,7 +214,8 @@ export default class RouteVideoSync extends Component {
|
||||||
playSpeed,
|
playSpeed,
|
||||||
playing,
|
playing,
|
||||||
onVideoClick,
|
onVideoClick,
|
||||||
segmentIndices
|
segmentIndices,
|
||||||
|
startTime
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="cabana-explorer-visuals-camera">
|
<div className="cabana-explorer-visuals-camera">
|
||||||
|
@ -231,7 +233,7 @@ export default class RouteVideoSync extends Component {
|
||||||
url,
|
url,
|
||||||
process.env.REACT_APP_VIDEO_CDN
|
process.env.REACT_APP_VIDEO_CDN
|
||||||
).getRearCameraStreamIndexUrl()}
|
).getRearCameraStreamIndexUrl()}
|
||||||
startTime={this.startTime()}
|
startTime={startTime || 0}
|
||||||
videoLength={this.videoLength()}
|
videoLength={this.videoLength()}
|
||||||
playbackSpeed={playSpeed}
|
playbackSpeed={playSpeed}
|
||||||
onVideoElementAvailable={this.onVideoElementAvailable}
|
onVideoElementAvailable={this.onVideoElementAvailable}
|
||||||
|
@ -254,7 +256,7 @@ export default class RouteVideoSync extends Component {
|
||||||
segmentIndices={segmentIndices}
|
segmentIndices={segmentIndices}
|
||||||
onUserSeek={this.onUserSeek}
|
onUserSeek={this.onUserSeek}
|
||||||
onPlaySeek={this.onPlaySeek}
|
onPlaySeek={this.onPlaySeek}
|
||||||
videoElement={this.state.videoElement}
|
videoElement={videoElement}
|
||||||
onPlay={this.props.onPlay}
|
onPlay={this.props.onPlay}
|
||||||
onPause={this.props.onPause}
|
onPause={this.props.onPause}
|
||||||
playing={this.props.playing}
|
playing={this.props.playing}
|
||||||
|
@ -281,4 +283,5 @@ RouteVideoSync.propTypes = {
|
||||||
playSpeed: PropTypes.number.isRequired,
|
playSpeed: PropTypes.number.isRequired,
|
||||||
onVideoClick: PropTypes.func,
|
onVideoClick: PropTypes.func,
|
||||||
segmentIndices: PropTypes.array,
|
segmentIndices: PropTypes.array,
|
||||||
|
startTime: PropTypes.number
|
||||||
};
|
};
|
||||||
|
|
16
src/index.js
16
src/index.js
|
@ -18,7 +18,19 @@ Sentry.init();
|
||||||
|
|
||||||
const routeFullName = getUrlParameter('route');
|
const routeFullName = getUrlParameter('route');
|
||||||
const isDemo = !routeFullName;
|
const isDemo = !routeFullName;
|
||||||
const props = { autoplay: true, isDemo };
|
let segments = getUrlParameter('segments');
|
||||||
|
if (segments && segments.length) {
|
||||||
|
segments = segments.split(',').map(Number);
|
||||||
|
}
|
||||||
|
if (segments.length !== 2) {
|
||||||
|
segments = undefined;
|
||||||
|
}
|
||||||
|
const props = {
|
||||||
|
autoplay: true,
|
||||||
|
startTime: Number(getUrlParameter('seekTime') || 0),
|
||||||
|
segments,
|
||||||
|
isDemo
|
||||||
|
};
|
||||||
let persistedDbc = null;
|
let persistedDbc = null;
|
||||||
|
|
||||||
if (routeFullName) {
|
if (routeFullName) {
|
||||||
|
@ -96,7 +108,7 @@ async function init() {
|
||||||
if (token) {
|
if (token) {
|
||||||
Request.configure(token);
|
Request.configure(token);
|
||||||
}
|
}
|
||||||
ReactDOM.render(<CanExplorer {...props} />, document.getElementById('root'));
|
ReactDOM.render(<CanExplorer {...props} />, document.getElementById('root')); // eslint-disable-line react/jsx-props-no-spreading
|
||||||
}
|
}
|
||||||
|
|
||||||
if (routeFullName || isDemo) {
|
if (routeFullName || isDemo) {
|
||||||
|
|
|
@ -3,9 +3,10 @@ import Raven from 'raven-js';
|
||||||
function init() {
|
function init() {
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
const opts = {};
|
const opts = {};
|
||||||
|
const webpackHash = __webpack_hash__; // eslint-disable-line
|
||||||
|
|
||||||
if (typeof __webpack_hash__ !== 'undefined') {
|
if (typeof webpackHash !== 'undefined') {
|
||||||
opts.release = __webpack_hash__; // eslint-disable-line no-undef
|
opts.release = webpackHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
Raven.config(
|
Raven.config(
|
||||||
|
|
Loading…
Reference in New Issue