diff --git a/.github/workflows/frontend-dev.yaml b/.github/workflows/frontend-dev.yaml new file mode 100644 index 000000000..ef966c847 --- /dev/null +++ b/.github/workflows/frontend-dev.yaml @@ -0,0 +1,87 @@ +name: Frontend Dev Deployment +on: + workflow_dispatch: + push: + branches: + - player-refactoring-phase-1 + paths: + - frontend/** +# Disable previous workflows for this action. +concurrency: + group: ${{ github.workflow }} #-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Cache node modules + uses: actions/cache@v1 + with: + path: node_modules + key: ${{ runner.OS }}-build-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.OS }}-build- + ${{ runner.OS }}- + + - name: Docker login + run: | + docker login ${{ secrets.OSS_REGISTRY_URL }} -u ${{ secrets.OSS_DOCKER_USERNAME }} -p "${{ secrets.OSS_REGISTRY_TOKEN }}" + + - uses: azure/k8s-set-context@v1 + with: + method: kubeconfig + kubeconfig: ${{ secrets.DEV_KUBECONFIG }} # Use content of kubeconfig in secret. + id: setcontext + + - name: Building and Pushing frontend image + id: build-image + env: + DOCKER_REPO: ${{ secrets.OSS_REGISTRY_URL }} + IMAGE_TAG: ${{ github.ref_name }}_${{ github.sha }} + ENVIRONMENT: staging + run: | + set -x + cd frontend + mv .env.sample .env + docker run --rm -v /etc/passwd:/etc/passwd -u `id -u`:`id -g` -v $(pwd):/home/${USER} -w /home/${USER} --name node_build node:14-stretch-slim /bin/bash -c "yarn && yarn build" + # https://github.com/docker/cli/issues/1134#issuecomment-613516912 + DOCKER_BUILDKIT=1 docker build --target=cicd -t $DOCKER_REPO/frontend:${IMAGE_TAG} . + docker tag $DOCKER_REPO/frontend:${IMAGE_TAG} $DOCKER_REPO/frontend:${IMAGE_TAG}-ee + docker push $DOCKER_REPO/frontend:${IMAGE_TAG} + docker push $DOCKER_REPO/frontend:${IMAGE_TAG}-ee + + - name: Deploy to kubernetes foss + run: | + cd scripts/helmcharts/ + + set -x + cat <>/tmp/image_override.yaml + frontend: + image: + tag: ${IMAGE_TAG} + EOF + + ## Update secerts + sed -i "s#openReplayContainerRegistry.*#openReplayContainerRegistry: \"${{ secrets.OSS_REGISTRY_URL }}\"#g" vars.yaml + sed -i "s/postgresqlPassword: \"changeMePassword\"/postgresqlPassword: \"${{ secrets.DEV_PG_PASSWORD }}\"/g" vars.yaml + sed -i "s/accessKey: \"changeMeMinioAccessKey\"/accessKey: \"${{ secrets.DEV_MINIO_ACCESS_KEY }}\"/g" vars.yaml + sed -i "s/secretKey: \"changeMeMinioPassword\"/secretKey: \"${{ secrets.DEV_MINIO_SECRET_KEY }}\"/g" vars.yaml + sed -i "s/jwt_secret: \"SetARandomStringHere\"/jwt_secret: \"${{ secrets.DEV_JWT_SECRET }}\"/g" vars.yaml + sed -i "s/domainName: \"\"/domainName: \"${{ secrets.DEV_DOMAIN_NAME }}\"/g" vars.yaml + + # Update changed image tag + sed -i "/frontend/{n;n;s/.*/ tag: ${IMAGE_TAG}/}" /tmp/image_override.yaml + + cat /tmp/image_override.yaml + # Deploy command + mv openreplay/charts/{ingress-nginx,frontend,quickwit} /tmp + rm -rf openreplay/charts/* + mv /tmp/{ingress-nginx,frontend,quickwit} openreplay/charts/ + helm template openreplay -n app openreplay -f vars.yaml -f /tmp/image_override.yaml --set ingress-nginx.enabled=false --set skipMigration=true --no-hooks | kubectl apply -n app -f - + env: + DOCKER_REPO: ${{ secrets.OSS_REGISTRY_URL }} + IMAGE_TAG: ${{ github.ref_name }}_${{ github.sha }} diff --git a/.github/workflows/frontend.yaml b/.github/workflows/frontend.yaml index 2f2fd3989..8a9ab31a5 100644 --- a/.github/workflows/frontend.yaml +++ b/.github/workflows/frontend.yaml @@ -1,4 +1,4 @@ -name: Frontend FOSS Deployment +name: Frontend Dev Deployment on: workflow_dispatch: push: diff --git a/frontend/.storybook/config.js b/frontend/.storybook/config.DEPRECATED.js similarity index 90% rename from frontend/.storybook/config.js rename to frontend/.storybook/config.DEPRECATED.js index 1ff1f28d1..fad172b6f 100644 --- a/frontend/.storybook/config.js +++ b/frontend/.storybook/config.DEPRECATED.js @@ -2,13 +2,10 @@ import { configure, addDecorator } from '@storybook/react'; import { Provider } from 'react-redux'; import store from '../app/store'; import { MemoryRouter } from "react-router" -import { PlayerProvider } from '../app/player/store' const withProvider = (story) => ( - { story() } - ) @@ -33,4 +30,4 @@ configure( require.context('../app', true, /\.stories\.js$/), ], module -); \ No newline at end of file +); diff --git a/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx b/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx index 167db8281..29e4826cc 100644 --- a/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx +++ b/frontend/app/components/Assist/ChatWindow/ChatWindow.tsx @@ -6,7 +6,7 @@ import stl from './chatWindow.module.css'; import ChatControls from '../ChatControls/ChatControls'; import Draggable from 'react-draggable'; import type { LocalStream } from 'Player'; -import { toggleVideoLocalStream } from 'Player' +import { PlayerContext } from 'App/components/Session/playerContext'; export interface Props { incomeStream: MediaStream[] | null; @@ -17,6 +17,10 @@ export interface Props { } function ChatWindow({ userId, incomeStream, localStream, endCall, isPrestart }: Props) { + const { player } = React.useContext(PlayerContext) + + const toggleVideoLocalStream = player.assistManager.toggleVideoLocalStream; + const [localVideoEnabled, setLocalVideoEnabled] = useState(false); const [anyRemoteEnabled, setRemoteEnabled] = useState(false); diff --git a/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx b/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx index 6d702acdb..3c0fce497 100644 --- a/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx +++ b/frontend/app/components/Assist/RequestingWindow/RequestingWindow.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { INDEXES } from 'App/constants/zindex'; import { connect } from 'react-redux'; import { Button, Loader, Icon } from 'UI'; -import { initiateCallEnd, releaseRemoteControl } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; interface Props { userDisplayName: string; @@ -14,20 +14,38 @@ export enum WindowType { Control, } +enum Actions { + CallEnd, + ControlEnd +} + const WIN_VARIANTS = { [WindowType.Call]: { text: 'to accept the call', icon: 'call' as const, - action: initiateCallEnd, + action: Actions.CallEnd, }, [WindowType.Control]: { text: 'to accept remote control request', icon: 'remote-control' as const, - action: releaseRemoteControl, + action: Actions.ControlEnd, }, }; function RequestingWindow({ userDisplayName, type }: Props) { + const { player } = React.useContext(PlayerContext) + + const { + assistManager: { + initiateCallEnd, + releaseRemoteControl, + } + } = player + + const actions = { + [Actions.CallEnd]: initiateCallEnd, + [Actions.ControlEnd]: releaseRemoteControl + } return (
{WIN_VARIANTS[type].text} -
@@ -48,6 +66,6 @@ function RequestingWindow({ userDisplayName, type }: Props) { ); } -export default connect((state) => ({ +export default connect((state: any) => ({ userDisplayName: state.getIn(['sessions', 'current', 'userDisplayName']), }))(RequestingWindow); diff --git a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx index e377cd3ba..35afb6721 100644 --- a/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx +++ b/frontend/app/components/Assist/components/AssistActions/AssistActions.tsx @@ -3,15 +3,8 @@ import { Button, Tooltip } from 'UI'; import { connect } from 'react-redux'; import cn from 'classnames'; import { toggleChatWindow } from 'Duck/sessions'; -import { connectPlayer } from 'Player'; import ChatWindow from '../../ChatWindow'; -import { - callPeer, - setCallArgs, - requestReleaseRemoteControl, - toggleAnnotation, - toggleUserName, -} from 'Player'; +// state enums import { CallingState, ConnectionStatus, @@ -19,6 +12,8 @@ import { RequestLocalStream, } from 'Player'; import type { LocalStream } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; import { toast } from 'react-toastify'; import { confirm } from 'UI'; import stl from './AassistActions.module.css'; @@ -48,17 +43,31 @@ interface Props { function AssistActions({ userId, - calling, - annotating, - peerConnectionStatus, - remoteControlStatus, hasPermission, isEnterprise, isCallActive, agentIds, - livePlay, userDisplayName, }: Props) { + const { player, store } = React.useContext(PlayerContext) + + const { + assistManager: { + call: callPeer, + setCallArgs, + requestReleaseRemoteControl, + toggleAnnotation, + }, + toggleUserName, + } = player + const { + calling, + annotating, + peerConnectionStatus, + remoteControl: remoteControlStatus, + livePlay, + } = store.get() + const [isPrestart, setPrestart] = useState(false); const [incomeStream, setIncomeStream] = useState([]); const [localStream, setLocalStream] = useState(null); @@ -236,7 +245,7 @@ function AssistActions({ } const con = connect( - (state) => { + (state: any) => { const permissions = state.getIn(['user', 'account', 'permissions']) || []; return { hasPermission: permissions.includes('ASSIST_CALL'), @@ -248,11 +257,5 @@ const con = connect( ); export default con( - connectPlayer((state) => ({ - calling: state.calling, - annotating: state.annotating, - remoteControlStatus: state.remoteControl, - peerConnectionStatus: state.peerConnectionStatus, - livePlay: state.livePlay, - }))(AssistActions) + observer(AssistActions) ); diff --git a/frontend/app/components/Errors/List/List.js b/frontend/app/components/Errors/List/List.js index 9f379319a..8a22a1f2d 100644 --- a/frontend/app/components/Errors/List/List.js +++ b/frontend/app/components/Errors/List/List.js @@ -25,7 +25,7 @@ const sortOptions = Object.entries(sortOptionsMap) @connect(state => ({ loading: state.getIn([ "errors", "loading" ]), - resolveToggleLoading: state.getIn(["errors", "resolve", "loading"]) || + resolveToggleLoading: state.getIn(["errors", "resolve", "loading"]) || state.getIn(["errors", "unresolve", "loading"]), ignoreLoading: state.getIn([ "errors", "ignore", "loading" ]), mergeLoading: state.getIn([ "errors", "merge", "loading" ]), @@ -54,19 +54,19 @@ export default class List extends React.PureComponent { } this.debounceFetch = debounce(this.props.editOptions, 1000); } - + componentDidMount() { this.props.applyFilter({ }); } check = ({ errorId }) => { const { checkedIds } = this.state; - const newCheckedIds = checkedIds.contains(errorId) - ? checkedIds.remove(errorId) + const newCheckedIds = checkedIds.contains(errorId) + ? checkedIds.remove(errorId) : checkedIds.add(errorId); this.setState({ checkedAll: newCheckedIds.size === this.props.list.size, - checkedIds: newCheckedIds + checkedIds: newCheckedIds }); } @@ -184,7 +184,7 @@ export default class List extends React.PureComponent { onClick={ this.unresolve } disabled={ someLoading || currentCheckedIds.size === 0} /> - } + } { status !== IGNORED && - } + }
- Sort By + Sort By - - - - - No Data -
- } - size="small" - show={filtered.length === 0} - > - - {filtered.map((l) => ( - - ))} - - - - - - ); - } -} diff --git a/frontend/app/components/Session_/Console/ConsoleRow/ConsoleRow.tsx b/frontend/app/components/Session_/Console/ConsoleRow/ConsoleRow.tsx index 85457d6b1..4c0096697 100644 --- a/frontend/app/components/Session_/Console/ConsoleRow/ConsoleRow.tsx +++ b/frontend/app/components/Session_/Console/ConsoleRow/ConsoleRow.tsx @@ -41,7 +41,7 @@ function ConsoleRow(props: Props) { )} {renderWithNL(lines.pop())} - {canExpand && expanded && lines.map((l: any, i: number) =>
{l}
)} + {/* {canExpand && expanded && lines.map((l: any, i: number) =>
{l}
)} */} jump(log.time)} /> diff --git a/frontend/app/components/Session_/Console/ConsoleRow/index.ts b/frontend/app/components/Session_/Console/ConsoleRow/index.ts deleted file mode 100644 index c9140d748..000000000 --- a/frontend/app/components/Session_/Console/ConsoleRow/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ConsoleRow'; diff --git a/frontend/app/components/Session_/Console/console.module.css b/frontend/app/components/Session_/Console/console.module.css deleted file mode 100644 index 2da78f540..000000000 --- a/frontend/app/components/Session_/Console/console.module.css +++ /dev/null @@ -1,38 +0,0 @@ - -.message { - overflow-x: auto; - margin-left: 10px; - font-size: 13px; - overflow-x: auto; - &::-webkit-scrollbar { - height: 2px; - } -} - -.line { - font-family: 'Menlo', 'monaco', 'consolas', monospace; - /* margin-top: -1px; ??? */ - display: flex; - align-items: flex-start; - border-bottom: solid thin $gray-light-shade; - &:hover { - background-coor: $active-blue !important; - } -} - -.timestamp { - -} - -.activeRow { - background-color: $teal-light !important; -} - -.icon { - padding-top: 4px; - margin-right: 7px; -} - -.inactiveRow { - opacity: 0.5; -} \ No newline at end of file diff --git a/frontend/app/components/Session_/EventsBlock/EventSearch/EventSearch.js b/frontend/app/components/Session_/EventsBlock/EventSearch/EventSearch.js index f0c7efe06..3c3396e72 100644 --- a/frontend/app/components/Session_/EventsBlock/EventSearch/EventSearch.js +++ b/frontend/app/components/Session_/EventsBlock/EventSearch/EventSearch.js @@ -1,9 +1,13 @@ import React, { useEffect } from 'react' import { Input, Icon } from 'UI' -import { connectPlayer, toggleEvents, scale } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; function EventSearch(props) { - const { onChange, clearSearch, value, header, toggleEvents, setActiveTab } = props; + const { player } = React.useContext(PlayerContext) + + const { onChange, clearSearch, value, header, setActiveTab } = props; + + const toggleEvents = () => player.toggleEvents() useEffect(() => { return () => { @@ -42,4 +46,4 @@ function EventSearch(props) { ) } -export default connectPlayer(() => ({}), { toggleEvents })(EventSearch) +export default EventSearch diff --git a/frontend/app/components/Session_/EventsBlock/EventsBlock.js b/frontend/app/components/Session_/EventsBlock/EventsBlock.js index 69ba51996..b3d978347 100644 --- a/frontend/app/components/Session_/EventsBlock/EventsBlock.js +++ b/frontend/app/components/Session_/EventsBlock/EventsBlock.js @@ -141,6 +141,7 @@ export default class EventsBlock extends React.Component { eventsIndex, filterOutNote, } = this.props; + const { query } = this.state; const _events = this.eventsList const isLastEvent = index === _events.size - 1; diff --git a/frontend/app/components/Session_/EventsBlock/NoteEvent.tsx b/frontend/app/components/Session_/EventsBlock/NoteEvent.tsx index 08e5631a9..676b1f901 100644 --- a/frontend/app/components/Session_/EventsBlock/NoteEvent.tsx +++ b/frontend/app/components/Session_/EventsBlock/NoteEvent.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Icon } from 'UI'; -import { tagProps, iTag, Note } from 'App/services/NotesService'; +import { tagProps, Note } from 'App/services/NotesService'; import { formatTimeOrDate } from 'App/date'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; @@ -9,7 +9,6 @@ import copy from 'copy-to-clipboard'; import { toast } from 'react-toastify'; import { session } from 'App/routes'; import { confirm } from 'UI'; -import { filterOutNote as filterOutTimelineNote } from 'Player'; import { TeamBadge } from 'Shared/SessionListContainer/components/Notes'; interface Props { @@ -24,7 +23,6 @@ function NoteEvent(props: Props) { const { settingsStore, notesStore } = useStore(); const { timezone } = settingsStore.sessionSettings; - console.log(props.noEdit); const onEdit = () => { props.onEdit({ isVisible: true, @@ -60,7 +58,6 @@ function NoteEvent(props: Props) { ) { notesStore.deleteNote(props.note.noteId).then((r) => { props.filterOutNote(props.note.noteId); - filterOutTimelineNote(props.note.noteId); toast.success('Note deleted'); }); } diff --git a/frontend/app/components/Session_/Exceptions/Exceptions.js b/frontend/app/components/Session_/Exceptions/Exceptions.js deleted file mode 100644 index 334e57688..000000000 --- a/frontend/app/components/Session_/Exceptions/Exceptions.js +++ /dev/null @@ -1,159 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import { getRE } from 'App/utils'; -import { - NoContent, - Loader, - Input, - ErrorItem, - SlideModal, - ErrorDetails, - ErrorHeader, - Link, - QuestionMarkHint, - Tabs, -} from 'UI'; -import { fetchErrorStackList } from 'Duck/sessions'; -import { connectPlayer, jump } from 'Player'; -import { error as errorRoute } from 'App/routes'; -import Autoscroll from '../Autoscroll'; -import BottomBlock from '../BottomBlock'; - -@connectPlayer((state) => ({ - logs: state.logListNow, - exceptions: state.exceptionsList, - // exceptionsNow: state.exceptionsListNow, -})) -@connect( - (state) => ({ - session: state.getIn(['sessions', 'current']), - errorStack: state.getIn(['sessions', 'errorStack']), - sourcemapUploaded: state.getIn(['sessions', 'sourcemapUploaded']), - loading: state.getIn(['sessions', 'fetchErrorStackList', 'loading']), - }), - { fetchErrorStackList } -) -export default class Exceptions extends React.PureComponent { - state = { - filter: '', - currentError: null, - }; - - onFilterChange = ({ target: { value } }) => this.setState({ filter: value }); - - setCurrentError = (err) => { - const { session } = this.props; - this.props.fetchErrorStackList(session.sessionId, err.errorId); - this.setState({ currentError: err }); - }; - closeModal = () => this.setState({ currentError: null }); - - render() { - const { exceptions, loading, errorStack, sourcemapUploaded } = this.props; - const { filter, currentError } = this.state; - const filterRE = getRE(filter, 'i'); - - const filtered = exceptions.filter((e) => filterRE.test(e.name) || filterRE.test(e.message)); - - // let lastIndex = -1; - // filtered.forEach((item, index) => { - // if ( - // this.props.exceptionsNow.length > 0 && - // item.time <= this.props.exceptionsNow[this.props.exceptionsNow.length - 1].time - // ) { - // lastIndex = index; - // } - // }); - - return ( - <> - -
- - {currentError.name} - - {currentError.function} -
-
{currentError.message}
- - ) - } - isDisplayed={currentError != null} - content={ - currentError && ( -
- - - - - -
- ) - } - onClose={this.closeModal} - /> - - -
- Exceptions -
- -
- - - - Upload Source Maps{' '} - - and see source code context obtained from stack traces in their original form. - - } - /> -
-
- - - - {filtered.map((e, index) => ( - jump(e.time)} - error={e} - key={e.key} - // selected={lastIndex === index} - // inactive={index > lastIndex} - onErrorClick={(jsEvent) => { - jsEvent.stopPropagation(); - jsEvent.preventDefault(); - this.setCurrentError(e); - }} - /> - ))} - - - -
- - ); - } -} diff --git a/frontend/app/components/Session_/Exceptions/Exceptions.tsx b/frontend/app/components/Session_/Exceptions/Exceptions.tsx new file mode 100644 index 000000000..987d0f215 --- /dev/null +++ b/frontend/app/components/Session_/Exceptions/Exceptions.tsx @@ -0,0 +1,125 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { getRE } from 'App/utils'; +import { + NoContent, + Loader, + Input, + ErrorItem, + SlideModal, + ErrorDetails, + Link, + QuestionMarkHint, +} from 'UI'; +import { error as errorRoute } from 'App/routes'; +import Autoscroll from '../Autoscroll'; +import BottomBlock from '../BottomBlock'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; + +interface IProps { + loading: boolean; + sourcemapUploaded: boolean; + errorStack: Record; +} + +function Exceptions({ errorStack, sourcemapUploaded, loading }: IProps) { + const { player, store } = React.useContext(PlayerContext); + const { logListNow: logs, exceptionsList: exceptions } = store.get(); + const [filter, setFilter] = React.useState(''); + const [currentError, setCurrentErrorVal] = React.useState(null); + + const onFilterChange = ({ target: { value } }: any) => setFilter(value); + const closeModal = () => setCurrentErrorVal(null); + + const filterRE = getRE(filter, 'i'); + const filtered = exceptions.filter((e: any) => filterRE.test(e.name) || filterRE.test(e.message)); + + return ( + <> + +
+ + {currentError.name} + + {currentError.function} +
+
{currentError.message}
+ + ) + } + isDisplayed={currentError != null} + content={ + currentError && ( +
+ + + + + +
+ ) + } + onClose={closeModal} + /> + + +
+ Exceptions +
+ +
+ + + + Upload Source Maps{' '} + + and see source code context obtained from stack traces in their original form. + + } + /> +
+
+ + + + {filtered.map((e: any, index) => ( + + player.jump(e.time)} error={e} /> + + ))} + + + +
+ + ); +} + +export default connect((state: any) => ({ + errorStack: state.getIn(['sessions', 'errorStack']), + sourcemapUploaded: state.getIn(['sessions', 'sourcemapUploaded']), + loading: state.getIn(['sessions', 'fetchErrorStackList', 'loading']), +}))(observer(Exceptions)); diff --git a/frontend/app/components/Session_/Fetch/Fetch.js b/frontend/app/components/Session_/Fetch/Fetch.DEPRECATED.js similarity index 100% rename from frontend/app/components/Session_/Fetch/Fetch.js rename to frontend/app/components/Session_/Fetch/Fetch.DEPRECATED.js diff --git a/frontend/app/components/Session_/GraphQL/GQLDetails.js b/frontend/app/components/Session_/GraphQL/GQLDetails.js index 4caba50a7..73eadb3ab 100644 --- a/frontend/app/components/Session_/GraphQL/GQLDetails.js +++ b/frontend/app/components/Session_/GraphQL/GQLDetails.js @@ -6,8 +6,6 @@ export default class GQLDetails extends React.PureComponent { render() { const { gql: { variables, response, duration, operationKind, operationName }, - nextClick, - prevClick, first = false, last = false, } = this.props; @@ -57,15 +55,6 @@ export default class GQLDetails extends React.PureComponent { - -
- - -
); } diff --git a/frontend/app/components/Session_/GraphQL/GraphQL.js b/frontend/app/components/Session_/GraphQL/GraphQL.js deleted file mode 100644 index 8c601dba1..000000000 --- a/frontend/app/components/Session_/GraphQL/GraphQL.js +++ /dev/null @@ -1,178 +0,0 @@ -import React from 'react'; -import { NoContent, Input, SlideModal, CloseButton, Button } from 'UI'; -import { getRE } from 'App/utils'; -import { connectPlayer, pause, jump } from 'Player'; -import BottomBlock from '../BottomBlock'; -import TimeTable from '../TimeTable'; -import GQLDetails from './GQLDetails'; -import { renderStart } from 'Components/Session_/Network/NetworkContent'; - -function renderDefaultStatus() { - return '2xx-3xx'; -} - -export function renderName(r) { - return ( -
-
{r.operationName}
- -
- ); -} - -@connectPlayer((state) => ({ - list: state.graphqlList, - listNow: state.graphqlListNow, - time: state.time, - livePlay: state.livePlay, -})) -export default class GraphQL extends React.PureComponent { - state = { - filter: '', - filteredList: this.props.list, - filteredListNow: this.props.listNow, - current: null, - currentIndex: 0, - showFetchDetails: false, - hasNextError: false, - hasPreviousError: false, - lastActiveItem: 0, - }; - - static filterList(list, value) { - const filterRE = getRE(value, 'i'); - - return value - ? list.filter( - (r) => - filterRE.test(r.operationKind) || - filterRE.test(r.operationName) || - filterRE.test(r.variables) - ) - : list; - } - - onFilterChange = ({ target: { value } }) => { - const { list } = this.props; - const filtered = GraphQL.filterList(list, value); - this.setState({ filter: value, filteredList: filtered, currentIndex: 0 }); - }; - - setCurrent = (item, index) => { - if (!this.props.livePlay) { - pause(); - jump(item.time); - } - this.setState({ current: item, currentIndex: index }); - }; - - closeModal = () => this.setState({ current: null, showFetchDetails: false }); - - static getDerivedStateFromProps(nextProps, prevState) { - const { list } = nextProps; - if (nextProps.time) { - const filtered = GraphQL.filterList(list, prevState.filter); - let i = 0; - filtered.forEach((item, index) => { - if (item.time <= nextProps.time) { - i = index; - } - }); - - return { - lastActiveItem: i, - }; - } - } - - render() { - const { current, currentIndex, filteredList, lastActiveItem } = this.state; - - return ( - - -

GraphQL

-
- -
- - } - isDisplayed={current != null} - content={ - current && ( - - ) - } - onClose={this.closeModal} - /> - - - GraphQL -
- -
-
- - - - {[ - { - label: 'Start', - width: 90, - render: renderStart, - }, - { - label: 'Status', - width: 70, - render: renderDefaultStatus, - }, - { - label: 'Type', - dataKey: 'operationKind', - width: 60, - }, - { - label: 'Name', - width: 240, - render: renderName, - }, - ]} - - - -
-
- ); - } -} diff --git a/frontend/app/components/Session_/GraphQL/GraphQL.tsx b/frontend/app/components/Session_/GraphQL/GraphQL.tsx new file mode 100644 index 000000000..6f89a5ec1 --- /dev/null +++ b/frontend/app/components/Session_/GraphQL/GraphQL.tsx @@ -0,0 +1,174 @@ +import React, { useEffect } from 'react'; +import { NoContent, Input, SlideModal, CloseButton, Button } from 'UI'; +import { getRE } from 'App/utils'; +import BottomBlock from '../BottomBlock'; +import TimeTable from '../TimeTable'; +import GQLDetails from './GQLDetails'; +import { renderStart } from 'Components/Session_/Network/NetworkContent'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; + +function renderDefaultStatus() { + return '2xx-3xx'; +} + +export function renderName(r: Record) { + const { player } = React.useContext(PlayerContext); + + return ( +
+
{r.operationName}
+ +
+ ); +} + +function GraphQL() { + const { player, store } = React.useContext(PlayerContext); + + const { graphqlList: list, graphqlListNow: listNow, time, livePlay } = store.get(); + + const defaultState = { + filter: '', + filteredList: list, + filteredListNow: listNow, + // @ts-ignore + current: null, + currentIndex: 0, + showFetchDetails: false, + hasNextError: false, + hasPreviousError: false, + lastActiveItem: 0, + }; + + const [state, setState] = React.useState(defaultState); + + const filterList = (list: any, value: string) => { + const filterRE = getRE(value, 'i'); + + return value + ? list.filter( + (r: any) => + filterRE.test(r.operationKind) || + filterRE.test(r.operationName) || + filterRE.test(r.variables) + ) + : list; + }; + + const onFilterChange = ({ target: { value } }: React.ChangeEvent) => { + const filtered = filterList(list, value); + setState((prevState) => ({ + ...prevState, + filter: value, + filteredList: filtered, + currentIndex: 0, + })); + }; + + const setCurrent = (item: any, index: number) => { + if (!livePlay) { + player.pause(); + player.jump(item.time); + } + setState((prevState) => ({ ...prevState, current: item, currentIndex: index })); + }; + + const closeModal = () => + setState((prevState) => ({ ...prevState, current: null, showFetchDetails: false })); + + useEffect(() => { + const filtered = filterList(listNow, state.filter); + if (filtered.length !== lastActiveItem) { + setState((prevState) => ({ ...prevState, lastActiveItem: listNow.length })); + } + }, [time]); + + const { current, currentIndex, filteredList, lastActiveItem } = state; + + return ( + + +

GraphQL

+
+ +
+ + } + isDisplayed={current != null} + content={ + current && ( + + ) + } + onClose={closeModal} + /> + + + GraphQL +
+ +
+
+ + + + {[ + { + label: 'Start', + width: 90, + render: renderStart, + }, + { + label: 'Status', + width: 70, + render: renderDefaultStatus, + }, + { + label: 'Type', + dataKey: 'operationKind', + width: 60, + }, + { + label: 'Name', + width: 240, + render: renderName, + }, + ]} + + + +
+
+ ); +} + +export default observer(GraphQL); diff --git a/frontend/app/components/Session_/Inspector/index.js b/frontend/app/components/Session_/Inspector/index.js index f76834fee..1f13b847f 100644 --- a/frontend/app/components/Session_/Inspector/index.js +++ b/frontend/app/components/Session_/Inspector/index.js @@ -1,27 +1,27 @@ import React, { useEffect, useState, useRef } from 'react'; -import { toggleInspectorMode, markElement } from 'Player'; import ElementView from './ElementView'; import BottomBlock from '../BottomBlock'; -import stl from './inspector.module.css' +import stl from './inspector.module.css'; +import { PlayerContext } from 'App/components/Session/playerContext'; // TODO: refactor: use Layout from the Sessions and put everything there under the WebPlayer folder -// function onMount(element, setOpen) { // TODO: through the MobX -// element.setOpen = setOpen; -// } +export default function Inspector() { + const { player } = React.useContext(PlayerContext); + const toggleInspectorMode = player.toggleInspectorMode; + const markElement = player.mark; -export default function Inspector () { const [doc, setDoc] = useState(null); const [openChain, setOpenChain] = useState([]); const [selectedElement, _setSelectedElement] = useState(null); const selectedElementRef = useRef(selectedElement); - const setSelectedElement = elem => { + const setSelectedElement = (elem) => { selectedElementRef.current = elem; _setSelectedElement(elem); - } + }; - useEffect(() => { + useEffect(() => { const doc = toggleInspectorMode(true, ({ target }) => { const openChain = []; let currentTarget = target; @@ -33,9 +33,9 @@ export default function Inspector () { setSelectedElement(target); }); setDoc(doc); - setOpenChain([ doc.documentElement ]); + setOpenChain([doc.documentElement]); - const onKeyPress = e => { + const onKeyPress = (e) => { if (e.key === 'Backspace' || e.key === 'Delete') { const elem = selectedElementRef.current; if (elem !== null && elem.parentElement !== null) { @@ -43,30 +43,30 @@ export default function Inspector () { setSelectedElement(null); } } - } - window.addEventListener("keydown", onKeyPress); + }; + window.addEventListener('keydown', onKeyPress); return () => { toggleInspectorMode(false); - window.removeEventListener("keydown", onKeyPress); - } + window.removeEventListener('keydown', onKeyPress); + }; }, []); - if (!doc) return null; - return ( - + if (!doc) return null; + return ( + -
markElement(null) } className={stl.wrapper}> - markElement(null)} className={stl.wrapper}> + markElement(e)} />
- ); -} \ No newline at end of file + ); +} diff --git a/frontend/app/components/Session_/LongTasks/LongTasks.js b/frontend/app/components/Session_/LongTasks/LongTasks.DEPRECATED.js similarity index 95% rename from frontend/app/components/Session_/LongTasks/LongTasks.js rename to frontend/app/components/Session_/LongTasks/LongTasks.DEPRECATED.js index fd3b4cc17..2fa80fd01 100644 --- a/frontend/app/components/Session_/LongTasks/LongTasks.js +++ b/frontend/app/components/Session_/LongTasks/LongTasks.DEPRECATED.js @@ -40,12 +40,12 @@ export default class GraphQL extends React.PureComponent { const { filter, current } = this.state; const filterRE = getRE(filter, 'i'); const filtered = list - .filter(({ containerType, context, containerName = "", containerId = "", containerSrc="" }) => - filterRE.test(containerName) || + .filter(({ containerType, context, containerName = "", containerId = "", containerSrc="" }) => + filterRE.test(containerName) || filterRE.test(containerId) || filterRE.test(containerSrc) || filterRE.test(CONTEXTS[ context ]) || - filterRE.test(CONTAINER_TYPES[ containerType ])); + filterRE.test(CONTAINER_TYPES[ containerType ])); const lastIndex = filtered.filter(item => item.time <= time).length - 1; return ( @@ -64,7 +64,7 @@ export default class GraphQL extends React.PureComponent { - Learn more + Learn more about Long Tasks API } diff --git a/frontend/app/components/Session_/LongTasks/index.js b/frontend/app/components/Session_/LongTasks/index.js index 521f0e2a7..ace8262a0 100644 --- a/frontend/app/components/Session_/LongTasks/index.js +++ b/frontend/app/components/Session_/LongTasks/index.js @@ -1 +1 @@ -export { default } from './LongTasks'; +export { default } from './LongTasks.DEPRECATED'; diff --git a/frontend/app/components/Session_/Network/Network.js b/frontend/app/components/Session_/Network/Network.DEPRECATED.js similarity index 97% rename from frontend/app/components/Session_/Network/Network.js rename to frontend/app/components/Session_/Network/Network.DEPRECATED.js index f824cba09..ce09e94d2 100644 --- a/frontend/app/components/Session_/Network/Network.js +++ b/frontend/app/components/Session_/Network/Network.DEPRECATED.js @@ -1,7 +1,7 @@ import React from 'react'; import cn from 'classnames'; -import { connectPlayer, jump, pause } from 'Player'; -import { Tooltip, Button, TextEllipsis } from 'UI'; +import { connectPlayer, } from 'Player'; +import { Tooltip, TextEllipsis } from 'UI'; import { getRE } from 'App/utils'; import { TYPES } from 'Types/session/resource'; import stl from './network.module.css'; diff --git a/frontend/app/components/Session_/Network/NetworkContent.js b/frontend/app/components/Session_/Network/NetworkContent.js index 5f335dbf3..2891a0597 100644 --- a/frontend/app/components/Session_/Network/NetworkContent.js +++ b/frontend/app/components/Session_/Network/NetworkContent.js @@ -1,6 +1,5 @@ import React from 'react'; import cn from 'classnames'; -// import { connectPlayer } from 'Player'; import { QuestionMarkHint, Tooltip, Tabs, Input, NoContent, Icon, Toggler, Button } from 'UI'; import { getRE } from 'App/utils'; import { TYPES } from 'Types/session/resource'; @@ -12,7 +11,6 @@ import BottomBlock from '../BottomBlock'; import InfoLine from '../BottomBlock/InfoLine'; import stl from './network.module.css'; import { Duration } from 'luxon'; -import { jump } from 'Player'; const ALL = 'ALL'; const XHR = 'xhr'; @@ -112,8 +110,6 @@ function renderSize(r) { content = 'Not captured'; } else { const headerSize = r.headerSize || 0; - const encodedSize = r.encodedBodySize || 0; - const transferred = headerSize + encodedSize; const showTransferred = r.headerSize != null; triggerText = formatBytes(r.decodedBodySize); @@ -234,7 +230,6 @@ export default class NetworkContent extends React.PureComponent { className="input-small" placeholder="Filter by name" icon="search" - iconPosition="left" name="filter" onChange={this.onFilterChange} height={28} diff --git a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx index de3cbdf5f..ef8598ddc 100644 --- a/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx +++ b/frontend/app/components/Session_/OverviewPanel/OverviewPanel.tsx @@ -1,6 +1,5 @@ -import { connectPlayer } from 'Player'; -import { toggleBottomBlock } from 'Duck/components/player'; import React, { useEffect } from 'react'; +import { toggleBottomBlock } from 'Duck/components/player'; import BottomBlock from '../BottomBlock'; import EventRow from './components/EventRow'; import { TYPES } from 'Types/session/event'; @@ -10,40 +9,39 @@ import FeatureSelection, { HELP_MESSAGE } from './components/FeatureSelection/Fe import TimelinePointer from './components/TimelinePointer'; import VerticalPointerLine from './components/VerticalPointerLine'; import cn from 'classnames'; -// import VerticalLine from './components/VerticalLine'; import OverviewPanelContainer from './components/OverviewPanelContainer'; import { NoContent, Icon } from 'UI'; +import { observer } from 'mobx-react-lite'; +import { PlayerContext } from 'App/components/Session/playerContext'; -interface Props { - resourceList: any[]; - exceptionsList: any[]; - eventsList: any[]; - toggleBottomBlock: any; - stackEventList: any[]; - issuesList: any[]; - performanceChartData: any; - endTime: number; - fetchPresented?: boolean; -} -function OverviewPanel(props: Props) { - const { fetchPresented = false } = props; +function OverviewPanel({ issuesList }: { issuesList: Record[] }) { + const { store } = React.useContext(PlayerContext) const [dataLoaded, setDataLoaded] = React.useState(false); const [selectedFeatures, setSelectedFeatures] = React.useState([ 'PERFORMANCE', 'ERRORS', - // 'EVENTS', 'NETWORK', ]); - const resources: any = React.useMemo(() => { const { - resourceList, - exceptionsList, - eventsList, - stackEventList, - issuesList, + endTime, performanceChartData, - } = props; + stackList: stackEventList, + eventList: eventsList, + exceptionsList, + resourceList: resourceListUnmap, + fetchList, + graphqlList, + } = store.get() + + const fetchPresented = fetchList.length > 0; + + const resourceList = resourceListUnmap + .filter((r: any) => r.isRed() || r.isYellow()) + .concat(fetchList.filter((i: any) => parseInt(i.status) >= 400)) + .concat(graphqlList.filter((i: any) => parseInt(i.status) >= 400)) + + const resources: any = React.useMemo(() => { return { NETWORK: resourceList, ERRORS: exceptionsList, @@ -59,25 +57,25 @@ function OverviewPanel(props: Props) { } if ( - props.resourceList.length > 0 || - props.exceptionsList.length > 0 || - props.eventsList.length > 0 || - props.stackEventList.length > 0 || - props.issuesList.length > 0 || - props.performanceChartData.length > 0 + resourceList.length > 0 || + exceptionsList.length > 0 || + eventsList.length > 0 || + stackEventList.length > 0 || + issuesList.length > 0 || + performanceChartData.length > 0 ) { setDataLoaded(true); } }, [ - props.resourceList, - props.exceptionsList, - props.eventsList, - props.stackEventList, - props.performanceChartData, + resourceList, + issuesList, + exceptionsList, + eventsList, + stackEventList, + performanceChartData, ]); return ( - X-RAY @@ -86,9 +84,10 @@ function OverviewPanel(props: Props) { - - + +
@@ -118,7 +117,7 @@ function OverviewPanel(props: Props) { fetchPresented={fetchPresented} /> )} - endTime={props.endTime} + endTime={endTime} message={HELP_MESSAGE[feature]} />
@@ -128,7 +127,6 @@ function OverviewPanel(props: Props) {
-
); } @@ -140,20 +138,5 @@ export default connect( toggleBottomBlock, } )( - connectPlayer((state: any) => ({ - fetchPresented: state.fetchList.length > 0, - resourceList: state.resourceList - .filter((r: any) => r.isRed() || r.isYellow()) - .concat(state.fetchList.filter((i: any) => parseInt(i.status) >= 400)) - .concat(state.graphqlList.filter((i: any) => parseInt(i.status) >= 400)), - exceptionsList: state.exceptionsList, - eventsList: state.eventList, - stackEventList: state.stackList, - performanceChartData: state.performanceChartData, - endTime: state.endTime, - }))(OverviewPanel) -); - -const Wrapper = React.memo((props: any) => { - return <>{props.children}; -}); + observer(OverviewPanel) +) diff --git a/frontend/app/components/Session_/OverviewPanel/components/OverviewPanelContainer/OverviewPanelContainer.tsx b/frontend/app/components/Session_/OverviewPanel/components/OverviewPanelContainer/OverviewPanelContainer.tsx index e017fe1db..8b802953d 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/OverviewPanelContainer/OverviewPanelContainer.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/OverviewPanelContainer/OverviewPanelContainer.tsx @@ -1,48 +1,33 @@ import React from 'react'; import VerticalLine from '../VerticalLine'; -import { connectPlayer, Controls } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; + interface Props { - children: React.ReactNode; - endTime: number; + children: React.ReactNode; + endTime: number; } const OverviewPanelContainer = React.memo((props: Props) => { - const { endTime } = props; - const [mouseX, setMouseX] = React.useState(0); - const [mouseIn, setMouseIn] = React.useState(false); - const onClickTrack = (e: any) => { - const p = e.nativeEvent.offsetX / e.target.offsetWidth; - const time = Math.max(Math.round(p * endTime), 0); - if (time) { - Controls.jump(time); - } - }; + const { player } = React.useContext(PlayerContext) - // const onMouseMoveCapture = (e: any) => { - // if (!mouseIn) { - // return; - // } - // const p = e.nativeEvent.offsetX / e.target.offsetWidth; - // setMouseX(p * 100); - // }; + const { endTime } = props; + const [mouseX, setMouseX] = React.useState(0); + const [mouseIn, setMouseIn] = React.useState(false); + const onClickTrack = (e: any) => { + const p = e.nativeEvent.offsetX / e.target.offsetWidth; + const time = Math.max(Math.round(p * endTime), 0); + if (time) { + player.jump(time); + } + }; - return ( -
setMouseIn(true)} - // onMouseOut={() => setMouseIn(false)} - > - {mouseIn && } -
{props.children}
-
- ); + return ( +
+ {mouseIn && } +
{props.children}
+
+ ); }); export default OverviewPanelContainer; - -// export default connectPlayer((state: any) => ({ -// endTime: state.endTime, -// }))(OverviewPanelContainer); diff --git a/frontend/app/components/Session_/OverviewPanel/components/PerformanceGraph/PerformanceGraph.tsx b/frontend/app/components/Session_/OverviewPanel/components/PerformanceGraph/PerformanceGraph.tsx index 8da42f303..2e719f377 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/PerformanceGraph/PerformanceGraph.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/PerformanceGraph/PerformanceGraph.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import { connectPlayer } from 'Player'; -import { AreaChart, Area, Tooltip, ResponsiveContainer } from 'recharts'; +import { AreaChart, Area, ResponsiveContainer } from 'recharts'; interface Props { list: any; diff --git a/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx b/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx index 5b6434794..206832329 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/TimelinePointer/TimelinePointer.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { Controls } from 'Player'; import { NETWORK, EXCEPTIONS } from 'Duck/components/player'; import { useModal } from 'App/components/Modal'; import { Icon, Tooltip } from 'UI'; @@ -7,6 +6,7 @@ import StackEventModal from '../StackEventModal'; import ErrorDetailsModal from 'App/components/Dashboard/components/Errors/ErrorDetailsModal'; import FetchDetails from 'Shared/FetchDetailsModal'; import GraphQLDetailsModal from 'Shared/GraphQLDetailsModal'; +import { PlayerContext } from 'App/components/Session/playerContext'; interface Props { pointer: any; @@ -15,11 +15,13 @@ interface Props { fetchPresented?: boolean; } const TimelinePointer = React.memo((props: Props) => { + const { player } = React.useContext(PlayerContext) + const { showModal } = useModal(); const createEventClickHandler = (pointer: any, type: any) => (e: any) => { if (props.noClick) return; e.stopPropagation(); - Controls.jump(pointer.time); + player.jump(pointer.time); if (!type) { return; } diff --git a/frontend/app/components/Session_/OverviewPanel/components/TimelineScale/TimelineScale.tsx b/frontend/app/components/Session_/OverviewPanel/components/TimelineScale/TimelineScale.tsx index 3905f4538..f8a8ec2f1 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/TimelineScale/TimelineScale.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/TimelineScale/TimelineScale.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { connectPlayer } from 'Player'; import { millisToMinutesAndSeconds } from 'App/utils'; interface Props { @@ -17,9 +16,6 @@ function TimelineScale(props: Props) { for (var i = 0; i < part; i++) { const txt = millisToMinutesAndSeconds(i * (endTime / part)); const el = document.createElement('div'); - // el.style.height = '10px'; - // el.style.width = '1px'; - // el.style.backgroundColor = '#ccc'; el.style.position = 'absolute'; el.style.left = `${i * gap}px`; el.style.paddingTop = '1px'; @@ -38,23 +34,11 @@ function TimelineScale(props: Props) { } drawScale(scaleRef.current); - - // const resize = () => drawScale(scaleRef.current); - - // window.addEventListener('resize', resize); - // return () => { - // window.removeEventListener('resize', resize); - // }; }, [scaleRef]); return (
- {/*
*/}
); } export default TimelineScale; - -// export default connectPlayer((state: any) => ({ -// endTime: state.endTime, -// }))(TimelineScale); diff --git a/frontend/app/components/Session_/OverviewPanel/components/VerticalPointerLine/VerticalPointerLine.tsx b/frontend/app/components/Session_/OverviewPanel/components/VerticalPointerLine/VerticalPointerLine.tsx index 688e8364c..5f815efa6 100644 --- a/frontend/app/components/Session_/OverviewPanel/components/VerticalPointerLine/VerticalPointerLine.tsx +++ b/frontend/app/components/Session_/OverviewPanel/components/VerticalPointerLine/VerticalPointerLine.tsx @@ -1,18 +1,16 @@ import React from 'react'; -import { connectPlayer } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; import VerticalLine from '../VerticalLine'; -interface Props { - time?: number; - scale?: number; -} -function VerticalPointerLine(props: Props) { - const { time, scale } = props; +function VerticalPointerLine() { + const { store } = React.useContext(PlayerContext) + + const { time, endTime } = store.get(); + const scale = 100 / endTime; + const left = time * scale; return ; } -export default connectPlayer((state: any) => ({ - time: state.time, - scale: 100 / state.endTime, -}))(VerticalPointerLine); +export default observer(VerticalPointerLine); diff --git a/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx b/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx index 03d74a247..e1ba5ccba 100644 --- a/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx +++ b/frontend/app/components/Session_/PageInsightsPanel/PageInsightsPanel.tsx @@ -3,7 +3,8 @@ import { Loader, Icon } from 'UI'; import { connect } from 'react-redux'; import { fetchInsights } from 'Duck/sessions'; import SelectorsList from './components/SelectorsList/SelectorsList'; -import { markTargets, Controls as Player } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; + import Select from 'Shared/Select'; import SelectDateRange from 'Shared/SelectDateRange'; import Period from 'Types/app/period'; @@ -22,6 +23,9 @@ interface Props { } function PageInsightsPanel({ filters, fetchInsights, events = [], insights, urlOptions, host, loading = true, setActiveTab }: Props) { + const { player: Player } = React.useContext(PlayerContext) + const markTargets = (t: any) => Player.markTargets(t) + const [insightsFilters, setInsightsFilters] = useState(filters); const defaultValue = urlOptions && urlOptions[0] ? urlOptions[0].value : ''; diff --git a/frontend/app/components/Session_/PageInsightsPanel/components/SelectorCard/SelectorCard.tsx b/frontend/app/components/Session_/PageInsightsPanel/components/SelectorCard/SelectorCard.tsx index 999dae866..39de770b7 100644 --- a/frontend/app/components/Session_/PageInsightsPanel/components/SelectorCard/SelectorCard.tsx +++ b/frontend/app/components/Session_/PageInsightsPanel/components/SelectorCard/SelectorCard.tsx @@ -1,9 +1,9 @@ -import React, { useState } from 'react'; +import React from 'react'; import stl from './SelectorCard.module.css'; import cn from 'classnames'; import type { MarkedTarget } from 'Player'; -import { activeTarget } from 'Player'; import { Tooltip } from 'react-tippy'; +import { PlayerContext } from 'App/components/Session/playerContext'; interface Props { index?: number; @@ -12,7 +12,11 @@ interface Props { } export default function SelectorCard({ index = 1, target, showContent }: Props) { + const { player } = React.useContext(PlayerContext) + const activeTarget = player.setActiveTarget + return ( + // @ts-ignore TODO for Alex
activeTarget(index)}>
{/* @ts-ignore */} diff --git a/frontend/app/components/Session_/PageInsightsPanel/components/SelectorsList/SelectorsList.tsx b/frontend/app/components/Session_/PageInsightsPanel/components/SelectorsList/SelectorsList.tsx index 14080e718..fabdca831 100644 --- a/frontend/app/components/Session_/PageInsightsPanel/components/SelectorsList/SelectorsList.tsx +++ b/frontend/app/components/Session_/PageInsightsPanel/components/SelectorsList/SelectorsList.tsx @@ -1,16 +1,15 @@ import React from 'react'; import { NoContent } from 'UI'; -import { connectPlayer } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; import SelectorCard from '../SelectorCard/SelectorCard'; -import type { MarkedTarget } from 'Player'; import stl from './selectorList.module.css'; -interface Props { - targets: Array; - activeTargetIndex: number; -} +function SelectorsList() { + const { store } = React.useContext(PlayerContext) + + const { markedTargets: targets, activeTargetIndex } = store.get() -function SelectorsList({ targets, activeTargetIndex }: Props) { return (
@@ -20,7 +19,4 @@ function SelectorsList({ targets, activeTargetIndex }: Props) { ); } -export default connectPlayer((state: any) => ({ - targets: state.markedTargets, - activeTargetIndex: state.activeTargetIndex, -}))(SelectorsList); +export default observer(SelectorsList); diff --git a/frontend/app/components/Session_/Performance/Performance.tsx b/frontend/app/components/Session_/Performance/Performance.tsx index 994401141..eca011fcb 100644 --- a/frontend/app/components/Session_/Performance/Performance.tsx +++ b/frontend/app/components/Session_/Performance/Performance.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; -import { Controls as PlayerControls, connectPlayer } from 'Player'; +import { PlayerContext } from 'App/components/Session/playerContext'; +import { observer } from 'mobx-react-lite'; import { AreaChart, Area, @@ -11,10 +12,8 @@ import { Tooltip, ResponsiveContainer, ReferenceLine, - CartesianGrid, Label, } from 'recharts'; -import { Checkbox } from 'UI'; import { durationFromMsFormatted } from 'App/date'; import { formatBytes } from 'App/utils'; @@ -174,362 +173,324 @@ function addFpsMetadata(data) { }); } -@connect((state) => ({ - userDeviceHeapSize: state.getIn(['sessions', 'current', 'userDeviceHeapSize']), - userDeviceMemorySize: state.getIn(['sessions', 'current', 'userDeviceMemorySize']), -})) -export default class Performance extends React.PureComponent { - _timeTicks = generateTicks(this.props.performanceChartData); - _data = addFpsMetadata(this.props.performanceChartData); - // state = { - // totalHeap: false, - // usedHeap: true, - // fps: true, - // } - // onCheckboxClick = (e, { name, checked }) => this.setState({ [ name ]: checked }) +function Performance({ + userDeviceHeapSize, +}: { + userDeviceHeapSize: number; +}) { + const { player, store } = React.useContext(PlayerContext); + const [_timeTicks, setTicks] = React.useState([]) + const [_data, setData] = React.useState([]) - onDotClick = ({ index }) => { - const point = this._data[index]; + const { + performanceChartTime, + performanceChartData, + connType, + connBandwidth, + performanceAvaliability: avaliability, + } = store.get(); + + React.useState(() => { + setTicks(generateTicks(performanceChartData)); + setData(addFpsMetadata(performanceChartData)); + }) + + + const onDotClick = ({ index: pointer }: { index: number }) => { + const point = _data[pointer]; if (!!point) { - PlayerControls.jump(point.time); + player.jump(point.time); } }; - onChartClick = (e) => { + const onChartClick = (e: any) => { if (e === null) return; const { activeTooltipIndex } = e; - const point = this._data[activeTooltipIndex]; + const point = _data[activeTooltipIndex]; if (!!point) { - PlayerControls.jump(point.time); + player.jump(point.time); } }; - render() { - const { - userDeviceHeapSize, - userDeviceMemorySize, - connType, - connBandwidth, - performanceChartTime, - avaliability = {}, - } = this.props; - const { fps, cpu, heap, nodes } = avaliability; - const avaliableCount = [fps, cpu, heap, nodes].reduce((c, av) => (av ? c + 1 : c), 0); - const height = avaliableCount === 0 ? '0' : `${100 / avaliableCount}%`; + const { fps, cpu, heap, nodes } = avaliability; + const avaliableCount = [fps, cpu, heap, nodes].reduce((c, av) => (av ? c + 1 : c), 0); + const height = avaliableCount === 0 ? '0' : `${100 / avaliableCount}%`; - return ( - - -
-
Performance
- - - {/* */} - - = 1000 ? `${connBandwidth / 1000} Mbps` : `${connBandwidth} Kbps` - } - display={connBandwidth != null} - /> - -
-
- - {fps && ( - - + +
+
Performance
+ + + + = 1000 ? `${connBandwidth / 1000} Mbps` : `${connBandwidth} Kbps` + } + display={connBandwidth != null} + /> + +
+
+ + {fps && ( + + + + + + - - - - {/* */} - {/* */} - - - - {/* */} - - - - - {/* */} - - - - - )} - {cpu && ( - - + + + + + + + + + + + )} + {cpu && ( + + + + + + {/* */} + ''} + domain={[0, 'dataMax']} + ticks={_timeTicks} > - - - - {/* */} - ''} - domain={[0, 'dataMax']} - ticks={this._timeTicks} - > - - - - - - - - - )} +
+
+ )} - {heap && ( - - + + + + + ''} // tick={false} + _timeTicks to cartesian array + domain={[0, 'dataMax']} + ticks={_timeTicks} > - - - - {/* */} - ''} // tick={false} + this._timeTicks to cartesian array - domain={[0, 'dataMax']} - ticks={this._timeTicks} - > - - max * 1.2]} - /> - - - - - - - )} - {nodes && ( - - + + max * 1.2]} + /> + + + + + + + )} + {nodes && ( + + + + + + ''} + domain={[0, 'dataMax']} + ticks={_timeTicks} > - - - - {/* */} - ''} - domain={[0, 'dataMax']} - ticks={this._timeTicks} - > - - max * 1.2]} - /> - - - - - - )} -
-
- ); - } +