change(ui) - player timeline slider and other fixes
This commit is contained in:
parent
410ffb1475
commit
fa7ae29a62
18 changed files with 327 additions and 88 deletions
|
|
@ -1,12 +1,12 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { Loader, NoContent, Button, LoadMoreButton, Pagination } from 'UI';
|
||||
import { Loader, NoContent, Button, Pagination } from 'UI';
|
||||
import { applyFilter, addAttribute, addEvent } from 'Duck/filters';
|
||||
import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage } from 'Duck/search';
|
||||
import { fetchSessions, addFilterByKeyAndValue, updateCurrentPage, setScrollPosition } from 'Duck/search';
|
||||
import SessionItem from 'Shared/SessionItem';
|
||||
import SessionListHeader from './SessionListHeader';
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
|
||||
const ALL = 'all';
|
||||
// const ALL = 'all';
|
||||
const PER_PAGE = 10;
|
||||
const AUTOREFRESH_INTERVAL = 3 * 60 * 1000;
|
||||
var timeoutId;
|
||||
|
|
@ -21,6 +21,7 @@ var timeoutId;
|
|||
filters: state.getIn([ 'search', 'instance', 'filters' ]),
|
||||
metaList: state.getIn(['customFields', 'list']).map(i => i.key),
|
||||
currentPage: state.getIn([ 'search', 'currentPage' ]),
|
||||
scrollY: state.getIn([ 'search', 'scrollY' ]),
|
||||
}), {
|
||||
applyFilter,
|
||||
addAttribute,
|
||||
|
|
@ -28,24 +29,15 @@ var timeoutId;
|
|||
fetchSessions,
|
||||
addFilterByKeyAndValue,
|
||||
updateCurrentPage,
|
||||
setScrollPosition,
|
||||
})
|
||||
export default class SessionList extends React.PureComponent {
|
||||
state = {
|
||||
showPages: 1,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.timeout();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.loading && !this.props.loading) {
|
||||
this.setState({ showPages: 1 });
|
||||
}
|
||||
}
|
||||
|
||||
addPage = () => this.setState({ showPages: this.state.showPages + 1 })
|
||||
|
||||
onUserClick = (userId, userAnonymousId) => {
|
||||
if (userId) {
|
||||
this.props.addFilterByKeyAndValue(FilterKey.USERID, userId);
|
||||
|
|
@ -75,17 +67,22 @@ export default class SessionList extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.setScrollPosition(window.scrollY)
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
|
||||
|
||||
componentDidMount() {
|
||||
const { scrollY } = this.props;
|
||||
console.log('scrollY', scrollY);
|
||||
window.scrollTo(0, scrollY);
|
||||
}
|
||||
|
||||
renderActiveTabContent(list) {
|
||||
const {
|
||||
loading,
|
||||
filters,
|
||||
onMenuItemClick,
|
||||
allList,
|
||||
// onMenuItemClick,
|
||||
// allList,
|
||||
activeTab,
|
||||
metaList,
|
||||
currentPage,
|
||||
|
|
@ -93,8 +90,6 @@ export default class SessionList extends React.PureComponent {
|
|||
} = this.props;
|
||||
const _filterKeys = filters.map(i => i.key);
|
||||
const hasUserFilter = _filterKeys.includes(FilterKey.USERID) || _filterKeys.includes(FilterKey.USERANONYMOUSID);
|
||||
const { showPages } = this.state;
|
||||
const displayedCount = Math.min(showPages * PER_PAGE, list.size);
|
||||
|
||||
return (
|
||||
<NoContent
|
||||
|
|
@ -105,7 +100,7 @@ export default class SessionList extends React.PureComponent {
|
|||
subtext={
|
||||
<div>
|
||||
<div>Please try changing your search parameters.</div>
|
||||
{allList.size > 0 && (
|
||||
{/* {allList.size > 0 && (
|
||||
<div className="pt-2">
|
||||
However, we found other sessions based on your search parameters.
|
||||
<div>
|
||||
|
|
@ -115,7 +110,7 @@ export default class SessionList extends React.PureComponent {
|
|||
>See All</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
|
|
@ -139,41 +134,29 @@ export default class SessionList extends React.PureComponent {
|
|||
debounceRequest={1000}
|
||||
/>
|
||||
</div>
|
||||
{/* <LoadMoreButton
|
||||
className="mt-12 mb-12"
|
||||
displayedCount={displayedCount}
|
||||
totalCount={list.size}
|
||||
loading={loading}
|
||||
onClick={this.addPage}
|
||||
description={ displayedCount === list.size &&
|
||||
<div className="color-gray-medium text-sm text-center my-3">
|
||||
Haven't found the session in the above list? <br/>Try being a bit more specific by setting a specific time frame or simply use different filters
|
||||
</div>
|
||||
}
|
||||
/> */}
|
||||
</NoContent>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { activeTab, allList, total } = this.props;
|
||||
var filteredList;
|
||||
// var filteredList;
|
||||
|
||||
if (activeTab.type !== ALL && activeTab.type !== 'bookmark' && activeTab.type !== 'live') { // Watchdog sessions
|
||||
filteredList = allList.filter(session => activeTab.fits(session))
|
||||
} else {
|
||||
filteredList = allList
|
||||
}
|
||||
// if (activeTab.type !== ALL && activeTab.type !== 'bookmark' && activeTab.type !== 'live') { // Watchdog sessions
|
||||
// filteredList = allList.filter(session => activeTab.fits(session))
|
||||
// } else {
|
||||
// filteredList = allList
|
||||
// }
|
||||
|
||||
if (activeTab.type === 'bookmark') {
|
||||
filteredList = filteredList.filter(item => item.favorite)
|
||||
}
|
||||
const _total = activeTab.type === 'all' ? total : filteredList.size
|
||||
// if (activeTab.type === 'bookmark') {
|
||||
// filteredList = filteredList.filter(item => item.favorite)
|
||||
// }
|
||||
// const _total = activeTab.type === 'all' ? total : allList.size
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<SessionListHeader activeTab={activeTab} count={_total}/>
|
||||
{ this.renderActiveTabContent(filteredList) }
|
||||
<SessionListHeader activeTab={activeTab} count={total}/>
|
||||
{ this.renderActiveTabContent(allList) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function CustomMetriPercentage(props: Props) {
|
|||
return (
|
||||
<div className="flex flex-col items-center justify-center" style={{ height: '240px'}}>
|
||||
<div className="text-6xl">{numberWithCommas(data.count)}</div>
|
||||
<div className="text-lg mt-6">{`${data.previousCount} ( ${data.countProgress}% )`}</div>
|
||||
<div className="text-lg mt-6">{`${parseInt(data.previousCount).toFixed(1)} ( ${parseInt(data.countProgress).toFixed(1)}% )`}</div>
|
||||
<div className="color-gray-medium">from previous period.</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import cn from 'classnames';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { Icon } from 'UI';
|
||||
|
||||
import cls from './PlayOverlay.css';
|
||||
|
||||
export default function PlayOverlay({ player }) {
|
||||
|
|
@ -11,20 +9,17 @@ export default function PlayOverlay({ player }) {
|
|||
const togglePlay = useCallback(() => {
|
||||
player.togglePlay();
|
||||
setIconVisible(true);
|
||||
setTimeout(
|
||||
() => setIconVisible(false),
|
||||
800,
|
||||
);
|
||||
setTimeout(() => setIconVisible(false), 800);
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
className="absolute inset-0 flex items-center justify-center"
|
||||
onClick={ togglePlay }
|
||||
>
|
||||
<div className={ cn("flex items-center justify-center", cls.iconWrapper, { [ cls.zoomWrapper ]: iconVisible }) } >
|
||||
<Icon name={ player.state.playing ? "play" : "pause"} size="30" color="gray-medium"/>
|
||||
</div>
|
||||
</div>
|
||||
className="absolute inset-0 flex items-center justify-center"
|
||||
onClick={ togglePlay }
|
||||
>
|
||||
<div className={ cn("flex items-center justify-center", cls.iconWrapper, { [ cls.zoomWrapper ]: iconVisible }) } >
|
||||
<Icon name={ player.state.playing ? "play" : "pause"} size="30" color="gray-medium"/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
import { useCallback } from 'react';
|
||||
import cn from 'classnames';
|
||||
import { Popup } from 'UI';
|
||||
|
||||
import { CRASHES, EVENTS } from 'Player/ios/state';
|
||||
|
||||
import TimeTracker from './TimeTracker';
|
||||
import PlayerTime from './PlayerTime';
|
||||
|
||||
import cls from './timeline.css';
|
||||
|
||||
export default function Timeline({ player }) {
|
||||
|
|
@ -19,7 +16,7 @@ export default function Timeline({ player }) {
|
|||
const time = Math.max(Math.round(p * player.state.endTime), 0);
|
||||
player.jump(time);
|
||||
});
|
||||
const scale = 100 / player.state.endTime;
|
||||
const scale = 100 / player.state.endTime;
|
||||
return (
|
||||
<div className="flex items-center" >
|
||||
<PlayerTime player={player} timeKey="time"/>
|
||||
|
|
|
|||
18
frontend/app/components/Session_/Player/Controls/Circle.tsx
Normal file
18
frontend/app/components/Session_/Player/Controls/Circle.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React, { memo, FC } from 'react';
|
||||
import styles from './timeline.css';
|
||||
|
||||
interface Props {
|
||||
preview?: boolean;
|
||||
}
|
||||
export const Circle: FC<Props> = memo(function Box({ preview }) {
|
||||
// const backgroundColor = yellow ? 'yellow' : 'white'
|
||||
return (
|
||||
<div
|
||||
className={ styles.positionTracker }
|
||||
// style={ { left: `${ time * scale }%` } }
|
||||
role={preview ? 'BoxPreview' : 'Box'}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export default Circle;
|
||||
|
|
@ -118,6 +118,7 @@ export default class Controls extends React.Component {
|
|||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.onKeyDown);
|
||||
//this.props.toggleInspectorMode(false);
|
||||
|
|
@ -166,10 +167,10 @@ export default class Controls extends React.Component {
|
|||
return;
|
||||
}
|
||||
if (this.props.inspectorMode) return;
|
||||
if (e.key === ' ') {
|
||||
document.activeElement.blur();
|
||||
this.props.togglePlay();
|
||||
}
|
||||
// if (e.key === ' ') {
|
||||
// document.activeElement.blur();
|
||||
// this.props.togglePlay();
|
||||
// }
|
||||
if (e.key === 'Esc' || e.key === 'Escape') {
|
||||
this.props.fullscreenOff();
|
||||
}
|
||||
|
|
@ -262,7 +263,7 @@ export default class Controls extends React.Component {
|
|||
|
||||
return (
|
||||
<div className={ cn(styles.controls, {'px-5 pt-0' : live}) }>
|
||||
{ !live && <Timeline jump={ this.props.jump } /> }
|
||||
{ !live && <Timeline jump={ this.props.jump } pause={this.props.pause} togglePlay={this.props.togglePlay} /> }
|
||||
{ !fullscreen &&
|
||||
<div className={ styles.buttons } data-is-live={ live }>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
import React, { memo } from 'react';
|
||||
import { useDragLayer } from "react-dnd";
|
||||
import Circle from './Circle'
|
||||
import type { CSSProperties, FC } from 'react'
|
||||
|
||||
const layerStyles: CSSProperties = {
|
||||
position: "fixed",
|
||||
pointerEvents: "none",
|
||||
zIndex: 100,
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
};
|
||||
|
||||
const ItemTypes = {
|
||||
BOX: 'box',
|
||||
}
|
||||
|
||||
function getItemStyles(initialOffset, currentOffset, maxX, minX) {
|
||||
if (!initialOffset || !currentOffset) {
|
||||
return {
|
||||
display: "none"
|
||||
};
|
||||
}
|
||||
let { x, y } = currentOffset;
|
||||
// if (isSnapToGrid) {
|
||||
// x -= initialOffset.x;
|
||||
// y -= initialOffset.y;
|
||||
// [x, y] = [x, y];
|
||||
// x += initialOffset.x;
|
||||
// y += initialOffset.y;
|
||||
// }
|
||||
if (x > maxX) {
|
||||
x = maxX;
|
||||
}
|
||||
|
||||
if (x < minX) {
|
||||
x = minX;
|
||||
}
|
||||
const transform = `translate(${x}px, ${initialOffset.y}px)`;
|
||||
return {
|
||||
transition: 'transform 0.1s ease-out',
|
||||
transform,
|
||||
WebkitTransform: transform
|
||||
};
|
||||
}
|
||||
|
||||
interface Props {
|
||||
onDrag: (offset: { x: number, y: number } | null) => void;
|
||||
maxX: number;
|
||||
minX: number;
|
||||
}
|
||||
|
||||
const CustomDragLayer: FC<Props> = memo(function CustomDragLayer(props) {
|
||||
const {
|
||||
itemType,
|
||||
isDragging,
|
||||
item,
|
||||
initialOffset,
|
||||
currentOffset,
|
||||
} = useDragLayer((monitor) => ({
|
||||
item: monitor.getItem(),
|
||||
itemType: monitor.getItemType(),
|
||||
initialOffset: monitor.getInitialSourceClientOffset(),
|
||||
currentOffset: monitor.getSourceClientOffset(),
|
||||
isDragging: monitor.isDragging(),
|
||||
}));
|
||||
|
||||
function renderItem() {
|
||||
switch (itemType) {
|
||||
case ItemTypes.BOX:
|
||||
return <Circle />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isDragging) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isDragging) {
|
||||
props.onDrag(currentOffset)
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={layerStyles}>
|
||||
<div
|
||||
style={getItemStyles(initialOffset, currentOffset, props.maxX, props.minX)}
|
||||
>
|
||||
{renderItem()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
||||
export default CustomDragLayer;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
import React, { memo, FC, useEffect, useRef, CSSProperties } from 'react';
|
||||
import type { DragSourceMonitor } from 'react-dnd'
|
||||
import { useDrag } from 'react-dnd'
|
||||
import { getEmptyImage } from 'react-dnd-html5-backend'
|
||||
import Circle from './Circle'
|
||||
|
||||
function getStyles(
|
||||
left: number,
|
||||
isDragging: boolean,
|
||||
): CSSProperties {
|
||||
// const transform = `translate3d(${(left * 1161) / 100}px, -8px, 0)`
|
||||
return {
|
||||
position: 'absolute',
|
||||
top: '-3px',
|
||||
left: `${left}%`,
|
||||
// transform,
|
||||
// WebkitTransform: transform,
|
||||
// IE fallback: hide the real node using CSS when dragging
|
||||
// because IE will ignore our custom "empty image" drag preview.
|
||||
opacity: isDragging ? 0 : 1,
|
||||
height: isDragging ? 0 : '',
|
||||
zIndex: '99999',
|
||||
cursor: 'move'
|
||||
}
|
||||
}
|
||||
|
||||
const ItemTypes = {
|
||||
BOX: 'box',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
left: number;
|
||||
top: number;
|
||||
onDrop?: (item, monitor) => void;
|
||||
}
|
||||
|
||||
const DraggableCircle: FC<Props> = memo(function DraggableCircle(props) {
|
||||
const { left, top } = props
|
||||
const [{ isDragging, item }, dragRef, preview] = useDrag(
|
||||
() => ({
|
||||
type: ItemTypes.BOX,
|
||||
item: { left, top },
|
||||
end: props.onDrop,
|
||||
collect: (monitor: DragSourceMonitor) => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
item: monitor.getItem(),
|
||||
}),
|
||||
}),
|
||||
[left, top],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
preview(getEmptyImage(), { captureDraggingState: true })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dragRef}
|
||||
style={getStyles(left, isDragging)}
|
||||
role="DraggableBox"
|
||||
>
|
||||
<Circle />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
||||
export default DraggableCircle
|
||||
|
|
@ -4,10 +4,6 @@ import styles from './timeTracker.css';
|
|||
|
||||
const TimeTracker = ({ time, scale }) => (
|
||||
<React.Fragment>
|
||||
<div
|
||||
className={ styles.positionTracker }
|
||||
style={ { left: `${ time * scale }%` } }
|
||||
/>
|
||||
<span
|
||||
className={ styles.playedTimeline }
|
||||
style={ { width: `${ time * scale }%` } }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { ReduxTime } from './Time';
|
|||
import stl from './timeline.css';
|
||||
import { TYPES } from 'Types/session/event';
|
||||
import { setTimelinePointer } from 'Duck/sessions';
|
||||
import DraggableCircle from './DraggableCircle';
|
||||
import CustomDragLayer from './CustomDragLayer';
|
||||
|
||||
const getPointerIcon = (type) => {
|
||||
// exception,
|
||||
|
|
@ -51,6 +53,8 @@ const getPointerIcon = (type) => {
|
|||
}
|
||||
|
||||
@connectPlayer(state => ({
|
||||
playing: state.playing,
|
||||
time: state.time,
|
||||
skipIntervals: state.skipIntervals,
|
||||
events: state.eventList,
|
||||
skip: state.skip,
|
||||
|
|
@ -72,6 +76,11 @@ const getPointerIcon = (type) => {
|
|||
state.getIn([ 'sessions', 'current', 'returningLocationTime' ]),
|
||||
}), { setTimelinePointer })
|
||||
export default class Timeline extends React.PureComponent {
|
||||
progressRef = React.createRef()
|
||||
progressWidth = 0
|
||||
seekTime = 0
|
||||
wasPlaying = false
|
||||
|
||||
seekProgress = (e) => {
|
||||
const { endTime } = this.props;
|
||||
const p = e.nativeEvent.offsetX / e.target.offsetWidth;
|
||||
|
|
@ -88,11 +97,32 @@ export default class Timeline extends React.PureComponent {
|
|||
componentDidMount() {
|
||||
const { issues, events, fetchList, skipToIssue } = this.props;
|
||||
const firstIssue = issues.get(0);
|
||||
this.progressWidth = this.progressRef.current.offsetWidth;
|
||||
|
||||
if (firstIssue && skipToIssue) {
|
||||
this.props.jump(firstIssue.time);
|
||||
}
|
||||
}
|
||||
|
||||
onDragEnd = (item, monitor) => {
|
||||
this.props.jump(this.seekTime);
|
||||
if (this.wasPlaying) {
|
||||
this.props.togglePlay();
|
||||
}
|
||||
}
|
||||
|
||||
onDrag = (offset) => {
|
||||
const { endTime } = this.props;
|
||||
|
||||
const p = (offset.x - 60) / this.progressRef.current.offsetWidth;
|
||||
const time = Math.max(Math.round(p * endTime), 0);
|
||||
this.seekTime = time;
|
||||
if (this.props.playing) {
|
||||
this.wasPlaying = true;
|
||||
this.props.pause();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
events,
|
||||
|
|
@ -103,7 +133,7 @@ export default class Timeline extends React.PureComponent {
|
|||
live,
|
||||
logList,
|
||||
exceptionsList,
|
||||
resourceList,
|
||||
resourceList,
|
||||
clickRageTime,
|
||||
stackList,
|
||||
fetchList,
|
||||
|
|
@ -111,12 +141,19 @@ export default class Timeline extends React.PureComponent {
|
|||
} = this.props;
|
||||
|
||||
const scale = 100 / endTime;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ cn("flex items-center") }
|
||||
>
|
||||
{ !live && <ReduxTime name="time" /> }
|
||||
<div className={ stl.progress } onClick={ disabled ? null : this.seekProgress }>
|
||||
<div
|
||||
className={ stl.progress }
|
||||
onClick={ disabled ? null : this.seekProgress }
|
||||
ref={ this.progressRef }
|
||||
>
|
||||
<DraggableCircle left={this.props.time * scale} onDrop={this.onDragEnd} />
|
||||
<CustomDragLayer onDrag={this.onDrag} minX={70} maxX={this.progressRef.current && this.progressRef.current.offsetWidth + 70} />
|
||||
<TimeTracker scale={ scale } />
|
||||
{ skip && skipIntervals.map(interval =>
|
||||
(<div
|
||||
|
|
|
|||
|
|
@ -2,4 +2,6 @@
|
|||
.time {
|
||||
padding: 0 12px;
|
||||
color: $gray-medium;
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -1,4 +1,29 @@
|
|||
@import 'zindex.css';
|
||||
|
||||
.positionTracker {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
/* border: solid 1px $teal; */
|
||||
outline: solid 1px $teal;
|
||||
outline-style: inset;
|
||||
margin-left: -7px;
|
||||
border-radius: 50%;
|
||||
background-color: $active-blue;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: $positionTracker;
|
||||
top: 0;
|
||||
transition: all 0.2s ease-out;
|
||||
&:hover,
|
||||
&:focus {
|
||||
transition: all 0.1s ease-in;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: -2px;
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 10px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useCallback } from 'react';
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import cn from 'classnames';
|
||||
import { Icon } from 'UI';
|
||||
|
||||
|
|
@ -12,14 +12,28 @@ interface Props {
|
|||
|
||||
export default function PlayIconLayer({ playing, togglePlay }: Props) {
|
||||
const [ showPlayOverlayIcon, setShowPlayOverlayIcon ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// TODO Find a better way to do this
|
||||
document.addEventListener('keydown', onKeyDown);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', onKeyDown);
|
||||
}
|
||||
}, [])
|
||||
|
||||
const onKeyDown = (e) => {
|
||||
if (e.key === ' ') {
|
||||
togglePlayAnimated()
|
||||
}
|
||||
}
|
||||
|
||||
const togglePlayAnimated = useCallback(() => {
|
||||
setShowPlayOverlayIcon(true);
|
||||
togglePlay();
|
||||
setTimeout(
|
||||
() => setShowPlayOverlayIcon(false),
|
||||
800,
|
||||
);
|
||||
setTimeout(() => setShowPlayOverlayIcon(false), 800);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={ clsOv.overlay } onClick={ togglePlayAnimated }>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ export default class Player extends React.PureComponent {
|
|||
closedLive,
|
||||
} = this.props;
|
||||
|
||||
console.log('PlayerControls', PlayerControls)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ cn(className, stl.playerBody, "flex flex-col relative") }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.wrapper {
|
||||
position: relative;
|
||||
/* margin-left: 25px; */
|
||||
margin-left: 15px;
|
||||
|
||||
&:hover .pin {
|
||||
border: solid thin rgba(0,0,0,0.2);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const APPLY = `${name}/APPLY`;
|
|||
const SET_ALERT_METRIC_ID = `${name}/SET_ALERT_METRIC_ID`;
|
||||
const UPDATE_CURRENT_PAGE = `${name}/UPDATE_CURRENT_PAGE`;
|
||||
const SET_ACTIVE_TAB = `${name}/SET_ACTIVE_TAB`;
|
||||
const SET_SCROLL_POSITION = `${name}/SET_SCROLL_POSITION`;
|
||||
|
||||
const REFRESH_FILTER_OPTIONS = 'filters/REFRESH_FILTER_OPTIONS';
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ const initialState = Map({
|
|||
filterSearchList: {},
|
||||
currentPage: 1,
|
||||
activeTab: {name: 'All', type: 'all' },
|
||||
scrollY: 0,
|
||||
});
|
||||
|
||||
// Metric - Series - [] - filters
|
||||
|
|
@ -91,6 +93,8 @@ function reducer(state = initialState, action = {}) {
|
|||
return state.set('currentPage', action.page);
|
||||
case SET_ACTIVE_TAB:
|
||||
return state.set('activeTab', action.tab).set('currentPage', 1);
|
||||
case SET_SCROLL_POSITION:
|
||||
return state.set('scrollY', action.scrollPosition);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
|
@ -300,4 +304,11 @@ export const refreshFilterOptions = () => {
|
|||
return {
|
||||
type: REFRESH_FILTER_OPTIONS
|
||||
}
|
||||
}
|
||||
|
||||
export const setScrollPosition = (scrollPosition) => {
|
||||
return {
|
||||
type: SET_SCROLL_POSITION,
|
||||
scrollPosition,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,9 @@
|
|||
import './init';
|
||||
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import store from './store';
|
||||
import Router from './Router';
|
||||
import { StoreProvider, RootStore } from './mstore';
|
||||
import { ModalProvider } from './components/Modal';
|
||||
import ModalRoot from './components/Modal/ModalRoot';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend'
|
||||
import { DndProvider } from 'react-dnd'
|
||||
|
||||
|
|
@ -17,10 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
<Provider store={ store }>
|
||||
<StoreProvider store={new RootStore()}>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
{/* <ModalProvider> */}
|
||||
{/* <ModalRoot /> */}
|
||||
<Router />
|
||||
{/* </ModalProvider> */}
|
||||
</DndProvider>
|
||||
</StoreProvider>
|
||||
</Provider>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pie-chart-fill" viewBox="0 0 16 16">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="bi bi-pie-chart-fill" viewBox="0 0 16 16">
|
||||
<path d="M15.985 8.5H8.207l-5.5 5.5a8 8 0 0 0 13.277-5.5zM2 13.292A8 8 0 0 1 7.5.015v7.778l-5.5 5.5zM8.5.015V7.5h7.485A8.001 8.001 0 0 0 8.5.015z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 290 B After Width: | Height: | Size: 247 B |
Loading…
Add table
Reference in a new issue