diff --git a/frontend/app/components/Client/Client.js b/frontend/app/components/Client/Client.js index ab16c6b40..6cba4b50a 100644 --- a/frontend/app/components/Client/Client.js +++ b/frontend/app/components/Client/Client.js @@ -18,41 +18,45 @@ import Roles from './Roles'; @withRouter export default class Client extends React.PureComponent { - constructor(props){ + constructor(props) { super(props); - } + } setTab = (tab) => { this.props.history.push(clientRoute(tab)); - } + }; renderActiveTab = () => ( - - - - - - - - - - + + + + + + + + + + - ) + ); render() { - const { match: { params: { activeTab } } } = this.props; + const { + match: { + params: { activeTab }, + }, + } = this.props; return ( -
-
-
+
+
-
- { activeTab && this.renderActiveTab() } +
+
+ {activeTab && this.renderActiveTab()} +
-
); } diff --git a/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx b/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx index a4551f854..efc275e61 100644 --- a/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx +++ b/frontend/app/components/Dashboard/components/DashboardList/DashboardList.tsx @@ -9,7 +9,7 @@ import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; function DashboardList() { const { dashboardStore } = useStore(); const list = dashboardStore.filteredList; - const dashboardsSearch = dashboardStore.dashboardsSearch; + const dashboardsSearch = dashboardStore.filter.query; const lenth = list.length; return ( @@ -18,17 +18,11 @@ function DashboardList() { title={
-
- {dashboardsSearch !== '' ? ( - 'No matching results' - ) : ( -
-
You haven't created any dashboards yet
-
- A Dashboard is a collection of Cards that can be shared across teams. -
-
- )} +
+ {dashboardsSearch !== '' ? 'No matching results' : "You haven't created any dashboards yet"} +
+
+ A Dashboard is a collection of Cards that can be shared across teams.
} diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index 84fb770a8..1d25974cf 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -5,9 +5,9 @@ import SaveFilterButton from 'Shared/SaveFilterButton'; import { connect } from 'react-redux'; import { Button } from 'UI'; import { edit, addFilter, fetchSessions, updateFilter } from 'Duck/search'; -import SessionSearchQueryParamHandler from 'Shared/SessionSearchQueryParamHandler'; import { debounce } from 'App/utils'; +import useSessionSearchQueryHandler from 'App/hooks/useSessionSearchQueryHandler'; let debounceFetch: any = () => {} @@ -24,6 +24,9 @@ function SessionSearch(props: Props) { const { appliedFilter, saveRequestPayloads = false, metaLoading } = props; const hasEvents = appliedFilter.filters.filter((i: any) => i.isEvent).size > 0; const hasFilters = appliedFilter.filters.filter((i: any) => !i.isEvent).size > 0; + + useSessionSearchQueryHandler({ appliedFilter, applyFilter: props.updateFilter }); + useEffect(() => { debounceFetch = debounce(() => props.fetchSessions(), 500); }, []) @@ -71,7 +74,6 @@ function SessionSearch(props: Props) { return !metaLoading && ( <> - {hasEvents || hasFilters ? (
diff --git a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx index 84745c8ba..64904e358 100644 --- a/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx +++ b/frontend/app/components/shared/SessionSearchField/SessionSearchField.tsx @@ -15,7 +15,6 @@ interface Props { fetchFilterSearch: (query: any) => void; addFilterByKeyAndValue: (key: string, value: string) => void; liveAddFilterByKeyAndValue: (key: string, value: string) => void; - filterSearchList: any; liveFetchFilterSearch: any; } function SessionSearchField(props: Props) { diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx deleted file mode 100644 index e38729b2a..000000000 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useEffect } from 'react'; -import { useHistory } from 'react-router'; -import { connect } from 'react-redux'; -import { addFilterByKeyAndValue, addFilter } from 'Duck/search'; -import { updateFilter } from 'Duck/search'; -import { createUrlQuery, getFiltersFromQuery } from 'App/utils/search'; - -interface Props { - appliedFilter: any; - updateFilter: any; - addFilterByKeyAndValue: typeof addFilterByKeyAndValue; - addFilter: typeof addFilter; -} -const SessionSearchQueryParamHandler = (props: Props) => { - const { appliedFilter } = props; - const history = useHistory(); - - const applyFilterFromQuery = () => { - const filter = getFiltersFromQuery(history.location.search, appliedFilter); - props.updateFilter(filter, true, false); - }; - - const generateUrlQuery = () => { - const search: any = createUrlQuery(appliedFilter); - history.replace({ search }); - }; - - useEffect(applyFilterFromQuery, []); - useEffect(generateUrlQuery, [appliedFilter]); - - return <>; -}; - -export default connect( - (state: any) => ({ - appliedFilter: state.getIn(['search', 'instance']), - }), - { addFilterByKeyAndValue, addFilter, updateFilter } -)(SessionSearchQueryParamHandler); diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts b/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts deleted file mode 100644 index c13bb493d..000000000 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './SessionSearchQueryParamHandler'; \ No newline at end of file diff --git a/frontend/app/hooks/useSessionSearchQueryHandler.ts b/frontend/app/hooks/useSessionSearchQueryHandler.ts new file mode 100644 index 000000000..40b1e80cb --- /dev/null +++ b/frontend/app/hooks/useSessionSearchQueryHandler.ts @@ -0,0 +1,35 @@ +import { useEffect } from 'react'; +import { useHistory } from 'react-router'; +import { createUrlQuery, getFiltersFromQuery } from 'App/utils/search'; + +interface Props { + appliedFilter: any; + applyFilter: any; +} + +const useSessionSearchQueryHandler = (props: Props) => { + const { appliedFilter, applyFilter } = props; + const history = useHistory(); + + useEffect(() => { + const applyFilterFromQuery = () => { + const filter = getFiltersFromQuery(history.location.search, appliedFilter); + applyFilter(filter, true, false); + }; + + applyFilterFromQuery(); + }, []); + + useEffect(() => { + const generateUrlQuery = () => { + const search: any = createUrlQuery(appliedFilter); + history.replace({ search }); + }; + + generateUrlQuery(); + }, [appliedFilter]); + + return null; +}; + +export default useSessionSearchQueryHandler; diff --git a/frontend/app/player/web/managers/DOM/DOMManager.ts b/frontend/app/player/web/managers/DOM/DOMManager.ts index 5ae59c5c4..8e0fb8b6b 100644 --- a/frontend/app/player/web/managers/DOM/DOMManager.ts +++ b/frontend/app/player/web/managers/DOM/DOMManager.ts @@ -4,7 +4,7 @@ import type Screen from '../../Screen/Screen'; import type { Message, SetNodeScroll } from '../../messages'; import { MType } from '../../messages'; import ListWalker from '../../../common/ListWalker'; -import StylesManager, { rewriteNodeStyleSheet } from './StylesManager'; +import StylesManager from './StylesManager'; import FocusManager from './FocusManager'; import SelectionManager from './SelectionManager'; import type { StyleElement } from './VirtualDOM'; @@ -289,11 +289,6 @@ export default class DOMManager extends ListWalker { vn = this.vTexts.get(msg.id) if (!vn) { logger.error("SetCssData: Node not found", msg); return } vn.setData(msg.data) - if (vn.node instanceof HTMLStyleElement) { - doc = this.screen.document - // TODO: move to message parsing - doc && rewriteNodeStyleSheet(doc, vn.node) - } if (msg.tp === MType.SetCssData) { // Styles in priority (do we need inlines as well?) vn.applyChanges() } diff --git a/frontend/app/player/web/managers/DOM/FocusManager.ts b/frontend/app/player/web/managers/DOM/FocusManager.ts index c75f3ddc3..5a808766c 100644 --- a/frontend/app/player/web/managers/DOM/FocusManager.ts +++ b/frontend/app/player/web/managers/DOM/FocusManager.ts @@ -2,8 +2,7 @@ import logger from 'App/logger'; import type { SetNodeFocus } from '../../messages'; import type { VElement } from './VirtualDOM'; import ListWalker from '../../../common/ListWalker'; - -const FOCUS_CLASS = "-openreplay-focus" +import { FOCUS_CLASSNAME } from '../../messages/rewriter/constants' export default class FocusManager extends ListWalker { constructor(private readonly vElements: Map) {super()} @@ -11,7 +10,7 @@ export default class FocusManager extends ListWalker { move(t: number) { const msg = this.moveGetLast(t) if (!msg) {return} - this.focused?.classList.remove(FOCUS_CLASS) + this.focused?.classList.remove(FOCUS_CLASSNAME) if (msg.id === -1) { this.focused = null return @@ -19,7 +18,7 @@ export default class FocusManager extends ListWalker { const vn = this.vElements.get(msg.id) if (!vn) { logger.error("Node not found", msg); return } this.focused = vn.node - this.focused.classList.add(FOCUS_CLASS) + this.focused.classList.add(FOCUS_CLASSNAME) } } \ No newline at end of file diff --git a/frontend/app/player/web/managers/DOM/StylesManager.ts b/frontend/app/player/web/managers/DOM/StylesManager.ts index 295b95d2f..92f51aed9 100644 --- a/frontend/app/player/web/managers/DOM/StylesManager.ts +++ b/frontend/app/player/web/managers/DOM/StylesManager.ts @@ -1,19 +1,18 @@ import type Screen from '../../Screen/Screen'; import type { CssInsertRule, CssDeleteRule } from '../../messages'; +import { replaceCSSPseudoclasses } from '../../messages/rewriter/rewriteMessage' type CSSRuleMessage = CssInsertRule | CssDeleteRule; -const HOVER_CN = "-openreplay-hover"; -const HOVER_SELECTOR = `.${HOVER_CN}`; - -// Doesn't work with css files (hasOwnProperty) -export function rewriteNodeStyleSheet(doc: Document, node: HTMLLinkElement | HTMLStyleElement) { +// Doesn't work with css files (hasOwnProperty returns false) +// TODO: recheck and remove if true +function rewriteNodeStyleSheet(doc: Document, node: HTMLLinkElement | HTMLStyleElement) { const ss = Object.values(doc.styleSheets).find(s => s.ownerNode === node); if (!ss || !ss.hasOwnProperty('rules')) { return; } for(let i = 0; i < ss.rules.length; i++){ const r = ss.rules[i] if (r instanceof CSSStyleRule) { - r.selectorText = r.selectorText.replace('/\:hover/g', HOVER_SELECTOR) + r.selectorText = replaceCSSPseudoclasses(r.selectorText) } } } @@ -29,7 +28,7 @@ export default class StylesManager { this.linkLoadingCount = 0; this.linkLoadPromises = []; - //cancel all promises? tothinkaboutit + //cancel all promises? thinkaboutit } setStyleHandlers(node: HTMLLinkElement, value: string): void { @@ -44,6 +43,7 @@ export default class StylesManager { } timeoutId = setTimeout(addSkipAndResolve, 4000); + // It would be better to make it more relyable with addEventListener node.onload = () => { const doc = this.screen.document; doc && rewriteNodeStyleSheet(doc, node); diff --git a/frontend/app/player/web/managers/MouseMoveManager.ts b/frontend/app/player/web/managers/MouseMoveManager.ts index fa320ab13..2cb508f59 100644 --- a/frontend/app/player/web/managers/MouseMoveManager.ts +++ b/frontend/app/player/web/managers/MouseMoveManager.ts @@ -1,20 +1,14 @@ import type Screen from '../Screen/Screen' import type { MouseMove } from "../messages"; - +import { HOVER_CLASSNAME } from '../messages/rewriter/constants' import ListWalker from '../../common/ListWalker' -const HOVER_CLASS = "-openreplay-hover"; -const HOVER_CLASS_DEPR = "-asayer-hover"; export default class MouseMoveManager extends ListWalker { private hoverElements: Array = [] constructor(private screen: Screen) {super()} - // private getCursorTarget() { - // return this.screen.getElementFromInternalPoint(this.current) - // } - private getCursorTargets() { return this.screen.getElementsFromInternalPoint(this.current) } @@ -25,12 +19,10 @@ export default class MouseMoveManager extends ListWalker { const diffRemove = this.hoverElements.filter(elem => !curHoverElements.includes(elem)) this.hoverElements = curHoverElements diffAdd.forEach(elem => { - elem.classList.add(HOVER_CLASS) - elem.classList.add(HOVER_CLASS_DEPR) + elem.classList.add(HOVER_CLASSNAME) }) diffRemove.forEach(elem => { - elem.classList.remove(HOVER_CLASS) - elem.classList.remove(HOVER_CLASS_DEPR) + elem.classList.remove(HOVER_CLASSNAME) }) } diff --git a/frontend/app/player/web/messages/JSONRawMessageReader.ts b/frontend/app/player/web/messages/JSONRawMessageReader.ts index ca193c326..c2499f4c3 100644 --- a/frontend/app/player/web/messages/JSONRawMessageReader.ts +++ b/frontend/app/player/web/messages/JSONRawMessageReader.ts @@ -2,7 +2,7 @@ import type { RawMessage } from './raw.gen' import type { TrackerMessage } from './tracker.gen' import translate from './tracker.gen' import { TP_MAP } from './tracker-legacy.gen' -import resolveURL from './urlBasedResolver' +import rewriteMessage from './rewriter/rewriteMessage' function legacyTranslate(msg: any): RawMessage | null { @@ -30,7 +30,7 @@ export default class JSONRawMessageReader { if (!rawMsg) { return this.readMessage() } - return resolveURL(rawMsg) + return rewriteMessage(rawMsg) } } diff --git a/frontend/app/player/web/messages/MFileReader.ts b/frontend/app/player/web/messages/MFileReader.ts index b5fdde85c..a7411b155 100644 --- a/frontend/app/player/web/messages/MFileReader.ts +++ b/frontend/app/player/web/messages/MFileReader.ts @@ -2,7 +2,7 @@ import type { Message } from './message.gen'; import type { RawMessage } from './raw.gen'; import { MType } from './raw.gen'; import RawMessageReader from './RawMessageReader.gen'; -import resolveURL from './urlBasedResolver' +import rewriteMessage from './rewriter/rewriteMessage' import Logger from 'App/logger' // TODO: composition instead of inheritance @@ -77,7 +77,7 @@ export default class MFileReader extends RawMessageReader { } const index = this.getLastMessageID() - const msg = Object.assign(resolveURL(rMsg), { + const msg = Object.assign(rewriteMessage(rMsg), { time: this.currentTime, _index: index, }) diff --git a/frontend/app/player/web/messages/rewriter/constants.ts b/frontend/app/player/web/messages/rewriter/constants.ts new file mode 100644 index 000000000..abd05897a --- /dev/null +++ b/frontend/app/player/web/messages/rewriter/constants.ts @@ -0,0 +1,2 @@ +export const HOVER_CLASSNAME = "-openreplay-hover" +export const FOCUS_CLASSNAME = "-openreplay-focus" diff --git a/frontend/app/player/web/messages/urlBasedResolver.ts b/frontend/app/player/web/messages/rewriter/rewriteMessage.ts similarity index 55% rename from frontend/app/player/web/messages/urlBasedResolver.ts rename to frontend/app/player/web/messages/rewriter/rewriteMessage.ts index 53bf1ed81..d488c8362 100644 --- a/frontend/app/player/web/messages/urlBasedResolver.ts +++ b/frontend/app/player/web/messages/rewriter/rewriteMessage.ts @@ -10,24 +10,31 @@ import type { RawAdoptedSsInsertRule, RawAdoptedSsReplaceURLBased, RawAdoptedSsReplace, -} from './raw.gen' -import { MType } from './raw.gen' +} from '../raw.gen' +import { MType } from '../raw.gen' import { resolveURL, resolveCSS } from './urlResolve' +import { HOVER_CLASSNAME, FOCUS_CLASSNAME } from './constants' -// type PickMessage = Extract; -// type ResolversMap = { -// [Key in MType]: (event: PickMessage) => RawMessage -// } +const HOVER_SELECTOR = `.${HOVER_CLASSNAME}` +const FOCUS_SELECTOR = `.${FOCUS_CLASSNAME}` +export function replaceCSSPseudoclasses(cssText: string): string { + return cssText + .replace('/\:hover/g', HOVER_SELECTOR) + .replace('/\:focus/g', FOCUS_SELECTOR) +} +function rewriteCSS(baseURL: string, cssText: string): string { + return replaceCSSPseudoclasses(resolveCSS(baseURL, cssText)) +} -// TODO: commonURLBased logic for feilds -const resolvers = { +// TODO: common logic for URL fields in all the ...URLBased messages +const REWRITERS = { [MType.SetNodeAttributeURLBased]: (msg: RawSetNodeAttributeURLBased): RawSetNodeAttribute => ({ ...msg, value: msg.name === 'src' || msg.name === 'href' ? resolveURL(msg.baseURL, msg.value) : (msg.name === 'style' - ? resolveCSS(msg.baseURL, msg.value) + ? rewriteCSS(msg.baseURL, msg.value) : msg.value ), tp: MType.SetNodeAttribute, @@ -35,35 +42,35 @@ const resolvers = { [MType.SetCssDataURLBased]: (msg: RawSetCssDataURLBased): RawSetCssData => ({ ...msg, - data: resolveCSS(msg.baseURL, msg.data), + data: rewriteCSS(msg.baseURL, msg.data), tp: MType.SetCssData, }), [MType.CssInsertRuleURLBased]: (msg: RawCssInsertRuleURLBased): RawCssInsertRule => ({ ...msg, - rule: resolveCSS(msg.baseURL, msg.rule), + rule: rewriteCSS(msg.baseURL, msg.rule), tp: MType.CssInsertRule, }), [MType.AdoptedSsInsertRuleURLBased]: (msg: RawAdoptedSsInsertRuleURLBased): RawAdoptedSsInsertRule => ({ ...msg, - rule: resolveCSS(msg.baseURL, msg.rule), + rule: rewriteCSS(msg.baseURL, msg.rule), tp: MType.AdoptedSsInsertRule, }), [MType.AdoptedSsReplaceURLBased]: (msg: RawAdoptedSsReplaceURLBased): RawAdoptedSsReplace => ({ ...msg, - text: resolveCSS(msg.baseURL, msg.text), + text: rewriteCSS(msg.baseURL, msg.text), tp: MType.AdoptedSsReplace, }), } as const -export default function resolve(msg: RawMessage): RawMessage { - // @ts-ignore --- any idea? - if (resolvers[msg.tp]) { +export default function rewriteMessage(msg: RawMessage): RawMessage { + // @ts-ignore --- any idea for correct typing? + if (REWRITERS[msg.tp]) { // @ts-ignore - return resolvers[msg.tp](msg) + return REWRITERS[msg.tp](msg) } return msg } \ No newline at end of file diff --git a/frontend/app/player/web/messages/urlResolve.ts b/frontend/app/player/web/messages/rewriter/urlResolve.ts similarity index 100% rename from frontend/app/player/web/messages/urlResolve.ts rename to frontend/app/player/web/messages/rewriter/urlResolve.ts diff --git a/mobs/ios_messages.rb b/mobs/ios_messages.rb index 57e737714..342137877 100644 --- a/mobs/ios_messages.rb +++ b/mobs/ios_messages.rb @@ -35,7 +35,7 @@ message 92, 'IOSMetadata' do string 'Value' end -message 93, 'IOSCustomEvent', :seq_index => true, :replayer => true do +message 93, 'IOSCustomEvent', :replayer => true do uint 'Timestamp' uint 'Length' string 'Name' @@ -63,7 +63,7 @@ message 96, 'IOSScreenChanges', :replayer => true do uint 'Height' end -message 97, 'IOSCrash', :seq_index => true do +message 97, 'IOSCrash' do uint 'Timestamp' uint 'Length' string 'Name' @@ -71,7 +71,7 @@ message 97, 'IOSCrash', :seq_index => true do string 'Stacktrace' end -message 98, 'IOSScreenEnter', :seq_index => true do +message 98, 'IOSScreenEnter' do uint 'Timestamp' uint 'Length' string 'Title' @@ -85,7 +85,7 @@ message 99, 'IOSScreenLeave' do string 'ViewName' end -message 100, 'IOSClickEvent', :seq_index => true, :replayer => true do +message 100, 'IOSClickEvent', :replayer => true do uint 'Timestamp' uint 'Length' string 'Label' @@ -93,7 +93,7 @@ message 100, 'IOSClickEvent', :seq_index => true, :replayer => true do uint 'Y' end -message 101, 'IOSInputEvent', :seq_index => true do +message 101, 'IOSInputEvent' do uint 'Timestamp' uint 'Length' string 'Value' @@ -115,7 +115,7 @@ Name/Value may be : "mainThreadCPU": Possible values (0 .. 100) "memoryUsage": Used memory in bytes =end -message 102, 'IOSPerformanceEvent', :replayer => true, :seq_index => true do +message 102, 'IOSPerformanceEvent', :replayer => true do uint 'Timestamp' uint 'Length' string 'Name' @@ -135,7 +135,7 @@ message 104, 'IOSInternalError' do string 'Content' end -message 105, 'IOSNetworkCall', :replayer => true, :seq_index => true do +message 105, 'IOSNetworkCall', :replayer => true do uint 'Timestamp' uint 'Length' uint 'Duration' @@ -163,7 +163,7 @@ message 110, 'IOSPerformanceAggregated', :swift => false do uint 'MaxBattery' end -message 111, 'IOSIssueEvent', :seq_index => true do +message 111, 'IOSIssueEvent' do uint 'Timestamp' string 'Type' string 'ContextString' diff --git a/mobs/run.rb b/mobs/run.rb index 398068f95..a66e685a0 100644 --- a/mobs/run.rb +++ b/mobs/run.rb @@ -84,14 +84,13 @@ end $context = :web class Message - attr_reader :id, :name, :tracker, :replayer, :swift, :seq_index, :attributes, :context - def initialize(name:, id:, tracker: $context == :web, replayer: $context == :web, swift: $context == :ios, seq_index: false, &block) + attr_reader :id, :name, :tracker, :replayer, :swift, :attributes, :context + def initialize(name:, id:, tracker: $context == :web, replayer: $context == :web, swift: $context == :ios, &block) @id = id @name = name @tracker = tracker @replayer = replayer @swift = swift - @seq_index = seq_index @context = $context @attributes = [] # opts.each { |key, value| send "#{key}=", value }