-
@@ -114,7 +134,7 @@ export default connect(
(state: any) => ({
saveRequestPayloads: state.getIn(['site', 'instance', 'saveRequestPayloads']),
appliedFilter: state.getIn(['search', 'instance']),
- metaLoading: state.getIn(['customFields', 'fetchRequestActive', 'loading'])
+ metaLoading: state.getIn(['customFields', 'fetchRequestActive', 'loading']),
}),
- { edit, addFilter, fetchSessions, updateFilter }
-)(SessionSearch);
+ { edit, addFilter, fetchSessions, updateFilter, refreshFilterOptions }
+)(observer(SessionSearch));
diff --git a/frontend/app/components/ui/Icons/filters_tag_element.tsx b/frontend/app/components/ui/Icons/filters_tag_element.tsx
new file mode 100644
index 000000000..d69534824
--- /dev/null
+++ b/frontend/app/components/ui/Icons/filters_tag_element.tsx
@@ -0,0 +1,19 @@
+
+/* Auto-generated, do not edit */
+import React from 'react';
+
+interface Props {
+ size?: number | string;
+ width?: number | string;
+ height?: number | string;
+ fill?: string;
+}
+
+function Filters_tag_element(props: Props) {
+ const { size = 14, width = size, height = size, fill = '' } = props;
+ return (
+
+ );
+}
+
+export default Filters_tag_element;
diff --git a/frontend/app/components/ui/Icons/index.ts b/frontend/app/components/ui/Icons/index.ts
index a513b17bf..d8aa47651 100644
--- a/frontend/app/components/ui/Icons/index.ts
+++ b/frontend/app/components/ui/Icons/index.ts
@@ -225,6 +225,7 @@ export { default as Filters_referrer } from './filters_referrer';
export { default as Filters_resize } from './filters_resize';
export { default as Filters_rev_id } from './filters_rev_id';
export { default as Filters_state_action } from './filters_state_action';
+export { default as Filters_tag_element } from './filters_tag_element';
export { default as Filters_ttfb } from './filters_ttfb';
export { default as Filters_user_alt } from './filters_user_alt';
export { default as Filters_userid } from './filters_userid';
@@ -344,6 +345,7 @@ export { default as Mic } from './mic';
export { default as Minus } from './minus';
export { default as Mobile } from './mobile';
export { default as Mouse_alt } from './mouse_alt';
+export { default as Mouse_pointer_click } from './mouse_pointer_click';
export { default as Network } from './network';
export { default as Next1 } from './next1';
export { default as No_dashboard } from './no_dashboard';
@@ -454,6 +456,7 @@ export { default as Turtle } from './turtle';
export { default as User_alt } from './user_alt';
export { default as User_circle } from './user_circle';
export { default as User_friends } from './user_friends';
+export { default as User_switch } from './user_switch';
export { default as Users } from './users';
export { default as Vendors_graphql } from './vendors_graphql';
export { default as Vendors_mobx } from './vendors_mobx';
diff --git a/frontend/app/components/ui/Icons/mouse_pointer_click.tsx b/frontend/app/components/ui/Icons/mouse_pointer_click.tsx
new file mode 100644
index 000000000..7e694c0a6
--- /dev/null
+++ b/frontend/app/components/ui/Icons/mouse_pointer_click.tsx
@@ -0,0 +1,19 @@
+
+/* Auto-generated, do not edit */
+import React from 'react';
+
+interface Props {
+ size?: number | string;
+ width?: number | string;
+ height?: number | string;
+ fill?: string;
+}
+
+function Mouse_pointer_click(props: Props) {
+ const { size = 14, width = size, height = size, fill = '' } = props;
+ return (
+
+ );
+}
+
+export default Mouse_pointer_click;
diff --git a/frontend/app/components/ui/Icons/user_switch.tsx b/frontend/app/components/ui/Icons/user_switch.tsx
new file mode 100644
index 000000000..513c24123
--- /dev/null
+++ b/frontend/app/components/ui/Icons/user_switch.tsx
@@ -0,0 +1,19 @@
+
+/* Auto-generated, do not edit */
+import React from 'react';
+
+interface Props {
+ size?: number | string;
+ width?: number | string;
+ height?: number | string;
+ fill?: string;
+}
+
+function User_switch(props: Props) {
+ const { size = 14, width = size, height = size, fill = '' } = props;
+ return (
+
+ );
+}
+
+export default User_switch;
diff --git a/frontend/app/components/ui/SVG.tsx b/frontend/app/components/ui/SVG.tsx
index 1acce18dc..2cb84072e 100644
--- a/frontend/app/components/ui/SVG.tsx
+++ b/frontend/app/components/ui/SVG.tsx
@@ -3,480 +3,483 @@
import React from 'react';
import {
Activity,
-Alarm_clock,
-Alarm_plus,
-All_sessions,
-Analytics,
-Anchor,
-Arrow_alt_square_right,
-Arrow_bar_left,
-Arrow_clockwise,
-Arrow_counterclockwise,
-Arrow_down_short,
-Arrow_down_up,
-Arrow_down,
-Arrow_repeat,
-Arrow_right_short,
-Arrow_square_left,
-Arrow_square_right,
-Arrow_up_short,
-Arrow_up,
-Arrows_angle_extend,
-Avatar_icn_avatar1,
-Avatar_icn_avatar10,
-Avatar_icn_avatar11,
-Avatar_icn_avatar12,
-Avatar_icn_avatar13,
-Avatar_icn_avatar14,
-Avatar_icn_avatar15,
-Avatar_icn_avatar16,
-Avatar_icn_avatar17,
-Avatar_icn_avatar18,
-Avatar_icn_avatar19,
-Avatar_icn_avatar2,
-Avatar_icn_avatar20,
-Avatar_icn_avatar21,
-Avatar_icn_avatar22,
-Avatar_icn_avatar23,
-Avatar_icn_avatar3,
-Avatar_icn_avatar4,
-Avatar_icn_avatar5,
-Avatar_icn_avatar6,
-Avatar_icn_avatar7,
-Avatar_icn_avatar8,
-Avatar_icn_avatar9,
-Ban,
-Bar_chart_line,
-Bar_pencil,
-Battery_charging,
-Battery,
-Bell_fill,
-Bell_plus,
-Bell_slash,
-Bell,
-Binoculars,
-Book_doc,
-Book,
-Bookmark,
-Broadcast,
-Browser_browser,
-Browser_chrome,
-Browser_edge,
-Browser_electron,
-Browser_facebook,
-Browser_firefox,
-Browser_ie,
-Browser_opera,
-Browser_safari,
-Buildings,
-Bullhorn,
-Business_time,
-Calendar_alt,
-Calendar_check,
-Calendar_day,
-Calendar,
-Call,
-Camera_alt,
-Camera_video_off,
-Camera_video,
-Camera,
-Card_checklist,
-Card_list,
-Card_text,
-Caret_down_fill,
-Caret_left_fill,
-Caret_right_fill,
-Caret_up_fill,
-Chat_dots,
-Chat_left_text,
-Chat_right_text,
-Chat_square_quote,
-Check_circle_fill,
-Check_circle,
-Check,
-Chevron_double_left,
-Chevron_double_right,
-Chevron_down,
-Chevron_left,
-Chevron_right,
-Chevron_up,
-Circle_fill,
-Circle,
-Click_hesitation,
-Click_rage,
-Clipboard_check,
-Clipboard_list_check,
-Clock_history,
-Clock,
-Close,
-Cloud_fog2_fill,
-Code,
-Cog,
-Cogs,
-Collection_play,
-Collection,
-Columns_gap_filled,
-Columns_gap,
-Console_error,
-Console_exception,
-Console_info,
-Console_warning,
-Console,
-Controller,
-Cookies,
-Copy,
-Credit_card_2_back,
-Credit_card_front,
-Cross,
-Cubes,
-Cursor_trash,
-Dash,
-Dashboard_icn,
-Db_icons_icn_card_clickmap,
-Db_icons_icn_card_errors,
-Db_icons_icn_card_funnel,
-Db_icons_icn_card_funnels,
-Db_icons_icn_card_insights,
-Db_icons_icn_card_library,
-Db_icons_icn_card_mapchart,
-Db_icons_icn_card_pathanalysis,
-Db_icons_icn_card_performance,
-Db_icons_icn_card_resources,
-Db_icons_icn_card_table,
-Db_icons_icn_card_timeseries,
-Db_icons_icn_card_webvitals,
-Desktop,
-Device,
-Diagram_3,
-Dice_3,
-Dizzy,
-Door_closed,
-Doublecheck,
-Download,
-Drag,
-Edit,
-Ellipsis_v,
-Emoji_dizzy,
-Enter,
-Envelope_check,
-Envelope_paper,
-Envelope_x,
-Envelope,
-Errors_icon,
-Event_click,
-Event_click_hesitation,
-Event_clickrage,
-Event_code,
-Event_i_cursor,
-Event_input,
-Event_input_hesitation,
-Event_link,
-Event_location,
-Event_mouse_thrashing,
-Event_resize,
-Event_view,
-Exclamation_circle_fill,
-Exclamation_circle,
-Exclamation_triangle,
-Expand_wide,
-Explosion,
-External_link_alt,
-Eye_slash_fill,
-Eye_slash,
-Eye,
-Fetch,
-Fflag_multi,
-Fflag_single,
-File_bar_graph,
-File_code,
-File_medical_alt,
-File_pdf,
-File,
-Files,
-Filetype_js,
-Filetype_pdf,
-Filter,
-Filters_arrow_return_right,
-Filters_browser,
-Filters_click,
-Filters_clickrage,
-Filters_code,
-Filters_console,
-Filters_country,
-Filters_cpu_load,
-Filters_custom,
-Filters_device,
-Filters_dom_complete,
-Filters_duration,
-Filters_error,
-Filters_fetch_failed,
-Filters_fetch,
-Filters_file_code,
-Filters_graphql,
-Filters_i_cursor,
-Filters_input,
-Filters_lcpt,
-Filters_link,
-Filters_location,
-Filters_memory_load,
-Filters_metadata,
-Filters_os,
-Filters_perfromance_network_request,
-Filters_platform,
-Filters_referrer,
-Filters_resize,
-Filters_rev_id,
-Filters_state_action,
-Filters_ttfb,
-Filters_user_alt,
-Filters_userid,
-Filters_view,
-Flag_na,
-Folder_plus,
-Folder2,
-Fullscreen,
-Funnel_cpu_fill,
-Funnel_cpu,
-Funnel_dizzy,
-Funnel_emoji_angry_fill,
-Funnel_emoji_angry,
-Funnel_emoji_dizzy_fill,
-Funnel_exclamation_circle_fill,
-Funnel_exclamation_circle,
-Funnel_file_earmark_break_fill,
-Funnel_file_earmark_break,
-Funnel_file_earmark_minus_fill,
-Funnel_file_earmark_minus,
-Funnel_file_medical_alt,
-Funnel_file_x,
-Funnel_hdd_fill,
-Funnel_hourglass_top,
-Funnel_image_fill,
-Funnel_image,
-Funnel_microchip,
-Funnel_mouse,
-Funnel_patch_exclamation_fill,
-Funnel_sd_card,
-Funnel_fill,
-Funnel_new,
-Funnel,
-Gear_fill,
-Gear,
-Geo_alt_fill_custom,
-Github,
-Graph_up_arrow,
-Graph_up,
-Grid_1x2,
-Grid_3x3,
-Grid_check,
-Grid_horizontal,
-Grid,
-Grip_horizontal,
-Hash,
-Hdd_stack,
-Headset,
-Heart_rate,
-High_engagement,
-History,
-Hourglass_start,
-Ic_errors,
-Ic_network,
-Ic_rage,
-Ic_resources,
-Id_card,
-Image,
-Info_circle_fill,
-Info_circle,
-Info_square,
-Info,
-Input_hesitation,
-Inspect,
-Integrations_assist,
-Integrations_bugsnag_text,
-Integrations_bugsnag,
-Integrations_cloudwatch_text,
-Integrations_cloudwatch,
-Integrations_datadog,
-Integrations_elasticsearch_text,
-Integrations_elasticsearch,
-Integrations_github,
-Integrations_graphql,
-Integrations_jira_text,
-Integrations_jira,
-Integrations_mobx,
-Integrations_newrelic_text,
-Integrations_newrelic,
-Integrations_ngrx,
-Integrations_openreplay_text,
-Integrations_openreplay,
-Integrations_redux,
-Integrations_rollbar_text,
-Integrations_rollbar,
-Integrations_segment,
-Integrations_sentry_text,
-Integrations_sentry,
-Integrations_slack_bw,
-Integrations_slack,
-Integrations_stackdriver,
-Integrations_sumologic_text,
-Integrations_sumologic,
-Integrations_teams_white,
-Integrations_teams,
-Integrations_vuejs,
-Integrations_zustand,
-Journal_code,
-Key,
-Layer_group,
-Layers_half,
-Lightbulb_on,
-Lightbulb,
-Link_45deg,
-List_alt,
-List_arrow,
-List_ul,
-List,
-Lock_alt,
-Low_disc_space,
-Magic,
-Map_marker_alt,
-Memory_ios,
-Memory,
-Mic_mute,
-Mic,
-Minus,
-Mobile,
-Mouse_alt,
-Network,
-Next1,
-No_dashboard,
-No_metrics_chart,
-No_metrics,
-No_recordings,
-Os_android,
-Os_chrome_os,
-Os_fedora,
-Os_ios,
-Os_linux,
-Os_mac_os_x,
-Os_other,
-Os_ubuntu,
-Os_windows,
-Os,
-Pause_circle_fill,
-Pause_fill,
-Pause,
-Pdf_download,
-Pencil_stop,
-Pencil,
-People,
-Percent,
-Performance_icon,
-Person_border,
-Person_fill,
-Person,
-Pie_chart_fill,
-Pin_fill,
-Play_circle_bold,
-Play_circle_light,
-Play_circle,
-Play_fill_new,
-Play_fill,
-Play_hover,
-Play,
-Plug,
-Plus_circle,
-Plus_lg,
-Plus,
-Pointer_sessions_search,
-Prev1,
-Pulse,
-Puzzle_piece,
-Puzzle,
-Question_circle,
-Question_lg,
-Quote_left,
-Quote_right,
-Quotes,
-Record_btn,
-Record_circle_fill,
-Record_circle,
-Record2,
-Redo_back,
-Redo,
-Redux,
-Remote_control,
-Replay_10,
-Resources_icon,
-Safe_fill,
-Safe,
-Sandglass,
-Search,
-Search_notification,
-Server,
-Share_alt,
-Shield_lock,
-Side_menu_closed,
-Side_menu_open,
-Signpost_split,
-Signup,
-Skip_forward_fill,
-Skip_forward,
-Slack,
-Slash_circle,
-Sleep,
-Sliders,
-Social_slack,
-Social_trello,
-Speedometer2,
-Spinner,
-Star_solid,
-Star,
-Step_forward,
-Stickies,
-Stop_record_circle,
-Stopwatch,
-Store,
-Sync_alt,
-Table_new,
-Table,
-Tablet_android,
-Tachometer_slow,
-Tachometer_slowest,
-Tags,
-Team_funnel,
-Telephone_fill,
-Telephone,
-Terminal,
-Text_paragraph,
-Thermometer_sun,
-Toggles,
-Tools,
-Trash,
-Turtle,
-User_alt,
-User_circle,
-User_friends,
-Users,
-Vendors_graphql,
-Vendors_mobx,
-Vendors_ngrx,
-Vendors_redux,
-Vendors_vuex,
-Web_vitals,
-Wifi,
-Window_alt,
-Window_restore,
-Window_x,
-Window,
-Zoom_in
+ Alarm_clock,
+ Alarm_plus,
+ All_sessions,
+ Analytics,
+ Anchor,
+ Arrow_alt_square_right,
+ Arrow_bar_left,
+ Arrow_clockwise,
+ Arrow_counterclockwise,
+ Arrow_down_short,
+ Arrow_down_up,
+ Arrow_down,
+ Arrow_repeat,
+ Arrow_right_short,
+ Arrow_square_left,
+ Arrow_square_right,
+ Arrow_up_short,
+ Arrow_up,
+ Arrows_angle_extend,
+ Avatar_icn_avatar1,
+ Avatar_icn_avatar10,
+ Avatar_icn_avatar11,
+ Avatar_icn_avatar12,
+ Avatar_icn_avatar13,
+ Avatar_icn_avatar14,
+ Avatar_icn_avatar15,
+ Avatar_icn_avatar16,
+ Avatar_icn_avatar17,
+ Avatar_icn_avatar18,
+ Avatar_icn_avatar19,
+ Avatar_icn_avatar2,
+ Avatar_icn_avatar20,
+ Avatar_icn_avatar21,
+ Avatar_icn_avatar22,
+ Avatar_icn_avatar23,
+ Avatar_icn_avatar3,
+ Avatar_icn_avatar4,
+ Avatar_icn_avatar5,
+ Avatar_icn_avatar6,
+ Avatar_icn_avatar7,
+ Avatar_icn_avatar8,
+ Avatar_icn_avatar9,
+ Ban,
+ Bar_chart_line,
+ Bar_pencil,
+ Battery_charging,
+ Battery,
+ Bell_fill,
+ Bell_plus,
+ Bell_slash,
+ Bell,
+ Binoculars,
+ Book_doc,
+ Book,
+ Bookmark,
+ Broadcast,
+ Browser_browser,
+ Browser_chrome,
+ Browser_edge,
+ Browser_electron,
+ Browser_facebook,
+ Browser_firefox,
+ Browser_ie,
+ Browser_opera,
+ Browser_safari,
+ Buildings,
+ Bullhorn,
+ Business_time,
+ Calendar_alt,
+ Calendar_check,
+ Calendar_day,
+ Calendar,
+ Call,
+ Camera_alt,
+ Camera_video_off,
+ Camera_video,
+ Camera,
+ Card_checklist,
+ Card_list,
+ Card_text,
+ Caret_down_fill,
+ Caret_left_fill,
+ Caret_right_fill,
+ Caret_up_fill,
+ Chat_dots,
+ Chat_left_text,
+ Chat_right_text,
+ Chat_square_quote,
+ Check_circle_fill,
+ Check_circle,
+ Check,
+ Chevron_double_left,
+ Chevron_double_right,
+ Chevron_down,
+ Chevron_left,
+ Chevron_right,
+ Chevron_up,
+ Circle_fill,
+ Circle,
+ Click_hesitation,
+ Click_rage,
+ Clipboard_check,
+ Clipboard_list_check,
+ Clock_history,
+ Clock,
+ Close,
+ Cloud_fog2_fill,
+ Code,
+ Cog,
+ Cogs,
+ Collection_play,
+ Collection,
+ Columns_gap_filled,
+ Columns_gap,
+ Console_error,
+ Console_exception,
+ Console_info,
+ Console_warning,
+ Console,
+ Controller,
+ Cookies,
+ Copy,
+ Credit_card_2_back,
+ Credit_card_front,
+ Cross,
+ Cubes,
+ Cursor_trash,
+ Dash,
+ Dashboard_icn,
+ Db_icons_icn_card_clickmap,
+ Db_icons_icn_card_errors,
+ Db_icons_icn_card_funnel,
+ Db_icons_icn_card_funnels,
+ Db_icons_icn_card_insights,
+ Db_icons_icn_card_library,
+ Db_icons_icn_card_mapchart,
+ Db_icons_icn_card_pathanalysis,
+ Db_icons_icn_card_performance,
+ Db_icons_icn_card_resources,
+ Db_icons_icn_card_table,
+ Db_icons_icn_card_timeseries,
+ Db_icons_icn_card_webvitals,
+ Desktop,
+ Device,
+ Diagram_3,
+ Dice_3,
+ Dizzy,
+ Door_closed,
+ Doublecheck,
+ Download,
+ Drag,
+ Edit,
+ Ellipsis_v,
+ Emoji_dizzy,
+ Enter,
+ Envelope_check,
+ Envelope_paper,
+ Envelope_x,
+ Envelope,
+ Errors_icon,
+ Event_click,
+ Event_click_hesitation,
+ Event_clickrage,
+ Event_code,
+ Event_i_cursor,
+ Event_input,
+ Event_input_hesitation,
+ Event_link,
+ Event_location,
+ Event_mouse_thrashing,
+ Event_resize,
+ Event_view,
+ Exclamation_circle_fill,
+ Exclamation_circle,
+ Exclamation_triangle,
+ Expand_wide,
+ Explosion,
+ External_link_alt,
+ Eye_slash_fill,
+ Eye_slash,
+ Eye,
+ Fetch,
+ Fflag_multi,
+ Fflag_single,
+ File_bar_graph,
+ File_code,
+ File_medical_alt,
+ File_pdf,
+ File,
+ Files,
+ Filetype_js,
+ Filetype_pdf,
+ Filter,
+ Filters_arrow_return_right,
+ Filters_browser,
+ Filters_click,
+ Filters_clickrage,
+ Filters_code,
+ Filters_console,
+ Filters_country,
+ Filters_cpu_load,
+ Filters_custom,
+ Filters_device,
+ Filters_dom_complete,
+ Filters_duration,
+ Filters_error,
+ Filters_fetch_failed,
+ Filters_fetch,
+ Filters_file_code,
+ Filters_graphql,
+ Filters_i_cursor,
+ Filters_input,
+ Filters_lcpt,
+ Filters_link,
+ Filters_location,
+ Filters_memory_load,
+ Filters_metadata,
+ Filters_os,
+ Filters_perfromance_network_request,
+ Filters_platform,
+ Filters_referrer,
+ Filters_resize,
+ Filters_rev_id,
+ Filters_state_action,
+ Filters_tag_element,
+ Filters_ttfb,
+ Filters_user_alt,
+ Filters_userid,
+ Filters_view,
+ Flag_na,
+ Folder_plus,
+ Folder2,
+ Fullscreen,
+ Funnel_cpu_fill,
+ Funnel_cpu,
+ Funnel_dizzy,
+ Funnel_emoji_angry_fill,
+ Funnel_emoji_angry,
+ Funnel_emoji_dizzy_fill,
+ Funnel_exclamation_circle_fill,
+ Funnel_exclamation_circle,
+ Funnel_file_earmark_break_fill,
+ Funnel_file_earmark_break,
+ Funnel_file_earmark_minus_fill,
+ Funnel_file_earmark_minus,
+ Funnel_file_medical_alt,
+ Funnel_file_x,
+ Funnel_hdd_fill,
+ Funnel_hourglass_top,
+ Funnel_image_fill,
+ Funnel_image,
+ Funnel_microchip,
+ Funnel_mouse,
+ Funnel_patch_exclamation_fill,
+ Funnel_sd_card,
+ Funnel_fill,
+ Funnel_new,
+ Funnel,
+ Gear_fill,
+ Gear,
+ Geo_alt_fill_custom,
+ Github,
+ Graph_up_arrow,
+ Graph_up,
+ Grid_1x2,
+ Grid_3x3,
+ Grid_check,
+ Grid_horizontal,
+ Grid,
+ Grip_horizontal,
+ Hash,
+ Hdd_stack,
+ Headset,
+ Heart_rate,
+ High_engagement,
+ History,
+ Hourglass_start,
+ Ic_errors,
+ Ic_network,
+ Ic_rage,
+ Ic_resources,
+ Id_card,
+ Image,
+ Info_circle_fill,
+ Info_circle,
+ Info_square,
+ Info,
+ Input_hesitation,
+ Inspect,
+ Integrations_assist,
+ Integrations_bugsnag_text,
+ Integrations_bugsnag,
+ Integrations_cloudwatch_text,
+ Integrations_cloudwatch,
+ Integrations_datadog,
+ Integrations_elasticsearch_text,
+ Integrations_elasticsearch,
+ Integrations_github,
+ Integrations_graphql,
+ Integrations_jira_text,
+ Integrations_jira,
+ Integrations_mobx,
+ Integrations_newrelic_text,
+ Integrations_newrelic,
+ Integrations_ngrx,
+ Integrations_openreplay_text,
+ Integrations_openreplay,
+ Integrations_redux,
+ Integrations_rollbar_text,
+ Integrations_rollbar,
+ Integrations_segment,
+ Integrations_sentry_text,
+ Integrations_sentry,
+ Integrations_slack_bw,
+ Integrations_slack,
+ Integrations_stackdriver,
+ Integrations_sumologic_text,
+ Integrations_sumologic,
+ Integrations_teams_white,
+ Integrations_teams,
+ Integrations_vuejs,
+ Integrations_zustand,
+ Journal_code,
+ Key,
+ Layer_group,
+ Layers_half,
+ Lightbulb_on,
+ Lightbulb,
+ Link_45deg,
+ List_alt,
+ List_arrow,
+ List_ul,
+ List,
+ Lock_alt,
+ Low_disc_space,
+ Magic,
+ Map_marker_alt,
+ Memory_ios,
+ Memory,
+ Mic_mute,
+ Mic,
+ Minus,
+ Mobile,
+ Mouse_alt,
+ Mouse_pointer_click,
+ Network,
+ Next1,
+ No_dashboard,
+ No_metrics_chart,
+ No_metrics,
+ No_recordings,
+ Os_android,
+ Os_chrome_os,
+ Os_fedora,
+ Os_ios,
+ Os_linux,
+ Os_mac_os_x,
+ Os_other,
+ Os_ubuntu,
+ Os_windows,
+ Os,
+ Pause_circle_fill,
+ Pause_fill,
+ Pause,
+ Pdf_download,
+ Pencil_stop,
+ Pencil,
+ People,
+ Percent,
+ Performance_icon,
+ Person_border,
+ Person_fill,
+ Person,
+ Pie_chart_fill,
+ Pin_fill,
+ Play_circle_bold,
+ Play_circle_light,
+ Play_circle,
+ Play_fill_new,
+ Play_fill,
+ Play_hover,
+ Play,
+ Plug,
+ Plus_circle,
+ Plus_lg,
+ Plus,
+ Pointer_sessions_search,
+ Prev1,
+ Pulse,
+ Puzzle_piece,
+ Puzzle,
+ Question_circle,
+ Question_lg,
+ Quote_left,
+ Quote_right,
+ Quotes,
+ Record_btn,
+ Record_circle_fill,
+ Record_circle,
+ Record2,
+ Redo_back,
+ Redo,
+ Redux,
+ Remote_control,
+ Replay_10,
+ Resources_icon,
+ Safe_fill,
+ Safe,
+ Sandglass,
+ Search,
+ Search_notification,
+ Server,
+ Share_alt,
+ Shield_lock,
+ Side_menu_closed,
+ Side_menu_open,
+ Signpost_split,
+ Signup,
+ Skip_forward_fill,
+ Skip_forward,
+ Slack,
+ Slash_circle,
+ Sleep,
+ Sliders,
+ Social_slack,
+ Social_trello,
+ Speedometer2,
+ Spinner,
+ Star_solid,
+ Star,
+ Step_forward,
+ Stickies,
+ Stop_record_circle,
+ Stopwatch,
+ Store,
+ Sync_alt,
+ Table_new,
+ Table,
+ Tablet_android,
+ Tachometer_slow,
+ Tachometer_slowest,
+ Tags,
+ Team_funnel,
+ Telephone_fill,
+ Telephone,
+ Terminal,
+ Text_paragraph,
+ Thermometer_sun,
+ Toggles,
+ Tools,
+ Trash,
+ Turtle,
+ User_alt,
+ User_circle,
+ User_friends,
+ User_switch,
+ Users,
+ Vendors_graphql,
+ Vendors_mobx,
+ Vendors_ngrx,
+ Vendors_redux,
+ Vendors_vuex,
+ Web_vitals,
+ Wifi,
+ Window_alt,
+ Window_restore,
+ Window_x,
+ Window,
+ Zoom_in
} from './Icons'
-// export type IconNames = 'activity' | 'alarm_clock' | 'alarm_plus' | 'all_sessions' | 'analytics' | 'anchor' | 'arrow_alt_square_right' | 'arrow_bar_left' | 'arrow_clockwise' | 'arrow_counterclockwise' | 'arrow_down_short' | 'arrow_down_up' | 'arrow_down' | 'arrow_repeat' | 'arrow_right_short' | 'arrow_square_left' | 'arrow_square_right' | 'arrow_up_short' | 'arrow_up' | 'arrows_angle_extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar_chart_line' | 'bar_pencil' | 'battery_charging' | 'battery' | 'bell_fill' | 'bell_plus' | 'bell_slash' | 'bell' | 'binoculars' | 'book_doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business_time' | 'calendar_alt' | 'calendar_check' | 'calendar_day' | 'calendar' | 'call' | 'camera_alt' | 'camera_video_off' | 'camera_video' | 'camera' | 'card_checklist' | 'card_list' | 'card_text' | 'caret_down_fill' | 'caret_left_fill' | 'caret_right_fill' | 'caret_up_fill' | 'chat_dots' | 'chat_left_text' | 'chat_right_text' | 'chat_square_quote' | 'check_circle_fill' | 'check_circle' | 'check' | 'chevron_double_left' | 'chevron_double_right' | 'chevron_down' | 'chevron_left' | 'chevron_right' | 'chevron_up' | 'circle_fill' | 'circle' | 'click_hesitation' | 'click_rage' | 'clipboard_check' | 'clipboard_list_check' | 'clock_history' | 'clock' | 'close' | 'cloud_fog2_fill' | 'code' | 'cog' | 'cogs' | 'collection_play' | 'collection' | 'columns_gap_filled' | 'columns_gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit_card_2_back' | 'credit_card_front' | 'cross' | 'cubes' | 'cursor_trash' | 'dash' | 'dashboard_icn' | 'db_icons/icn_card_clickMap' | 'db_icons/icn_card_errors' | 'db_icons/icn_card_funnel' | 'db_icons/icn_card_funnels' | 'db_icons/icn_card_insights' | 'db_icons/icn_card_library' | 'db_icons/icn_card_mapchart' | 'db_icons/icn_card_pathAnalysis' | 'db_icons/icn_card_performance' | 'db_icons/icn_card_resources' | 'db_icons/icn_card_table' | 'db_icons/icn_card_timeseries' | 'db_icons/icn_card_webVitals' | 'desktop' | 'device' | 'diagram_3' | 'dice_3' | 'dizzy' | 'door_closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis_v' | 'emoji_dizzy' | 'enter' | 'envelope_check' | 'envelope_paper' | 'envelope_x' | 'envelope' | 'errors_icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i_cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation_circle_fill' | 'exclamation_circle' | 'exclamation_triangle' | 'expand_wide' | 'explosion' | 'external_link_alt' | 'eye_slash_fill' | 'eye_slash' | 'eye' | 'fetch' | 'fflag_multi' | 'fflag_single' | 'file_bar_graph' | 'file_code' | 'file_medical_alt' | 'file_pdf' | 'file' | 'files' | 'filetype_js' | 'filetype_pdf' | 'filter' | 'filters/arrow_return_right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu_load' | 'filters/custom' | 'filters/device' | 'filters/dom_complete' | 'filters/duration' | 'filters/error' | 'filters/fetch_failed' | 'filters/fetch' | 'filters/file_code' | 'filters/graphql' | 'filters/i_cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory_load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance_network_request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev_id' | 'filters/state_action' | 'filters/ttfb' | 'filters/user_alt' | 'filters/userid' | 'filters/view' | 'flag_na' | 'folder_plus' | 'folder2' | 'fullscreen' | 'funnel/cpu_fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji_angry_fill' | 'funnel/emoji_angry' | 'funnel/emoji_dizzy_fill' | 'funnel/exclamation_circle_fill' | 'funnel/exclamation_circle' | 'funnel/file_earmark_break_fill' | 'funnel/file_earmark_break' | 'funnel/file_earmark_minus_fill' | 'funnel/file_earmark_minus' | 'funnel/file_medical_alt' | 'funnel/file_x' | 'funnel/hdd_fill' | 'funnel/hourglass_top' | 'funnel/image_fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch_exclamation_fill' | 'funnel/sd_card' | 'funnel_fill' | 'funnel_new' | 'funnel' | 'gear_fill' | 'gear' | 'geo_alt_fill_custom' | 'github' | 'graph_up_arrow' | 'graph_up' | 'grid_1x2' | 'grid_3x3' | 'grid_check' | 'grid_horizontal' | 'grid' | 'grip_horizontal' | 'hash' | 'hdd_stack' | 'headset' | 'heart_rate' | 'high_engagement' | 'history' | 'hourglass_start' | 'ic_errors' | 'ic_network' | 'ic_rage' | 'ic_resources' | 'id_card' | 'image' | 'info_circle_fill' | 'info_circle' | 'info_square' | 'info' | 'input_hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag_text' | 'integrations/bugsnag' | 'integrations/cloudwatch_text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch_text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira_text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic_text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay_text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar_text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry_text' | 'integrations/sentry' | 'integrations/slack_bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic_text' | 'integrations/sumologic' | 'integrations/teams_white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal_code' | 'key' | 'layer_group' | 'layers_half' | 'lightbulb_on' | 'lightbulb' | 'link_45deg' | 'list_alt' | 'list_arrow' | 'list_ul' | 'list' | 'lock_alt' | 'low_disc_space' | 'magic' | 'map_marker_alt' | 'memory_ios' | 'memory' | 'mic_mute' | 'mic' | 'minus' | 'mobile' | 'mouse_alt' | 'network' | 'next1' | 'no_dashboard' | 'no_metrics_chart' | 'no_metrics' | 'no_recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause_circle_fill' | 'pause_fill' | 'pause' | 'pdf_download' | 'pencil_stop' | 'pencil' | 'people' | 'percent' | 'performance_icon' | 'person_border' | 'person_fill' | 'person' | 'pie_chart_fill' | 'pin_fill' | 'play_circle_bold' | 'play_circle_light' | 'play_circle' | 'play_fill_new' | 'play_fill' | 'play_hover' | 'play' | 'plug' | 'plus_circle' | 'plus_lg' | 'plus' | 'pointer_sessions_search' | 'prev1' | 'pulse' | 'puzzle_piece' | 'puzzle' | 'question_circle' | 'question_lg' | 'quote_left' | 'quote_right' | 'quotes' | 'record_btn' | 'record_circle_fill' | 'record_circle' | 'record2' | 'redo_back' | 'redo' | 'redux' | 'remote_control' | 'replay_10' | 'resources_icon' | 'safe_fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share_alt' | 'shield_lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost_split' | 'signup' | 'skip_forward_fill' | 'skip_forward' | 'slack' | 'slash_circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'speedometer2' | 'spinner' | 'star_solid' | 'star' | 'step_forward' | 'stickies' | 'stop_record_circle' | 'stopwatch' | 'store' | 'sync_alt' | 'table_new' | 'table' | 'tablet_android' | 'tachometer_slow' | 'tachometer_slowest' | 'tags' | 'team_funnel' | 'telephone_fill' | 'telephone' | 'terminal' | 'text_paragraph' | 'thermometer_sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user_alt' | 'user_circle' | 'user_friends' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web_vitals' | 'wifi' | 'window_alt' | 'window_restore' | 'window_x' | 'window' | 'zoom_in';
-export type OldIconNames = 'activity' | 'alarm-clock' | 'alarm-plus' | 'all-sessions' | 'analytics' | 'anchor' | 'arrow-alt-square-right' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-square-left' | 'arrow-square-right' | 'arrow-up-short' | 'arrow-up' | 'arrows-angle-extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-fill' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book-doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business-time' | 'calendar-alt' | 'calendar-check' | 'calendar-day' | 'calendar' | 'call' | 'camera-alt' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-checklist' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-left-fill' | 'caret-right-fill' | 'caret-up-fill' | 'chat-dots' | 'chat-left-text' | 'chat-right-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-double-left' | 'chevron-double-right' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clipboard-list-check' | 'clock-history' | 'clock' | 'close' | 'cloud-fog2-fill' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'columns-gap-filled' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'credit-card-front' | 'cross' | 'cubes' | 'cursor-trash' | 'dash' | 'dashboard-icn' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dice-3' | 'dizzy' | 'door-closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'expand-wide' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/state-action' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel-new' | 'funnel' | 'gear-fill' | 'gear' | 'geo-alt-fill-custom' | 'github' | 'graph-up-arrow' | 'graph-up' | 'grid-1x2' | 'grid-3x3' | 'grid-check' | 'grid-horizontal' | 'grid' | 'grip-horizontal' | 'hash' | 'hdd-stack' | 'headset' | 'heart-rate' | 'high-engagement' | 'history' | 'hourglass-start' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'key' | 'layer-group' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-arrow' | 'list-ul' | 'list' | 'lock-alt' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus-lg' | 'plus' | 'pointer-sessions-search' | 'prev1' | 'pulse' | 'puzzle-piece' | 'puzzle' | 'question-circle' | 'question-lg' | 'quote-left' | 'quote-right' | 'quotes' | 'record-btn' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo-back' | 'redo' | 'redux' | 'remote-control' | 'replay-10' | 'resources-icon' | 'safe-fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'skip-forward-fill' | 'skip-forward' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'speedometer2' | 'spinner' | 'star-solid' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table-new' | 'table' | 'tablet-android' | 'tachometer-slow' | 'tachometer-slowest' | 'tags' | 'team-funnel' | 'telephone-fill' | 'telephone' | 'terminal' | 'text-paragraph' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web-vitals' | 'wifi' | 'window-alt' | 'window-restore' | 'window-x' | 'window' | 'zoom-in';
+// export type NewIconNames = 'activity' | 'alarm_clock' | 'alarm_plus' | 'all_sessions' | 'analytics' | 'anchor' | 'arrow_alt_square_right' | 'arrow_bar_left' | 'arrow_clockwise' | 'arrow_counterclockwise' | 'arrow_down_short' | 'arrow_down_up' | 'arrow_down' | 'arrow_repeat' | 'arrow_right_short' | 'arrow_square_left' | 'arrow_square_right' | 'arrow_up_short' | 'arrow_up' | 'arrows_angle_extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar_chart_line' | 'bar_pencil' | 'battery_charging' | 'battery' | 'bell_fill' | 'bell_plus' | 'bell_slash' | 'bell' | 'binoculars' | 'book_doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business_time' | 'calendar_alt' | 'calendar_check' | 'calendar_day' | 'calendar' | 'call' | 'camera_alt' | 'camera_video_off' | 'camera_video' | 'camera' | 'card_checklist' | 'card_list' | 'card_text' | 'caret_down_fill' | 'caret_left_fill' | 'caret_right_fill' | 'caret_up_fill' | 'chat_dots' | 'chat_left_text' | 'chat_right_text' | 'chat_square_quote' | 'check_circle_fill' | 'check_circle' | 'check' | 'chevron_double_left' | 'chevron_double_right' | 'chevron_down' | 'chevron_left' | 'chevron_right' | 'chevron_up' | 'circle_fill' | 'circle' | 'click_hesitation' | 'click_rage' | 'clipboard_check' | 'clipboard_list_check' | 'clock_history' | 'clock' | 'close' | 'cloud_fog2_fill' | 'code' | 'cog' | 'cogs' | 'collection_play' | 'collection' | 'columns_gap_filled' | 'columns_gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit_card_2_back' | 'credit_card_front' | 'cross' | 'cubes' | 'cursor_trash' | 'dash' | 'dashboard_icn' | 'db_icons/icn_card_clickMap' | 'db_icons/icn_card_errors' | 'db_icons/icn_card_funnel' | 'db_icons/icn_card_funnels' | 'db_icons/icn_card_insights' | 'db_icons/icn_card_library' | 'db_icons/icn_card_mapchart' | 'db_icons/icn_card_pathAnalysis' | 'db_icons/icn_card_performance' | 'db_icons/icn_card_resources' | 'db_icons/icn_card_table' | 'db_icons/icn_card_timeseries' | 'db_icons/icn_card_webVitals' | 'desktop' | 'device' | 'diagram_3' | 'dice_3' | 'dizzy' | 'door_closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis_v' | 'emoji_dizzy' | 'enter' | 'envelope_check' | 'envelope_paper' | 'envelope_x' | 'envelope' | 'errors_icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i_cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation_circle_fill' | 'exclamation_circle' | 'exclamation_triangle' | 'expand_wide' | 'explosion' | 'external_link_alt' | 'eye_slash_fill' | 'eye_slash' | 'eye' | 'fetch' | 'fflag_multi' | 'fflag_single' | 'file_bar_graph' | 'file_code' | 'file_medical_alt' | 'file_pdf' | 'file' | 'files' | 'filetype_js' | 'filetype_pdf' | 'filter' | 'filters/arrow_return_right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu_load' | 'filters/custom' | 'filters/device' | 'filters/dom_complete' | 'filters/duration' | 'filters/error' | 'filters/fetch_failed' | 'filters/fetch' | 'filters/file_code' | 'filters/graphql' | 'filters/i_cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory_load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance_network_request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev_id' | 'filters/state_action' | 'filters/tag_element' | 'filters/ttfb' | 'filters/user_alt' | 'filters/userid' | 'filters/view' | 'flag_na' | 'folder_plus' | 'folder2' | 'fullscreen' | 'funnel/cpu_fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji_angry_fill' | 'funnel/emoji_angry' | 'funnel/emoji_dizzy_fill' | 'funnel/exclamation_circle_fill' | 'funnel/exclamation_circle' | 'funnel/file_earmark_break_fill' | 'funnel/file_earmark_break' | 'funnel/file_earmark_minus_fill' | 'funnel/file_earmark_minus' | 'funnel/file_medical_alt' | 'funnel/file_x' | 'funnel/hdd_fill' | 'funnel/hourglass_top' | 'funnel/image_fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch_exclamation_fill' | 'funnel/sd_card' | 'funnel_fill' | 'funnel_new' | 'funnel' | 'gear_fill' | 'gear' | 'geo_alt_fill_custom' | 'github' | 'graph_up_arrow' | 'graph_up' | 'grid_1x2' | 'grid_3x3' | 'grid_check' | 'grid_horizontal' | 'grid' | 'grip_horizontal' | 'hash' | 'hdd_stack' | 'headset' | 'heart_rate' | 'high_engagement' | 'history' | 'hourglass_start' | 'ic_errors' | 'ic_network' | 'ic_rage' | 'ic_resources' | 'id_card' | 'image' | 'info_circle_fill' | 'info_circle' | 'info_square' | 'info' | 'input_hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag_text' | 'integrations/bugsnag' | 'integrations/cloudwatch_text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch_text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira_text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic_text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay_text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar_text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry_text' | 'integrations/sentry' | 'integrations/slack_bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic_text' | 'integrations/sumologic' | 'integrations/teams_white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal_code' | 'key' | 'layer_group' | 'layers_half' | 'lightbulb_on' | 'lightbulb' | 'link_45deg' | 'list_alt' | 'list_arrow' | 'list_ul' | 'list' | 'lock_alt' | 'low_disc_space' | 'magic' | 'map_marker_alt' | 'memory_ios' | 'memory' | 'mic_mute' | 'mic' | 'minus' | 'mobile' | 'mouse_alt' | 'mouse_pointer_click' | 'network' | 'next1' | 'no_dashboard' | 'no_metrics_chart' | 'no_metrics' | 'no_recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause_circle_fill' | 'pause_fill' | 'pause' | 'pdf_download' | 'pencil_stop' | 'pencil' | 'people' | 'percent' | 'performance_icon' | 'person_border' | 'person_fill' | 'person' | 'pie_chart_fill' | 'pin_fill' | 'play_circle_bold' | 'play_circle_light' | 'play_circle' | 'play_fill_new' | 'play_fill' | 'play_hover' | 'play' | 'plug' | 'plus_circle' | 'plus_lg' | 'plus' | 'pointer_sessions_search' | 'prev1' | 'pulse' | 'puzzle_piece' | 'puzzle' | 'question_circle' | 'question_lg' | 'quote_left' | 'quote_right' | 'quotes' | 'record_btn' | 'record_circle_fill' | 'record_circle' | 'record2' | 'redo_back' | 'redo' | 'redux' | 'remote_control' | 'replay_10' | 'resources_icon' | 'safe_fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share_alt' | 'shield_lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost_split' | 'signup' | 'skip_forward_fill' | 'skip_forward' | 'slack' | 'slash_circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'speedometer2' | 'spinner' | 'star_solid' | 'star' | 'step_forward' | 'stickies' | 'stop_record_circle' | 'stopwatch' | 'store' | 'sync_alt' | 'table_new' | 'table' | 'tablet_android' | 'tachometer_slow' | 'tachometer_slowest' | 'tags' | 'team_funnel' | 'telephone_fill' | 'telephone' | 'terminal' | 'text_paragraph' | 'thermometer_sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user_alt' | 'user_circle' | 'user_friends' | 'user_switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web_vitals' | 'wifi' | 'window_alt' | 'window_restore' | 'window_x' | 'window' | 'zoom_in';
+export type IconNames = 'activity' | 'alarm-clock' | 'alarm-plus' | 'all-sessions' | 'analytics' | 'anchor' | 'arrow-alt-square-right' | 'arrow-bar-left' | 'arrow-clockwise' | 'arrow-counterclockwise' | 'arrow-down-short' | 'arrow-down-up' | 'arrow-down' | 'arrow-repeat' | 'arrow-right-short' | 'arrow-square-left' | 'arrow-square-right' | 'arrow-up-short' | 'arrow-up' | 'arrows-angle-extend' | 'avatar/icn_avatar1' | 'avatar/icn_avatar10' | 'avatar/icn_avatar11' | 'avatar/icn_avatar12' | 'avatar/icn_avatar13' | 'avatar/icn_avatar14' | 'avatar/icn_avatar15' | 'avatar/icn_avatar16' | 'avatar/icn_avatar17' | 'avatar/icn_avatar18' | 'avatar/icn_avatar19' | 'avatar/icn_avatar2' | 'avatar/icn_avatar20' | 'avatar/icn_avatar21' | 'avatar/icn_avatar22' | 'avatar/icn_avatar23' | 'avatar/icn_avatar3' | 'avatar/icn_avatar4' | 'avatar/icn_avatar5' | 'avatar/icn_avatar6' | 'avatar/icn_avatar7' | 'avatar/icn_avatar8' | 'avatar/icn_avatar9' | 'ban' | 'bar-chart-line' | 'bar-pencil' | 'battery-charging' | 'battery' | 'bell-fill' | 'bell-plus' | 'bell-slash' | 'bell' | 'binoculars' | 'book-doc' | 'book' | 'bookmark' | 'broadcast' | 'browser/browser' | 'browser/chrome' | 'browser/edge' | 'browser/electron' | 'browser/facebook' | 'browser/firefox' | 'browser/ie' | 'browser/opera' | 'browser/safari' | 'buildings' | 'bullhorn' | 'business-time' | 'calendar-alt' | 'calendar-check' | 'calendar-day' | 'calendar' | 'call' | 'camera-alt' | 'camera-video-off' | 'camera-video' | 'camera' | 'card-checklist' | 'card-list' | 'card-text' | 'caret-down-fill' | 'caret-left-fill' | 'caret-right-fill' | 'caret-up-fill' | 'chat-dots' | 'chat-left-text' | 'chat-right-text' | 'chat-square-quote' | 'check-circle-fill' | 'check-circle' | 'check' | 'chevron-double-left' | 'chevron-double-right' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-fill' | 'circle' | 'click-hesitation' | 'click-rage' | 'clipboard-check' | 'clipboard-list-check' | 'clock-history' | 'clock' | 'close' | 'cloud-fog2-fill' | 'code' | 'cog' | 'cogs' | 'collection-play' | 'collection' | 'columns-gap-filled' | 'columns-gap' | 'console/error' | 'console/exception' | 'console/info' | 'console/warning' | 'console' | 'controller' | 'cookies' | 'copy' | 'credit-card-2-back' | 'credit-card-front' | 'cross' | 'cubes' | 'cursor-trash' | 'dash' | 'dashboard-icn' | 'db-icons/icn-card-clickMap' | 'db-icons/icn-card-errors' | 'db-icons/icn-card-funnel' | 'db-icons/icn-card-funnels' | 'db-icons/icn-card-insights' | 'db-icons/icn-card-library' | 'db-icons/icn-card-mapchart' | 'db-icons/icn-card-pathAnalysis' | 'db-icons/icn-card-performance' | 'db-icons/icn-card-resources' | 'db-icons/icn-card-table' | 'db-icons/icn-card-timeseries' | 'db-icons/icn-card-webVitals' | 'desktop' | 'device' | 'diagram-3' | 'dice-3' | 'dizzy' | 'door-closed' | 'doublecheck' | 'download' | 'drag' | 'edit' | 'ellipsis-v' | 'emoji-dizzy' | 'enter' | 'envelope-check' | 'envelope-paper' | 'envelope-x' | 'envelope' | 'errors-icon' | 'event/click' | 'event/click_hesitation' | 'event/clickrage' | 'event/code' | 'event/i-cursor' | 'event/input' | 'event/input_hesitation' | 'event/link' | 'event/location' | 'event/mouse_thrashing' | 'event/resize' | 'event/view' | 'exclamation-circle-fill' | 'exclamation-circle' | 'exclamation-triangle' | 'expand-wide' | 'explosion' | 'external-link-alt' | 'eye-slash-fill' | 'eye-slash' | 'eye' | 'fetch' | 'fflag-multi' | 'fflag-single' | 'file-bar-graph' | 'file-code' | 'file-medical-alt' | 'file-pdf' | 'file' | 'files' | 'filetype-js' | 'filetype-pdf' | 'filter' | 'filters/arrow-return-right' | 'filters/browser' | 'filters/click' | 'filters/clickrage' | 'filters/code' | 'filters/console' | 'filters/country' | 'filters/cpu-load' | 'filters/custom' | 'filters/device' | 'filters/dom-complete' | 'filters/duration' | 'filters/error' | 'filters/fetch-failed' | 'filters/fetch' | 'filters/file-code' | 'filters/graphql' | 'filters/i-cursor' | 'filters/input' | 'filters/lcpt' | 'filters/link' | 'filters/location' | 'filters/memory-load' | 'filters/metadata' | 'filters/os' | 'filters/perfromance-network-request' | 'filters/platform' | 'filters/referrer' | 'filters/resize' | 'filters/rev-id' | 'filters/state-action' | 'filters/tag-element' | 'filters/ttfb' | 'filters/user-alt' | 'filters/userid' | 'filters/view' | 'flag-na' | 'folder-plus' | 'folder2' | 'fullscreen' | 'funnel/cpu-fill' | 'funnel/cpu' | 'funnel/dizzy' | 'funnel/emoji-angry-fill' | 'funnel/emoji-angry' | 'funnel/emoji-dizzy-fill' | 'funnel/exclamation-circle-fill' | 'funnel/exclamation-circle' | 'funnel/file-earmark-break-fill' | 'funnel/file-earmark-break' | 'funnel/file-earmark-minus-fill' | 'funnel/file-earmark-minus' | 'funnel/file-medical-alt' | 'funnel/file-x' | 'funnel/hdd-fill' | 'funnel/hourglass-top' | 'funnel/image-fill' | 'funnel/image' | 'funnel/microchip' | 'funnel/mouse' | 'funnel/patch-exclamation-fill' | 'funnel/sd-card' | 'funnel-fill' | 'funnel-new' | 'funnel' | 'gear-fill' | 'gear' | 'geo-alt-fill-custom' | 'github' | 'graph-up-arrow' | 'graph-up' | 'grid-1x2' | 'grid-3x3' | 'grid-check' | 'grid-horizontal' | 'grid' | 'grip-horizontal' | 'hash' | 'hdd-stack' | 'headset' | 'heart-rate' | 'high-engagement' | 'history' | 'hourglass-start' | 'ic-errors' | 'ic-network' | 'ic-rage' | 'ic-resources' | 'id-card' | 'image' | 'info-circle-fill' | 'info-circle' | 'info-square' | 'info' | 'input-hesitation' | 'inspect' | 'integrations/assist' | 'integrations/bugsnag-text' | 'integrations/bugsnag' | 'integrations/cloudwatch-text' | 'integrations/cloudwatch' | 'integrations/datadog' | 'integrations/elasticsearch-text' | 'integrations/elasticsearch' | 'integrations/github' | 'integrations/graphql' | 'integrations/jira-text' | 'integrations/jira' | 'integrations/mobx' | 'integrations/newrelic-text' | 'integrations/newrelic' | 'integrations/ngrx' | 'integrations/openreplay-text' | 'integrations/openreplay' | 'integrations/redux' | 'integrations/rollbar-text' | 'integrations/rollbar' | 'integrations/segment' | 'integrations/sentry-text' | 'integrations/sentry' | 'integrations/slack-bw' | 'integrations/slack' | 'integrations/stackdriver' | 'integrations/sumologic-text' | 'integrations/sumologic' | 'integrations/teams-white' | 'integrations/teams' | 'integrations/vuejs' | 'integrations/zustand' | 'journal-code' | 'key' | 'layer-group' | 'layers-half' | 'lightbulb-on' | 'lightbulb' | 'link-45deg' | 'list-alt' | 'list-arrow' | 'list-ul' | 'list' | 'lock-alt' | 'low-disc-space' | 'magic' | 'map-marker-alt' | 'memory-ios' | 'memory' | 'mic-mute' | 'mic' | 'minus' | 'mobile' | 'mouse-alt' | 'mouse-pointer-click' | 'network' | 'next1' | 'no-dashboard' | 'no-metrics-chart' | 'no-metrics' | 'no-recordings' | 'os/android' | 'os/chrome_os' | 'os/fedora' | 'os/ios' | 'os/linux' | 'os/mac_os_x' | 'os/other' | 'os/ubuntu' | 'os/windows' | 'os' | 'pause-circle-fill' | 'pause-fill' | 'pause' | 'pdf-download' | 'pencil-stop' | 'pencil' | 'people' | 'percent' | 'performance-icon' | 'person-border' | 'person-fill' | 'person' | 'pie-chart-fill' | 'pin-fill' | 'play-circle-bold' | 'play-circle-light' | 'play-circle' | 'play-fill-new' | 'play-fill' | 'play-hover' | 'play' | 'plug' | 'plus-circle' | 'plus-lg' | 'plus' | 'pointer-sessions-search' | 'prev1' | 'pulse' | 'puzzle-piece' | 'puzzle' | 'question-circle' | 'question-lg' | 'quote-left' | 'quote-right' | 'quotes' | 'record-btn' | 'record-circle-fill' | 'record-circle' | 'record2' | 'redo-back' | 'redo' | 'redux' | 'remote-control' | 'replay-10' | 'resources-icon' | 'safe-fill' | 'safe' | 'sandglass' | 'search' | 'search_notification' | 'server' | 'share-alt' | 'shield-lock' | 'side_menu_closed' | 'side_menu_open' | 'signpost-split' | 'signup' | 'skip-forward-fill' | 'skip-forward' | 'slack' | 'slash-circle' | 'sleep' | 'sliders' | 'social/slack' | 'social/trello' | 'speedometer2' | 'spinner' | 'star-solid' | 'star' | 'step-forward' | 'stickies' | 'stop-record-circle' | 'stopwatch' | 'store' | 'sync-alt' | 'table-new' | 'table' | 'tablet-android' | 'tachometer-slow' | 'tachometer-slowest' | 'tags' | 'team-funnel' | 'telephone-fill' | 'telephone' | 'terminal' | 'text-paragraph' | 'thermometer-sun' | 'toggles' | 'tools' | 'trash' | 'turtle' | 'user-alt' | 'user-circle' | 'user-friends' | 'user-switch' | 'users' | 'vendors/graphql' | 'vendors/mobx' | 'vendors/ngrx' | 'vendors/redux' | 'vendors/vuex' | 'web-vitals' | 'wifi' | 'window-alt' | 'window-restore' | 'window-x' | 'window' | 'zoom-in';
interface Props {
- name: OldIconNames;
+ name: IconNames;
size?: number | string;
width?: number | string;
height?: number | string;
@@ -1163,6 +1166,9 @@ const SVG = (props: Props) => {
// case 'filters/state-action':
case 'filters/state-action': return
;
+ // case 'filters/tag-element':
+ case 'filters/tag-element': return
;
+
// case 'filters/ttfb':
case 'filters/ttfb': return
;
@@ -1520,6 +1526,9 @@ const SVG = (props: Props) => {
// case 'mouse-alt':
case 'mouse-alt': return
;
+ // case 'mouse-pointer-click':
+ case 'mouse-pointer-click': return
;
+
case 'network': return
;
@@ -1850,6 +1859,9 @@ const SVG = (props: Props) => {
// case 'user-friends':
case 'user-friends': return
;
+ // case 'user-switch':
+ case 'user-switch': return
;
+
case 'users': return
;
diff --git a/frontend/app/constants/consoleLevels.js b/frontend/app/constants/consoleLevels.js
index f449548d7..38c5ddb3a 100644
--- a/frontend/app/constants/consoleLevels.js
+++ b/frontend/app/constants/consoleLevels.js
@@ -1,5 +1,5 @@
export default {
warning: 'Warnings',
alert: 'Alerts',
- all: 'Log Entires',
+ all: 'Log Entries',
};
diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js
index 10cffed45..bb43947fb 100644
--- a/frontend/app/duck/search.js
+++ b/frontend/app/duck/search.js
@@ -8,7 +8,12 @@ import { errors as errorsRoute, isRoute } from 'App/routes';
import { fetchList as fetchSessionList, fetchAutoplayList } from './sessions';
import { fetchList as fetchErrorsList } from './errors';
import { FilterCategory, FilterKey } from 'Types/filter/filterType';
-import { filtersMap, liveFiltersMap, conditionalFiltersMap, generateFilterOptions } from 'Types/filter/newFilter';
+import {
+ filtersMap,
+ liveFiltersMap,
+ conditionalFiltersMap,
+ generateFilterOptions
+} from "Types/filter/newFilter";
import { DURATION_FILTER } from 'App/constants/storageKeys';
import Period, { CUSTOM_RANGE } from 'Types/app/period';
diff --git a/frontend/app/hooks/useSessionSearchQueryHandler.ts b/frontend/app/hooks/useSessionSearchQueryHandler.ts
index 110b4b72b..08db46b5e 100644
--- a/frontend/app/hooks/useSessionSearchQueryHandler.ts
+++ b/frontend/app/hooks/useSessionSearchQueryHandler.ts
@@ -1,38 +1,44 @@
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
import { useHistory } from 'react-router';
import { createUrlQuery, getFiltersFromQuery } from 'App/utils/search';
interface Props {
+ onBeforeLoad?: () => Promise
;
appliedFilter: any;
applyFilter: any;
loading: boolean;
}
const useSessionSearchQueryHandler = (props: Props) => {
+ const [beforeHookLoaded, setBeforeHookLoaded] = useState(!props.onBeforeLoad);
const { appliedFilter, applyFilter, loading } = props;
const history = useHistory();
useEffect(() => {
- const applyFilterFromQuery = () => {
+ const applyFilterFromQuery = async () => {
if (!loading) {
+ if (props.onBeforeLoad) {
+ await props.onBeforeLoad();
+ setBeforeHookLoaded(true);
+ }
const filter = getFiltersFromQuery(history.location.search, appliedFilter);
applyFilter(filter, true, false);
}
};
- applyFilterFromQuery();
+ void applyFilterFromQuery();
}, [loading]);
useEffect(() => {
const generateUrlQuery = () => {
- if (!loading) {
+ if (!loading && beforeHookLoaded) {
const search: any = createUrlQuery(appliedFilter);
history.replace({ search });
}
};
generateUrlQuery();
- }, [appliedFilter, loading]);
+ }, [appliedFilter, loading, beforeHookLoaded]);
return null;
};
diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx
index f10a4e43f..2266f9b91 100644
--- a/frontend/app/mstore/index.tsx
+++ b/frontend/app/mstore/index.tsx
@@ -5,22 +5,21 @@ import UserStore from './userStore';
import RoleStore from './roleStore';
import APIClient from 'App/api_client';
import FunnelStore from './funnelStore';
-import {
- services
-} from 'App/services';
+import { services } from 'App/services';
import SettingsStore from './settingsStore';
import AuditStore from './auditStore';
import NotificationStore from './notificationStore';
import ErrorStore from './errorStore';
import SessionStore from './sessionStore';
import NotesStore from './notesStore';
-import BugReportStore from './bugReportStore'
-import RecordingsStore from './recordingsStore'
+import BugReportStore from './bugReportStore';
+import RecordingsStore from './recordingsStore';
import AssistMultiviewStore from './assistMultiviewStore';
-import WeeklyReportStore from './weeklyReportConfigStore'
-import AlertStore from './alertsStore'
-import FeatureFlagsStore from "./featureFlagsStore";
+import WeeklyReportStore from './weeklyReportConfigStore';
+import AlertStore from './alertsStore';
+import FeatureFlagsStore from './featureFlagsStore';
import UxtestingStore from './uxtestingStore';
+import TagWatchStore from './tagWatchStore';
export class RootStore {
dashboardStore: DashboardStore;
@@ -37,10 +36,11 @@ export class RootStore {
bugReportStore: BugReportStore;
recordingsStore: RecordingsStore;
assistMultiviewStore: AssistMultiviewStore;
- weeklyReportStore: WeeklyReportStore
- alertsStore: AlertStore
- featureFlagsStore: FeatureFlagsStore
- uxtestingStore: UxtestingStore
+ weeklyReportStore: WeeklyReportStore;
+ alertsStore: AlertStore;
+ featureFlagsStore: FeatureFlagsStore;
+ uxtestingStore: UxtestingStore;
+ tagWatchStore: TagWatchStore;
constructor() {
this.dashboardStore = new DashboardStore();
@@ -61,13 +61,14 @@ export class RootStore {
this.alertsStore = new AlertStore();
this.featureFlagsStore = new FeatureFlagsStore();
this.uxtestingStore = new UxtestingStore();
+ this.tagWatchStore = new TagWatchStore();
}
initClient() {
const client = new APIClient();
- services.forEach(service => {
+ services.forEach((service) => {
service.initClient(client);
- })
+ });
}
}
diff --git a/frontend/app/mstore/tagWatchStore.ts b/frontend/app/mstore/tagWatchStore.ts
new file mode 100644
index 000000000..433b27451
--- /dev/null
+++ b/frontend/app/mstore/tagWatchStore.ts
@@ -0,0 +1,66 @@
+import { makeAutoObservable } from 'mobx';
+import { tagWatchService } from 'App/services';
+import { CreateTag, Tag } from 'App/services/TagWatchService';
+
+export default class TagWatchStore {
+ tags: Tag[] = [];
+ isLoading = false;
+
+ constructor() {
+ makeAutoObservable(this);
+ }
+
+ setTags = (tags: Tag[]) => {
+ this.tags = tags;
+ };
+
+ setLoading = (loading: boolean) => {
+ this.isLoading = loading;
+ };
+
+ getTags = async () => {
+ if (this.isLoading) {
+ return;
+ }
+ this.setLoading(true);
+ try {
+ const tags: Tag[] = await tagWatchService.getTags();
+ this.setTags(tags);
+ return tags;
+ } catch (e) {
+ console.error(e);
+ } finally {
+ this.setLoading(false);
+ }
+ };
+
+ createTag = async (data: CreateTag) => {
+ try {
+ const tagId: number = await tagWatchService.createTag(data);
+ return tagId;
+ } catch (e) {
+ console.error(e);
+ }
+ };
+
+ deleteTag = async (id: number) => {
+ try {
+ await tagWatchService.deleteTag(id);
+ this.setTags(this.tags.filter((t) => t.tagId !== id));
+ } catch (e) {
+ console.error(e);
+ }
+ };
+
+ updateTagName = async (id: number, name: string) => {
+ try {
+ await tagWatchService.updateTagName(id, name);
+ const updatedTag = this.tags.find((t) => t.tagId === id)
+ if (updatedTag) {
+ this.setTags(this.tags.map((t) => t.tagId === id ? { ...updatedTag, name } : t));
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ };
+}
diff --git a/frontend/app/player/web/Screen/Inspector.ts b/frontend/app/player/web/Screen/Inspector.ts
index 028f7f919..330567114 100644
--- a/frontend/app/player/web/Screen/Inspector.ts
+++ b/frontend/app/player/web/Screen/Inspector.ts
@@ -1,19 +1,10 @@
import type Screen from './Screen'
import type Marker from './Marker'
-//import { select } from 'optimal-select';
-
export default class Inspector {
- // private captureCallbacks = [];
- // private bubblingCallbacks = [];
constructor(private screen: Screen, private marker: Marker) {}
private onMouseMove = (e: MouseEvent) => {
- // const { overlay } = this.screen;
- // if (!overlay.contains(e.target)) {
- // return;
- // }
-
e.stopPropagation();
const target = this.screen.getElementFromPoint(e);
@@ -34,30 +25,15 @@ export default class Inspector {
return
}
this.clickCallback && this.clickCallback({ target });
- // const targets = [ target ];
- // while (target.parentElement !== null) {
- // target = target.parentElement;
- // targets.push(target);
- // }
- // for (let i = targets.length - 1; i >= 0; i--) {
- // for (let j = 0; j < this.captureCallbacks.length; j++) {
- // this.captureCallbacks[j]({ target: targets[i] });
- // }
- // }
-
- // onTargetClick(select(markedTarget, { root: this.screen.document }));
}
- // addClickListener(callback, useCapture = false) {
- // if (useCapture) {
- // this.captureCallbacks.push(callback);
- // } else {
- // //this.bubblingCallbacks.push(callback);
- // }
- // }
+ addClickListener(callback: (el: { target: Element }) => void) {
+ this.clickCallback = callback
+ }
- private clickCallback: (e: { target: Element }) => void | null = null
- enable(clickCallback?: Inspector['clickCallback']) {
+ private clickCallback: (e: { target: Element }) => void = () => {}
+
+ enable() {
this.screen.overlay.addEventListener('mousemove', this.onMouseMove)
this.screen.overlay.addEventListener('mouseleave', this.onOverlayLeave)
this.screen.overlay.addEventListener('click', this.onMarkClick)
diff --git a/frontend/app/player/web/Screen/Marker.ts b/frontend/app/player/web/Screen/Marker.ts
index d55ed5be3..25d22ac92 100644
--- a/frontend/app/player/web/Screen/Marker.ts
+++ b/frontend/app/player/web/Screen/Marker.ts
@@ -1,5 +1,6 @@
-import type Screen from './Screen'
+import type Screen from './Screen';
import styles from './marker.module.css';
+import { finder } from '@medv/finder';
const metaCharsMap = {
'&': '&',
@@ -9,7 +10,7 @@ const metaCharsMap = {
"'": ''',
'/': '/',
'`': '`',
- '=': '='
+ '=': '=',
};
function escapeHtml(str: string) {
@@ -19,29 +20,30 @@ function escapeHtml(str: string) {
});
}
-
function escapeRegExp(string: string) {
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function safeString(string: string) {
- return (escapeHtml(escapeRegExp(string)))
+ return escapeHtml(escapeRegExp(string));
}
export default class Marker {
private _target: Element | null = null;
private selector: string | null = null;
- private tooltip: HTMLDivElement
- private marker: HTMLDivElement
+ private readonly tooltip: HTMLDivElement;
+ private readonly tooltipSelector: HTMLDivElement;
+ private readonly tooltipHint: HTMLDivElement;
+ private marker: HTMLDivElement;
- constructor(overlay: HTMLElement, private readonly screen: Screen) {
+ constructor(private readonly overlay: HTMLElement, private readonly screen: Screen) {
this.tooltip = document.createElement('div');
this.tooltip.className = styles.tooltip;
- this.tooltip.appendChild(document.createElement('div'));
-
- const htmlStr = document.createElement('div');
- htmlStr.innerHTML = 'Right-click > Inspect for more details.';
- this.tooltip.appendChild(htmlStr);
+ this.tooltipSelector = document.createElement('div');
+ this.tooltipHint = document.createElement('div');
+ this.tooltipHint.innerText = '(click to tag element)'
+ this.tooltipHint.className = styles.tooltipHint;
+ this.tooltip.append(this.tooltipSelector, this.tooltipHint);
const marker = document.createElement('div');
marker.className = styles.marker;
@@ -78,19 +80,17 @@ export default class Marker {
}
unmark() {
- this.mark(null)
+ this.mark(null);
}
private autodefineTarget() {
- // TODO: put to Screen
- if (this.selector) {
+ if (this.selector && this.screen.document) {
try {
const fitTargets = this.screen.document.querySelectorAll(this.selector);
if (fitTargets.length === 0) {
this._target = null;
} else {
- // TODO: fix getCursorTarget()?
- // this._target = fitTargets[0];
+ this._target = fitTargets[0];
// const cursorTarget = this.screen.getCursorTarget();
// fitTargets.forEach((target) => {
// if (target.contains(cursorTarget)) {
@@ -108,27 +108,23 @@ export default class Marker {
markBySelector(selector: string) {
this.selector = selector;
+ this.lastSelector = selector;
this.autodefineTarget();
this.redraw();
}
+ lastSelector = '';
private getTagString(el: Element) {
- const attrs = el.attributes;
- let str = `${el.tagName.toLowerCase()}`;
-
- for (let i = 0; i < attrs.length; i++) {
- let k = attrs[i];
- const attribute = k.name;
- if (attribute === 'class') {
- str += `${'.' + safeString(k.value).split(' ').join('.')}`;
- }
-
- if (attribute === 'id') {
- str += `${'#' + safeString(k.value).split(' ').join('#')}`;
- }
- }
-
- return str;
+ if (!this.screen.document) return '';
+ const selector = finder(el, {
+ root: this.screen.document.body,
+ seedMinLength: 3,
+ optimizedMinLength: 2,
+ threshold: 1000,
+ maxNumberOfTries: 10_000,
+ });
+ this.lastSelector = selector;
+ return selector
}
redraw() {
@@ -146,6 +142,17 @@ export default class Marker {
this.marker.style.width = rect.width + 'px';
this.marker.style.height = rect.height + 'px';
- this.tooltip.firstChild.innerHTML = this.getTagString(this._target);
+ const replayScale = this.screen.getScale()
+ if (replayScale < 1) {
+ const upscale = (1 / replayScale).toFixed(3);
+ const yShift = ((1 - replayScale)/2) * 100;
+ this.tooltip.style.transform = `scale(${upscale}) translateY(-${yShift + 0.5}%)`
+ }
+ this.tooltipSelector.textContent = this.getTagString(this._target);
+ }
+
+
+ clean() {
+ this.marker.remove();
}
}
diff --git a/frontend/app/player/web/Screen/marker.module.css b/frontend/app/player/web/Screen/marker.module.css
index fe88efc9e..997c8f0f9 100644
--- a/frontend/app/player/web/Screen/marker.module.css
+++ b/frontend/app/player/web/Screen/marker.module.css
@@ -39,24 +39,21 @@
position: absolute;
left: 0;
bottom: 100%;
- padding: 15px;
+ padding: 8px;
+ max-width: 600px;
box-shadow: 2px 2px 1px rgba(40, 40, 100, .3);
z-index: 999;
border-radius: 3px;
background-color: #202124;
- min-width: 400px;
- font-size: 20px !important;
+ min-width: 300px;
+ font-size: 16px !important;
+ color:#9BBBDC;
+}
- & div:first-child {
- max-width: 600px;
- height: 22px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- & div:last-child {
- font-size: 18px;
- margin-top: 10px;
- color: $tealx;
- }
+.tooltipHint {
+ margin-top: 4px;
+ font-size: 14px;
+ width: 100%;
+ text-align: center;
+ color: rgba(138, 170, 201, 0.8);
}
\ No newline at end of file
diff --git a/frontend/app/player/web/WebPlayer.ts b/frontend/app/player/web/WebPlayer.ts
index 23b36ed7c..0a23f2c7d 100644
--- a/frontend/app/player/web/WebPlayer.ts
+++ b/frontend/app/player/web/WebPlayer.ts
@@ -16,6 +16,8 @@ export default class WebPlayer extends Player {
...TargetMarker.INITIAL_STATE,
...MessageManager.INITIAL_STATE,
...MessageLoader.INITIAL_STATE,
+ ...InspectorController.INITIAL_STATE,
+
liveTimeTravel: false,
inspectorMode: false,
}
@@ -65,7 +67,7 @@ export default class WebPlayer extends Player {
}
this.targetMarker = new TargetMarker(this.screen, wpState)
- this.inspectorController = new InspectorController(screen)
+ this.inspectorController = new InspectorController(screen, wpState)
const endTime = session.duration?.valueOf() || 0
@@ -126,7 +128,7 @@ export default class WebPlayer extends Player {
this.inspectorController.marker?.mark(e)
}
- toggleInspectorMode = (flag: boolean, clickCallback?: Parameters[0]) => {
+ toggleInspectorMode = (flag: boolean) => {
if (typeof flag !== 'boolean') {
const { inspectorMode } = this.wpState.get()
flag = !inspectorMode
@@ -135,13 +137,17 @@ export default class WebPlayer extends Player {
if (flag) {
this.pause()
this.wpState.update({ inspectorMode: true })
- return this.inspectorController.enableInspector(clickCallback)
+ return this.inspectorController.enableInspector()
} else {
this.inspectorController.disableInspector()
this.wpState.update({ inspectorMode: false })
}
}
+ markBySelector = (selector: string) => {
+ this.inspectorController.markBySelector(selector)
+ }
+
// Target Marker
setActiveTarget = (...args: Parameters) => {
this.targetMarker.setActiveTarget(...args)
diff --git a/frontend/app/player/web/addons/InspectorController.ts b/frontend/app/player/web/addons/InspectorController.ts
index 7c4c48690..31832ad8f 100644
--- a/frontend/app/player/web/addons/InspectorController.ts
+++ b/frontend/app/player/web/addons/InspectorController.ts
@@ -1,72 +1,58 @@
-import Marker from '../Screen/Marker'
-import Inspector from '../Screen/Inspector'
-import Screen from '../Screen/Screen'
-import type { Dimensions } from '../Screen/types'
-
+import type { Store } from 'Player';
+import { State } from 'Player/web/addons/TargetMarker';
+import Marker from '../Screen/Marker';
+import Inspector from '../Screen/Inspector';
+import Screen, { ScaleMode } from '../Screen/Screen';
+import type { Dimensions } from '../Screen/types';
export default class InspectorController {
- private substitutor: Screen | null = null
- private inspector: Inspector | null = null
- marker: Marker | null = null
- constructor(private screen: Screen) {
- screen.overlay.addEventListener('contextmenu', () => {
- screen.overlay.style.display = 'none'
- const doc = screen.document
- if (!doc) { return }
- const returnOverlay = () => {
- screen.overlay.style.display = 'block'
- doc.removeEventListener('mousemove', returnOverlay)
- doc.removeEventListener('mouseclick', returnOverlay) // TODO: prevent default in case of input selection
- }
- doc.addEventListener('mousemove', returnOverlay)
- doc.addEventListener('mouseclick', returnOverlay)
- })
+ static INITIAL_STATE = {
+ tagSelector: '',
+ }
+ private substitutor: Screen | null = null;
+ private inspector: Inspector | null = null;
+ marker: Marker | null = null;
+
+ constructor(private screen: Screen, private readonly store: Store<{ tagSelector: string }>) {
+ screen.overlay.addEventListener('contextmenu', () => {
+ screen.overlay.style.display = 'none';
+ const doc = screen.document;
+ if (!doc) {
+ return;
+ }
+ const returnOverlay = () => {
+ screen.overlay.style.display = 'block';
+ doc.removeEventListener('mousemove', returnOverlay);
+ doc.removeEventListener('mouseclick', returnOverlay); // TODO: prevent default in case of input selection
+ };
+ doc.addEventListener('mousemove', returnOverlay);
+ doc.addEventListener('mouseclick', returnOverlay);
+ });
}
scale(dims: Dimensions) {
- if (this.substitutor) {
- this.substitutor.scale(dims)
- }
+ this.screen.scale(dims);
}
- enableInspector(clickCallback?: (e: { target: Element }) => void): Document | null {
- const parent = this.screen.getParentElement()
- if (!parent) return null;
- if (!this.substitutor) {
- this.substitutor = new Screen()
- this.marker = new Marker(this.substitutor.overlay, this.substitutor)
- this.inspector = new Inspector(this.substitutor, this.marker)
- //this.inspector.addClickListener(clickCallback, true)
- this.substitutor.attach(parent)
- }
+ enableInspector(): Document | null {
+ this.marker = new Marker(this.screen.overlay, this.screen);
+ this.inspector = new Inspector(this.screen, this.marker);
+ this.inspector.addClickListener(() => {
+ this.store.update({ tagSelector: this.marker?.lastSelector ?? '' })
+ });
- this.substitutor.display(false)
+ this.inspector?.enable();
+ return this.screen.document;
+ }
- const docElement = this.screen.document?.documentElement // this.substitutor.document?.importNode(
- const doc = this.substitutor.document
- if (doc && docElement) {
- doc.open()
- doc.write(docElement.outerHTML)
- doc.close()
-
- // TODO! : copy stylesheets & cssRules?
- }
- this.screen.display(false);
- this.inspector.enable(clickCallback);
- this.substitutor.display(true);
- return doc;
+ markBySelector(selector: string) {
+ this.marker?.markBySelector(selector);
}
disableInspector() {
- if (this.substitutor) {
- const doc = this.substitutor.document;
- if (doc) {
- doc.documentElement.innerHTML = "";
- }
- this.inspector.clean();
- this.substitutor.display(false);
- }
- this.screen.display(true);
+ this.inspector?.clean();
+ this.inspector = null;
+ this.marker?.clean();
+ this.marker = null;
}
-
}
diff --git a/frontend/app/player/web/messages/RawMessageReader.gen.ts b/frontend/app/player/web/messages/RawMessageReader.gen.ts
index a392655ba..1ca3c9ce6 100644
--- a/frontend/app/player/web/messages/RawMessageReader.gen.ts
+++ b/frontend/app/player/web/messages/RawMessageReader.gen.ts
@@ -741,6 +741,14 @@ export default class RawMessageReader extends PrimitiveReader {
};
}
+ case 120: {
+ const tagId = this.readInt(); if (tagId === null) { return resetPointer() }
+ return {
+ tp: MType.TagTrigger,
+ tagId,
+ };
+ }
+
case 93: {
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
const length = this.readUint(); if (length === null) { return resetPointer() }
diff --git a/frontend/app/player/web/messages/message.gen.ts b/frontend/app/player/web/messages/message.gen.ts
index b121ae69f..c6286dc32 100644
--- a/frontend/app/player/web/messages/message.gen.ts
+++ b/frontend/app/player/web/messages/message.gen.ts
@@ -63,6 +63,7 @@ import type {
RawTabChange,
RawTabData,
RawCanvasNode,
+ RawTagTrigger,
RawIosEvent,
RawIosScreenChanges,
RawIosClickEvent,
@@ -196,6 +197,8 @@ export type TabData = RawTabData & Timed
export type CanvasNode = RawCanvasNode & Timed
+export type TagTrigger = RawTagTrigger & Timed
+
export type IosEvent = RawIosEvent & Timed
export type IosScreenChanges = RawIosScreenChanges & Timed
diff --git a/frontend/app/player/web/messages/raw.gen.ts b/frontend/app/player/web/messages/raw.gen.ts
index ca3af1886..291ead61d 100644
--- a/frontend/app/player/web/messages/raw.gen.ts
+++ b/frontend/app/player/web/messages/raw.gen.ts
@@ -61,6 +61,7 @@ export const enum MType {
TabChange = 117,
TabData = 118,
CanvasNode = 119,
+ TagTrigger = 120,
IosEvent = 93,
IosScreenChanges = 96,
IosClickEvent = 100,
@@ -494,6 +495,11 @@ export interface RawCanvasNode {
timestamp: number,
}
+export interface RawTagTrigger {
+ tp: MType.TagTrigger,
+ tagId: number,
+}
+
export interface RawIosEvent {
tp: MType.IosEvent,
timestamp: number,
@@ -586,4 +592,4 @@ export interface RawIosIssueEvent {
}
-export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawIosEvent | RawIosScreenChanges | RawIosClickEvent | RawIosInputEvent | RawIosPerformanceEvent | RawIosLog | RawIosInternalError | RawIosNetworkCall | RawIosSwipeEvent | RawIosIssueEvent;
+export type RawMessage = RawTimestamp | RawSetPageLocation | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawRedux | RawVuex | RawMobX | RawNgRx | RawGraphQl | RawPerformanceTrack | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawIosEvent | RawIosScreenChanges | RawIosClickEvent | RawIosInputEvent | RawIosPerformanceEvent | RawIosLog | RawIosInternalError | RawIosNetworkCall | RawIosSwipeEvent | RawIosIssueEvent;
diff --git a/frontend/app/player/web/messages/tracker-legacy.gen.ts b/frontend/app/player/web/messages/tracker-legacy.gen.ts
index 09d7576c6..48b0ed623 100644
--- a/frontend/app/player/web/messages/tracker-legacy.gen.ts
+++ b/frontend/app/player/web/messages/tracker-legacy.gen.ts
@@ -62,6 +62,7 @@ export const TP_MAP = {
117: MType.TabChange,
118: MType.TabData,
119: MType.CanvasNode,
+ 120: MType.TagTrigger,
93: MType.IosEvent,
96: MType.IosScreenChanges,
100: MType.IosClickEvent,
diff --git a/frontend/app/player/web/messages/tracker.gen.ts b/frontend/app/player/web/messages/tracker.gen.ts
index 6ad41e111..47d266617 100644
--- a/frontend/app/player/web/messages/tracker.gen.ts
+++ b/frontend/app/player/web/messages/tracker.gen.ts
@@ -509,8 +509,13 @@ type TrCanvasNode = [
timestamp: number,
]
+type TrTagTrigger = [
+ type: 120,
+ tagId: number,
+]
-export type TrackerMessage = TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequestDeprecated | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrNetworkRequest | TrWSChannel | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrTabChange | TrTabData | TrCanvasNode
+
+export type TrackerMessage = TrTimestamp | TrSetPageLocation | TrSetViewportSize | TrSetViewportScroll | TrCreateDocument | TrCreateElementNode | TrCreateTextNode | TrMoveNode | TrRemoveNode | TrSetNodeAttribute | TrRemoveNodeAttribute | TrSetNodeData | TrSetNodeScroll | TrSetInputTarget | TrSetInputValue | TrSetInputChecked | TrMouseMove | TrNetworkRequestDeprecated | TrConsoleLog | TrPageLoadTiming | TrPageRenderTiming | TrCustomEvent | TrUserID | TrUserAnonymousID | TrMetadata | TrCSSInsertRule | TrCSSDeleteRule | TrFetch | TrProfiler | TrOTable | TrStateAction | TrRedux | TrVuex | TrMobX | TrNgRx | TrGraphQL | TrPerformanceTrack | TrStringDict | TrSetNodeAttributeDict | TrResourceTimingDeprecated | TrConnectionInformation | TrSetPageVisibility | TrLoadFontFace | TrSetNodeFocus | TrLongTask | TrSetNodeAttributeURLBased | TrSetCSSDataURLBased | TrTechnicalInfo | TrCustomIssue | TrCSSInsertRuleURLBased | TrMouseClick | TrCreateIFrameDocument | TrAdoptedSSReplaceURLBased | TrAdoptedSSInsertRuleURLBased | TrAdoptedSSDeleteRule | TrAdoptedSSAddOwner | TrAdoptedSSRemoveOwner | TrJSException | TrZustand | TrBatchMetadata | TrPartitionedMessage | TrNetworkRequest | TrWSChannel | TrInputChange | TrSelectionChange | TrMouseThrashing | TrUnbindNodes | TrResourceTiming | TrTabChange | TrTabData | TrCanvasNode | TrTagTrigger
export default function translate(tMsg: TrackerMessage): RawMessage | null {
switch(tMsg[0]) {
@@ -1028,6 +1033,13 @@ export default function translate(tMsg: TrackerMessage): RawMessage | null {
}
}
+ case 120: {
+ return {
+ tp: MType.TagTrigger,
+ tagId: tMsg[1],
+ }
+ }
+
default:
return null
}
diff --git a/frontend/app/services/TagWatchService.ts b/frontend/app/services/TagWatchService.ts
new file mode 100644
index 000000000..f8c9ff461
--- /dev/null
+++ b/frontend/app/services/TagWatchService.ts
@@ -0,0 +1,38 @@
+import BaseService from "App/services/BaseService";
+
+export interface CreateTag {
+ name: string;
+ selector: string;
+ ignoreClickRage: boolean;
+ ignoreDeadClick: boolean;
+}
+
+export interface Tag extends CreateTag {
+ tagId: number;
+}
+
+export default class TagWatchService extends BaseService {
+ createTag(data: CreateTag) {
+ return this.client.post('/tags', data)
+ .then(r => r.json())
+ .then((response: { data: any; }) => response.data || {})
+ }
+
+ getTags() {
+ return this.client.get('/tags')
+ .then(r => r.json())
+ .then((response: { data: any; }) => response.data || {})
+ }
+
+ deleteTag(id: number) {
+ return this.client.delete(`/tags/${id}`)
+ .then(r => r.json())
+ .then((response: { data: any; }) => response.data || {})
+ }
+
+ updateTagName(id: number, name: string) {
+ return this.client.put(`/tags/${id}`, { name })
+ .then(r => r.json())
+ .then((response: { data: any; }) => response.data || {})
+ }
+}
\ No newline at end of file
diff --git a/frontend/app/services/index.ts b/frontend/app/services/index.ts
index 14d6af804..100c81b7b 100644
--- a/frontend/app/services/index.ts
+++ b/frontend/app/services/index.ts
@@ -1,23 +1,24 @@
-import DashboardService from "./DashboardService";
-import MetricService from "./MetricService";
-import FunnelService from "./FunnelService";
-import SessionSerivce from "./SessionService";
-import UserService from "./UserService";
+import DashboardService from './DashboardService';
+import MetricService from './MetricService';
+import FunnelService from './FunnelService';
+import SessionService from './SessionService';
+import UserService from './UserService';
import AuditService from './AuditService';
-import ErrorService from "./ErrorService";
-import NotesService from "./NotesService";
-import RecordingsService from "./RecordingsService";
-import ConfigService from './ConfigService'
-import AlertsService from './AlertsService'
-import WebhookService from './WebhookService'
-import HealthService from "./HealthService";
-import FFlagsService from "App/services/FFlagsService";
-import AssistStatsService from './AssistStatsService'
-import UxtestingService from './UxtestingService'
+import ErrorService from './ErrorService';
+import NotesService from './NotesService';
+import RecordingsService from './RecordingsService';
+import ConfigService from './ConfigService';
+import AlertsService from './AlertsService';
+import WebhookService from './WebhookService';
+import HealthService from './HealthService';
+import FFlagsService from 'App/services/FFlagsService';
+import AssistStatsService from './AssistStatsService';
+import UxtestingService from './UxtestingService';
+import TagWatchService from 'App/services/TagWatchService';
-export const dashboardService = new DashboardService();
+export const dashboardService = new DashboardService();
export const metricService = new MetricService();
-export const sessionService = new SessionSerivce();
+export const sessionService = new SessionService();
export const userService = new UserService();
export const funnelService = new FunnelService();
export const auditService = new AuditService();
@@ -36,6 +37,8 @@ export const assistStatsService = new AssistStatsService();
export const uxtestingService = new UxtestingService();
+export const tagWatchService = new TagWatchService();
+
export const services = [
dashboardService,
metricService,
@@ -52,5 +55,6 @@ export const services = [
healthService,
fflagsService,
assistStatsService,
- uxtestingService
-]
\ No newline at end of file
+ uxtestingService,
+ tagWatchService,
+];
diff --git a/frontend/app/svg/icons/filters/tag-element.svg b/frontend/app/svg/icons/filters/tag-element.svg
new file mode 100644
index 000000000..4f60cfef1
--- /dev/null
+++ b/frontend/app/svg/icons/filters/tag-element.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/frontend/app/svg/icons/mouse-pointer-click.svg b/frontend/app/svg/icons/mouse-pointer-click.svg
new file mode 100644
index 000000000..d3578f05d
--- /dev/null
+++ b/frontend/app/svg/icons/mouse-pointer-click.svg
@@ -0,0 +1,7 @@
+
diff --git a/frontend/app/svg/icons/user-switch.svg b/frontend/app/svg/icons/user-switch.svg
new file mode 100644
index 000000000..6ea7b910d
--- /dev/null
+++ b/frontend/app/svg/icons/user-switch.svg
@@ -0,0 +1,5 @@
+
diff --git a/frontend/app/types/filter/filterType.ts b/frontend/app/types/filter/filterType.ts
index f7dc00d37..c6855dfd5 100644
--- a/frontend/app/types/filter/filterType.ts
+++ b/frontend/app/types/filter/filterType.ts
@@ -64,6 +64,8 @@ export const setQueryParamKeyFromFilterkey = (filterKey: string) => {
return 'duration';
case FilterKey.FEATURE_FLAG:
return 'feature_flag';
+ case FilterKey.TAGGED_ELEMENT:
+ return 'tnw'
}
};
@@ -149,6 +151,8 @@ export const getFilterKeyTypeByKey = (key: string) => {
return FilterKey.DURATION;
case FilterKey.FEATURE_FLAG:
return 'feature_flag';
+ case 'tnw':
+ return FilterKey.TAGGED_ELEMENT
}
};
@@ -306,4 +310,5 @@ export enum FilterKey {
CLICKMAP_URL = 'clickMapUrl',
FEATURE_FLAG = 'featureFlag',
+ TAGGED_ELEMENT = 'tag',
}
diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js
index 20542c3c8..6867234db 100644
--- a/frontend/app/types/filter/newFilter.js
+++ b/frontend/app/types/filter/newFilter.js
@@ -262,6 +262,17 @@ export const filters = [
operatorOptions: filterOptions.getOperatorsByKeys(['is']),
icon: 'filters/duration'
},
+ {
+ key: FilterKey.TAGGED_ELEMENT,
+ type: FilterType.MULTIPLE_DROPDOWN,
+ category: FilterCategory.RECORDING_ATTRIBUTES,
+ label: 'Tagged Element',
+ operator: 'is',
+ isEvent: true,
+ icon: 'filters/tag-element',
+ operatorOptions: filterOptions.getOperatorsByKeys(['is']),
+ options: [],
+ },
{
key: FilterKey.USER_COUNTRY,
type: FilterType.MULTIPLE_DROPDOWN,
@@ -721,6 +732,15 @@ export const addElementToFiltersMap = (
};
};
+export const addOptionsToFilter = (
+ key,
+ options,
+) => {
+ if (filtersMap[key] && filtersMap[key].options) {
+ filtersMap[key].options = options
+ }
+}
+
export const addElementToFlagConditionsMap = (
category = FilterCategory.METADATA,
key,
@@ -830,7 +850,11 @@ export default Record({
if (type === FilterKey.METADATA) {
_filter = filtersMap[filter.source];
} else {
- _filter = filtersMap[type];
+ if (filtersMap[filter.key]) {
+ _filter = filtersMap[filter.key]
+ } else {
+ _filter = filtersMap[type];
+ }
}
}
diff --git a/frontend/app/utils/search.ts b/frontend/app/utils/search.ts
index 0f1355689..756945d38 100644
--- a/frontend/app/utils/search.ts
+++ b/frontend/app/utils/search.ts
@@ -48,18 +48,18 @@ export const getFiltersFromQuery = (search: string, filter: any) => {
return;
}
- const entires = getQueryObject(search);
- const period: any = getPeriodFromEntries(entires);
- const filters = getFiltersFromEntries(entires);
+ const entries = getQueryObject(search);
+ const period: any = getPeriodFromEntries(entries);
+ const filters = getFiltersFromEntries(entries);
return Filter({ filters, rangeValue: period.rangeName, startDate: period.start, endDate: period.end });
};
-const getFiltersFromEntries = (entires: any) => {
+const getFiltersFromEntries = (entries: any) => {
const _filters: any = { ...filtersMap };
const filters: any = [];
- if (entires.length > 0) {
- entires.forEach((item: any) => {
+ if (entries.length > 0) {
+ entries.forEach((item: any) => {
if (!item.key || !item.value) {
return;
}
@@ -72,29 +72,33 @@ const getFiltersFromEntries = (entires: any) => {
const sourceArr = tmp[1] ? tmp[1].split('|') : [];
const sourceOperator = sourceArr.shift();
- if (filterKey) {
- filter.type = filterKey;
- filter.key = filterKey;
+ if (filterKey && _filters[filterKey]) {
+ filter = _filters[filterKey];
+ filter.value = valueArr;
} else {
- filter = _filters[item.key];
- if (!!filter) {
- filter.type = filter.key;
- filter.key = filter.key;
+ if (filterKey) {
+ filter.type = filterKey;
+ filter.key = filterKey;
+ } else {
+ filter = _filters[item.key];
+ if (!!filter) {
+ filter.type = filter.key;
+ }
}
- }
- if (!filter) {
- return;
- }
+ if (!filter) {
+ return;
+ }
- filter.value = valueArr;
- filter.operator = operator;
- if (filter.icon === 'filters/metadata') {
- filter.source = filter.type;
- filter.type = 'MULTIPLE';
- } else {
- filter.source = sourceArr && sourceArr.length > 0 ? sourceArr : null;
- filter.sourceOperator = !!sourceOperator ? decodeURI(sourceOperator) : null;
+ filter.value = valueArr;
+ filter.operator = operator;
+ if (filter.icon === 'filters/metadata') {
+ filter.source = filter.type;
+ filter.type = 'MULTIPLE';
+ } else {
+ filter.source = sourceArr && sourceArr.length > 0 ? sourceArr : null;
+ filter.sourceOperator = !!sourceOperator ? decodeURI(sourceOperator) : null;
+ }
}
if (!filter.filters || filter.filters.size === 0) {
@@ -106,15 +110,15 @@ const getFiltersFromEntries = (entires: any) => {
return filters;
};
-const getPeriodFromEntries = (entires: any) => {
- const rangeFilter = entires.find(({ key }: any) => key === 'range');
+const getPeriodFromEntries = (entries: any) => {
+ const rangeFilter = entries.find(({ key }: any) => key === 'range');
if (!rangeFilter) {
return Period();
}
if (rangeFilter.value === CUSTOM_RANGE) {
- const start = entires.find(({ key }: any) => key === 'rStart').value;
- const end = entires.find(({ key }: any) => key === 'rEnd').value;
+ const start = entries.find(({ key }: any) => key === 'rStart').value;
+ const end = entries.find(({ key }: any) => key === 'rEnd').value;
return Period({ rangeName: rangeFilter.value, start, end });
}
diff --git a/frontend/package.json b/frontend/package.json
index de466fd57..9ca4e1423 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -27,6 +27,7 @@
"@ant-design/icons": "^5.2.5",
"@babel/plugin-transform-private-methods": "^7.23.3",
"@floating-ui/react-dom-interactions": "^0.10.3",
+ "@medv/finder": "^3.1.0",
"@sentry/browser": "^5.21.1",
"@svg-maps/world": "^1.0.1",
"@svgr/webpack": "^6.2.1",
diff --git a/frontend/scripts/icons.js b/frontend/scripts/icons.js
index 50389745d..29bc01c03 100644
--- a/frontend/scripts/icons.js
+++ b/frontend/scripts/icons.js
@@ -112,11 +112,11 @@ ${iconPaths.map(icon => ` ${titleCase(icon.fileName)}`).join(',\n')}
} from './Icons'
-// export type IconNames = ${icons.map((icon) => '\'' + icon.slice(0, -4).replaceAll('-', '_') + '\'').join(' | ')};
-export type OldIconNames = ${icons.map((icon) => '\'' + icon.slice(0, -4) + '\'').join(' | ')};
+// export type NewIconNames = ${icons.map((icon) => '\'' + icon.slice(0, -4).replaceAll('-', '_') + '\'').join(' | ')};
+export type IconNames = ${icons.map((icon) => '\'' + icon.slice(0, -4) + '\'').join(' | ')};
interface Props {
- name: OldIconNames;
+ name: IconNames;
size?: number | string;
width?: number | string;
height?: number | string;
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 156721e02..6923417f2 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -2762,6 +2762,13 @@ __metadata:
languageName: node
linkType: hard
+"@medv/finder@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "@medv/finder@npm:3.1.0"
+ checksum: 43f8f5af50d51e2609a26b7f1306cf0b170967df9aa0169d70319215957d47066c35d888d8d049d47384639e28c1bba96bc9c09558bb7368ea43c8b0b6e03836
+ languageName: node
+ linkType: hard
+
"@mrmlnc/readdir-enhanced@npm:^2.2.1":
version: 2.2.1
resolution: "@mrmlnc/readdir-enhanced@npm:2.2.1"
@@ -18091,6 +18098,7 @@ __metadata:
"@babel/runtime": ^7.23.2
"@floating-ui/react-dom-interactions": ^0.10.3
"@jest/globals": ^29.7.0
+ "@medv/finder": ^3.1.0
"@openreplay/sourcemap-uploader": ^3.0.8
"@sentry/browser": ^5.21.1
"@storybook/addon-actions": ^6.5.12
diff --git a/mobs/messages.rb b/mobs/messages.rb
index 9c6b7e0ba..02954dfad 100644
--- a/mobs/messages.rb
+++ b/mobs/messages.rb
@@ -518,6 +518,10 @@ message 119, 'CanvasNode' do
uint 'Timestamp'
end
+message 120, 'TagTrigger', :replayer => :devtools do
+ int 'TagId'
+end
+
## Backend-only
message 125, 'IssueEvent', :replayer => false, :tracker => false do
uint 'MessageID'
diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json
index b99cc9dfb..a996172db 100644
--- a/tracker/tracker/package.json
+++ b/tracker/tracker/package.json
@@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
- "version": "12.0.0",
+ "version": "12.0.1-2",
"keywords": [
"logging",
"replay"
diff --git a/tracker/tracker/src/common/messages.gen.ts b/tracker/tracker/src/common/messages.gen.ts
index 188ba2caa..be0e23b4a 100644
--- a/tracker/tracker/src/common/messages.gen.ts
+++ b/tracker/tracker/src/common/messages.gen.ts
@@ -73,6 +73,7 @@ export declare const enum Type {
TabChange = 117,
TabData = 118,
CanvasNode = 119,
+ TagTrigger = 120,
}
@@ -580,6 +581,11 @@ export type CanvasNode = [
/*timestamp:*/ number,
]
+export type TagTrigger = [
+ /*type:*/ Type.TagTrigger,
+ /*tagId:*/ number,
+]
-type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode
+
+type Message = Timestamp | SetPageLocation | SetViewportSize | SetViewportScroll | CreateDocument | CreateElementNode | CreateTextNode | MoveNode | RemoveNode | SetNodeAttribute | RemoveNodeAttribute | SetNodeData | SetNodeScroll | SetInputTarget | SetInputValue | SetInputChecked | MouseMove | NetworkRequestDeprecated | ConsoleLog | PageLoadTiming | PageRenderTiming | CustomEvent | UserID | UserAnonymousID | Metadata | CSSInsertRule | CSSDeleteRule | Fetch | Profiler | OTable | StateAction | Redux | Vuex | MobX | NgRx | GraphQL | PerformanceTrack | StringDict | SetNodeAttributeDict | ResourceTimingDeprecated | ConnectionInformation | SetPageVisibility | LoadFontFace | SetNodeFocus | LongTask | SetNodeAttributeURLBased | SetCSSDataURLBased | TechnicalInfo | CustomIssue | CSSInsertRuleURLBased | MouseClick | CreateIFrameDocument | AdoptedSSReplaceURLBased | AdoptedSSInsertRuleURLBased | AdoptedSSDeleteRule | AdoptedSSAddOwner | AdoptedSSRemoveOwner | JSException | Zustand | BatchMetadata | PartitionedMessage | NetworkRequest | WSChannel | InputChange | SelectionChange | MouseThrashing | UnbindNodes | ResourceTiming | TabChange | TabData | CanvasNode | TagTrigger
export default Message
diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts
index 869689959..b140328b9 100644
--- a/tracker/tracker/src/main/app/index.ts
+++ b/tracker/tracker/src/main/app/index.ts
@@ -1,6 +1,6 @@
import ConditionsManager from '../modules/conditionsManager.js'
import FeatureFlags from '../modules/featureFlags.js'
-import type Message from './messages.gen.js'
+import Message, { TagTrigger } from './messages.gen.js'
import {
Timestamp,
Metadata,
@@ -34,6 +34,7 @@ import type { Options as SessOptions } from './session.js'
import type { Options as NetworkOptions } from '../modules/network.js'
import CanvasRecorder from './canvas.js'
import UserTestManager from '../modules/userTesting/index.js'
+import TagWatcher from '../modules/tagWatcher.js'
import type {
Options as WebworkerOptions,
@@ -179,6 +180,7 @@ export default class App {
private uxtManager: UserTestManager
private conditionsManager: ConditionsManager | null = null
public featureFlags: FeatureFlags
+ private tagWatcher: TagWatcher
constructor(
projectKey: string,
@@ -230,6 +232,9 @@ export default class App {
this.session = new Session(this, this.options)
this.attributeSender = new AttributeSender(this, Boolean(this.options.disableStringDict))
this.featureFlags = new FeatureFlags(this)
+ this.tagWatcher = new TagWatcher(this.sessionStorage, this.debug.error, (tag) => {
+ this.send(TagTrigger(tag) as Message)
+ })
this.session.attachUpdateCallback(({ userID, metadata }) => {
if (userID != null) {
// TODO: nullable userID
@@ -687,6 +692,7 @@ export default class App {
this.startCallbacks.forEach((cb) => cb(onStartInfo))
await this.conditionsManager?.fetchConditions(projectID as string, token as string)
await this.featureFlags.reloadFlags(token as string)
+ await this.tagWatcher.fetchTags(this.options.ingestPoint, token as string)
this.conditionsManager?.processFlags(this.featureFlags.flags)
}
const cycle = () => {
@@ -896,7 +902,6 @@ export default class App {
) {
const reason =
'OpenReplay: trying to call `start()` on the instance that has been started already.'
- this.signalError(reason, [])
return Promise.resolve(UnsuccessfulStart(reason))
}
this.activityState = ActivityState.Starting
@@ -1049,8 +1054,10 @@ export default class App {
this.compressionThreshold = compressionThreshold
const onStartInfo = { sessionToken: token, userUUID, sessionID }
// TODO: start as early as possible (before receiving the token)
+ /** after start */
this.startCallbacks.forEach((cb) => cb(onStartInfo)) // MBTODO: callbacks after DOM "mounted" (observed)
void this.featureFlags.reloadFlags()
+ await this.tagWatcher.fetchTags(this.options.ingestPoint, token)
this.activityState = ActivityState.Active
if (canvasEnabled) {
@@ -1234,6 +1241,7 @@ export default class App {
this.ticker.stop()
this.stopCallbacks.forEach((cb) => cb())
this.debug.log('OpenReplay tracking stopped.')
+ this.tagWatcher.clear()
if (this.worker && stopWorker) {
this.worker.postMessage('stop')
}
diff --git a/tracker/tracker/src/main/app/messages.gen.ts b/tracker/tracker/src/main/app/messages.gen.ts
index 57b4722f9..8f4e332b1 100644
--- a/tracker/tracker/src/main/app/messages.gen.ts
+++ b/tracker/tracker/src/main/app/messages.gen.ts
@@ -942,3 +942,12 @@ export function CanvasNode(
]
}
+export function TagTrigger(
+ tagId: number,
+): Messages.TagTrigger {
+ return [
+ Messages.Type.TagTrigger,
+ tagId,
+ ]
+}
+
diff --git a/tracker/tracker/src/main/modules/tagWatcher.ts b/tracker/tracker/src/main/modules/tagWatcher.ts
new file mode 100644
index 000000000..74ce132ec
--- /dev/null
+++ b/tracker/tracker/src/main/modules/tagWatcher.ts
@@ -0,0 +1,83 @@
+export const WATCHED_TAGS_KEY = '__or__watched_tags__'
+
+class TagWatcher {
+ intervals: Record> = {}
+ tags: { id: number; selector: string }[] = []
+ observer: IntersectionObserver
+
+ constructor(
+ private readonly sessionStorage: Storage,
+ private readonly errLog: (args: any[]) => void,
+ private readonly onTag: (tag: number) => void,
+ ) {
+ const tags: { id: number; selector: string }[] = JSON.parse(
+ sessionStorage.getItem(WATCHED_TAGS_KEY) ?? '[]',
+ )
+ this.setTags(tags)
+ this.observer = new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ if (entry.target) {
+ // @ts-ignore
+ const tag = entry.target.__or_watcher_tagname as number
+ if (tag) {
+ this.onTagRendered(tag)
+ }
+ this.observer.unobserve(entry.target)
+ }
+ }
+ })
+ })
+ }
+
+ async fetchTags(ingest: string, token: string) {
+ return fetch(`${ingest}/v1/web/tags`, {
+ method: 'GET',
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ })
+ .then((r) => r.json())
+ .then(({ tags }: { tags: { id: number; selector: string }[] }) => {
+ if (tags && tags.length) {
+ this.setTags(tags)
+ this.sessionStorage.setItem(WATCHED_TAGS_KEY, JSON.stringify(tags) || '')
+ }
+ })
+ .catch((e) => this.errLog(e))
+ }
+
+ setTags(tags: { id: number; selector: string }[]) {
+ this.tags = tags
+ this.intervals = {}
+ tags.forEach((tag) => {
+ this.intervals[tag.id] = setInterval(() => {
+ const possibleEls = document.querySelectorAll(tag.selector)
+ if (possibleEls.length > 0) {
+ const el = possibleEls[0]
+ // @ts-ignore
+ el.__or_watcher_tagname = tag.id
+ this.observer.observe(el)
+ }
+ }, 500)
+ })
+ }
+
+ onTagRendered(tagId: number) {
+ if (this.intervals[tagId]) {
+ clearInterval(this.intervals[tagId])
+ }
+ this.onTag(tagId)
+ }
+
+ clear() {
+ this.tags.forEach((tag) => {
+ clearInterval(this.intervals[tag.id])
+ })
+ this.tags = []
+ this.intervals = {}
+ this.observer.disconnect()
+ }
+}
+
+export default TagWatcher
diff --git a/tracker/tracker/src/tests/tagWatcher.test.ts b/tracker/tracker/src/tests/tagWatcher.test.ts
new file mode 100644
index 000000000..243730d62
--- /dev/null
+++ b/tracker/tracker/src/tests/tagWatcher.test.ts
@@ -0,0 +1,103 @@
+import TagWatcher, { WATCHED_TAGS_KEY } from '../main/modules/TagWatcher'
+import { describe, expect, jest, afterEach, beforeEach, test } from '@jest/globals'
+
+describe('TagWatcher', () => {
+ let sessionStorageMock: Storage
+ let errLogMock: (args: any[]) => void
+ const onTag = jest.fn()
+ let mockObserve: Function
+ let mockUnobserve: Function
+ let mockDisconnect: Function
+
+ beforeEach(() => {
+ sessionStorageMock = {
+ // @ts-ignore
+ getItem: jest.fn(),
+ setItem: jest.fn(),
+ }
+ errLogMock = jest.fn()
+ mockObserve = jest.fn()
+ mockUnobserve = jest.fn()
+ mockDisconnect = jest.fn()
+
+ // @ts-ignore
+ global.IntersectionObserver = jest.fn((callback) => ({
+ observe: mockObserve,
+ unobserve: mockUnobserve,
+ disconnect: mockDisconnect,
+ callback,
+ }))
+ jest.useFakeTimers()
+ // @ts-ignore
+ global.document.querySelectorAll = jest.fn()
+ })
+
+ afterEach(() => {
+ jest.restoreAllMocks()
+ jest.useRealTimers()
+ })
+ function triggerIntersection(elements: any, isIntersecting: boolean, observer: any) {
+ const entries = elements.map((el: any) => ({
+ isIntersecting,
+ target: el,
+ }))
+ // @ts-ignore
+ observer.callback(entries)
+ }
+
+ test('constructor initializes with tags from sessionStorage', () => {
+ // @ts-ignore
+ sessionStorageMock.getItem.mockReturnValue('div,span')
+ const watcher = new TagWatcher(sessionStorageMock, errLogMock, onTag)
+ expect(watcher.tags).toEqual(['div', 'span'])
+ expect(watcher.intervals).toHaveProperty('div')
+ expect(watcher.intervals).toHaveProperty('span')
+ })
+
+ test('fetchTags sets tags and updates sessionStorage', async () => {
+ // @ts-ignore
+ global.fetch = jest.fn(() =>
+ Promise.resolve({
+ json: () => Promise.resolve(['div', 'span', 'p']),
+ }),
+ )
+ const watcher = new TagWatcher(sessionStorageMock, errLogMock, onTag)
+ await watcher.fetchTags('https://localhost.com', '123')
+ expect(watcher.tags).toEqual(['div', 'span', 'p'])
+ expect(sessionStorageMock.setItem).toHaveBeenCalledWith(WATCHED_TAGS_KEY, 'div,span,p')
+ })
+
+ test('setTags sets intervals for each tag', () => {
+ const watcher = new TagWatcher(sessionStorageMock, errLogMock, onTag)
+ watcher.setTags([
+ { id: 1, selector: 'div' },
+ { id: 2, selector: 'p' },
+ ])
+ expect(watcher.intervals).toHaveProperty('div')
+ expect(watcher.intervals).toHaveProperty('p')
+ expect(mockObserve).not.toHaveBeenCalled() // No elements to observe initially
+ })
+
+ test('onTagRendered sends messages', () => {
+ const watcher = new TagWatcher(sessionStorageMock, errLogMock, onTag)
+ watcher.setTags([{ id: 1, selector: 'div' }])
+ // @ts-ignore
+ document.querySelectorAll.mockReturnValue([{ __or_watcher_tagname: 'div' }]) // Mock a found element
+ jest.advanceTimersByTime(1000)
+ triggerIntersection([{ __or_watcher_tagname: 'div' }], true, watcher.observer)
+ expect(onTag).toHaveBeenCalled()
+ expect(watcher.observer.unobserve).toHaveBeenCalled()
+ })
+
+ test('clear method clears all intervals and resets tags', () => {
+ const watcher = new TagWatcher(sessionStorageMock, errLogMock, onTag)
+ watcher.setTags([
+ { id: 1, selector: 'div' },
+ { id: 2, selector: 'p' },
+ ])
+ watcher.clear()
+ expect(watcher.tags).toEqual([])
+ expect(watcher.intervals).toEqual({})
+ expect(watcher.observer.disconnect).toHaveBeenCalled()
+ })
+})
diff --git a/tracker/tracker/src/webworker/MessageEncoder.gen.ts b/tracker/tracker/src/webworker/MessageEncoder.gen.ts
index cf9f4855e..7f04cf0dc 100644
--- a/tracker/tracker/src/webworker/MessageEncoder.gen.ts
+++ b/tracker/tracker/src/webworker/MessageEncoder.gen.ts
@@ -294,6 +294,10 @@ export default class MessageEncoder extends PrimitiveEncoder {
return this.string(msg[1]) && this.uint(msg[2])
break
+ case Messages.Type.TagTrigger:
+ return this.int(msg[1])
+ break
+
}
}