|
@@ -7,24 +7,29 @@ import {BreadcrumbType} from 'sentry/types/breadcrumbs';
|
|
|
import {TabKey} from 'sentry/utils/replays/hooks/useActiveReplayTab';
|
|
|
import {
|
|
|
BreadcrumbFrame,
|
|
|
- ErrorFrame,
|
|
|
LargestContentfulPaintFrame,
|
|
|
+ MultiClickFrame,
|
|
|
MutationFrame,
|
|
|
- NavigationFrame,
|
|
|
+ NavFrame,
|
|
|
+ ReplayFrame,
|
|
|
SlowClickFrame,
|
|
|
SpanFrame,
|
|
|
} from 'sentry/utils/replays/types';
|
|
|
import type {Color} from 'sentry/utils/theme';
|
|
|
|
|
|
-export function getColor(frame: BreadcrumbFrame | SpanFrame | ErrorFrame): Color {
|
|
|
+export function getColor(frame: ReplayFrame): Color {
|
|
|
if ('category' in frame) {
|
|
|
switch (frame.category) {
|
|
|
+ case 'navigation':
|
|
|
+ return 'green300';
|
|
|
case 'issue':
|
|
|
return 'red300';
|
|
|
case 'ui.slowClickDetected':
|
|
|
return (frame as SlowClickFrame).data.endReason === 'timeout'
|
|
|
? 'red300'
|
|
|
: 'yellow300';
|
|
|
+ case 'ui.multiClick':
|
|
|
+ return 'red300';
|
|
|
case 'replay.mutations':
|
|
|
return 'yellow300';
|
|
|
case 'ui.click':
|
|
@@ -61,17 +66,19 @@ export function getColor(frame: BreadcrumbFrame | SpanFrame | ErrorFrame): Color
|
|
|
*
|
|
|
* @deprecated
|
|
|
*/
|
|
|
-export function getBreadcrumbType(
|
|
|
- frame: BreadcrumbFrame | SpanFrame | ErrorFrame
|
|
|
-): BreadcrumbType {
|
|
|
+export function getBreadcrumbType(frame: ReplayFrame): BreadcrumbType {
|
|
|
if ('category' in frame) {
|
|
|
switch (frame.category) {
|
|
|
+ case 'navigation':
|
|
|
+ return BreadcrumbType.NAVIGATION;
|
|
|
case 'issue':
|
|
|
return BreadcrumbType.ERROR;
|
|
|
case 'ui.slowClickDetected':
|
|
|
return (frame as SlowClickFrame).data.endReason === 'timeout'
|
|
|
? BreadcrumbType.ERROR
|
|
|
: BreadcrumbType.WARNING;
|
|
|
+ case 'ui.multiClick':
|
|
|
+ return BreadcrumbType.ERROR;
|
|
|
case 'replay.mutations':
|
|
|
return BreadcrumbType.WARNING;
|
|
|
case 'ui.click':
|
|
@@ -105,7 +112,7 @@ export function getBreadcrumbType(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-export function getTitle(frame: BreadcrumbFrame | SpanFrame | ErrorFrame): ReactNode {
|
|
|
+export function getTitle(frame: ReplayFrame): ReactNode {
|
|
|
if (
|
|
|
typeof frame.data === 'object' &&
|
|
|
frame.data !== null &&
|
|
@@ -118,10 +125,14 @@ export function getTitle(frame: BreadcrumbFrame | SpanFrame | ErrorFrame): React
|
|
|
if ('category' in frame) {
|
|
|
const [type, action] = frame.category.split('.');
|
|
|
switch (frame.category) {
|
|
|
+ case 'navigation':
|
|
|
+ return 'Navigation';
|
|
|
case 'ui.slowClickDetected':
|
|
|
return (frame as SlowClickFrame).data.endReason === 'timeout'
|
|
|
? 'Dead Click'
|
|
|
: 'Slow Click';
|
|
|
+ case 'ui.multiClick':
|
|
|
+ return 'Rage Click';
|
|
|
case 'replay.mutations':
|
|
|
return 'Replay';
|
|
|
case 'ui.click':
|
|
@@ -138,7 +149,19 @@ export function getTitle(frame: BreadcrumbFrame | SpanFrame | ErrorFrame): React
|
|
|
if ('message' in frame) {
|
|
|
return frame.message; // TODO(replay): Included for backwards compat
|
|
|
}
|
|
|
- return frame.description;
|
|
|
+
|
|
|
+ switch (frame.op) {
|
|
|
+ case 'navigation.navigate':
|
|
|
+ return 'Page Load';
|
|
|
+ case 'navigation.reload':
|
|
|
+ return 'Reload';
|
|
|
+ case 'navigation.back_forward':
|
|
|
+ return 'Navigate Back';
|
|
|
+ case 'navigation.push':
|
|
|
+ return 'Navigation';
|
|
|
+ default:
|
|
|
+ return frame.description;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function stringifyNodeAttributes(node: SlowClickFrame['data']['node']) {
|
|
@@ -151,11 +174,12 @@ function stringifyNodeAttributes(node: SlowClickFrame['data']['node']) {
|
|
|
}`;
|
|
|
}
|
|
|
|
|
|
-export function getDescription(
|
|
|
- frame: BreadcrumbFrame | SpanFrame | ErrorFrame
|
|
|
-): ReactNode {
|
|
|
+export function getDescription(frame: ReplayFrame): ReactNode {
|
|
|
if ('category' in frame) {
|
|
|
switch (frame.category) {
|
|
|
+ case 'navigation':
|
|
|
+ const navFrame = frame as NavFrame;
|
|
|
+ return navFrame.data.to;
|
|
|
case 'issue':
|
|
|
case 'ui.slowClickDetected': {
|
|
|
const slowClickFrame = frame as SlowClickFrame;
|
|
@@ -173,6 +197,12 @@ export function getDescription(
|
|
|
duration: slowClickFrame.data.timeAfterClickMs,
|
|
|
});
|
|
|
}
|
|
|
+ case 'ui.multiClick':
|
|
|
+ const multiClickFrame = frame as MultiClickFrame;
|
|
|
+ return tct('Rage clicked [clickCount] times on [selector]', {
|
|
|
+ clickCount: multiClickFrame.data.clickCount,
|
|
|
+ selector: stringifyNodeAttributes(multiClickFrame.data.node),
|
|
|
+ });
|
|
|
case 'replay.mutations': {
|
|
|
const mutationFrame = frame as MutationFrame;
|
|
|
return mutationFrame.data.limit
|
|
@@ -186,6 +216,7 @@ export function getDescription(
|
|
|
);
|
|
|
}
|
|
|
case 'ui.click':
|
|
|
+ return frame.message ?? ''; // This should be the selector
|
|
|
case 'ui.input':
|
|
|
case 'ui.keyDown':
|
|
|
case 'ui.blur':
|
|
@@ -202,8 +233,7 @@ export function getDescription(
|
|
|
case 'navigation.reload':
|
|
|
case 'navigation.back_forward':
|
|
|
case 'navigation.push':
|
|
|
- // @ts-expect-error `.to` isn't part of the type
|
|
|
- return (frame as NavigationFrame).data.to ?? '';
|
|
|
+ return frame.description;
|
|
|
case 'largest-contentful-paint': {
|
|
|
const lcpFrame = frame as LargestContentfulPaintFrame;
|
|
|
if (typeof lcpFrame.data.value === 'number') {
|
|
@@ -228,13 +258,16 @@ export function getDescription(
|
|
|
export function getTabKeyForFrame(frame: BreadcrumbFrame | SpanFrame): TabKey {
|
|
|
if ('category' in frame) {
|
|
|
switch (frame.category) {
|
|
|
+ case 'navigation':
|
|
|
+ return TabKey.NETWORK;
|
|
|
case 'issue':
|
|
|
return TabKey.ERRORS;
|
|
|
- case 'ui.slowClickDetected':
|
|
|
case 'replay.mutations':
|
|
|
case 'ui.click':
|
|
|
case 'ui.input':
|
|
|
case 'ui.keyDown':
|
|
|
+ case 'ui.multiClick':
|
|
|
+ case 'ui.slowClickDetected':
|
|
|
return TabKey.DOM;
|
|
|
case 'console':
|
|
|
default: // Custom breadcrumbs will fall through here
|