Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
nick-delirium
aefa251329
tracker: react native "sendMessage" hook for gql integration 2024-12-27 13:55:39 +01:00
nick-delirium
a6ede1fdaf
ui: name for gql msg 2024-12-26 15:07:49 +01:00
nick-delirium
d91e98ff99
new mobile gql message 2024-12-26 15:07:48 +01:00
nick-delirium
53424b4001
graphql events support for mobile player, react-native connector 2024-12-26 15:07:48 +01:00
29 changed files with 484 additions and 158 deletions

View file

@ -6,9 +6,9 @@ func IsReplayerType(id int) bool {
} }
func IsMobileType(id int) bool { func IsMobileType(id int) bool {
return 90 == id || 91 == id || 92 == id || 93 == id || 94 == id || 95 == id || 96 == id || 97 == id || 98 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 106 == id || 107 == id || 110 == id || 111 == id return 90 == id || 91 == id || 92 == id || 93 == id || 94 == id || 95 == id || 96 == id || 97 == id || 98 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 106 == id || 107 == id || 110 == id || 111 == id || 89 == id
} }
func IsDOMType(id int) bool { func IsDOMType(id int) bool {
return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 37 == id || 38 == id || 49 == id || 50 == id || 51 == id || 43 == id || 52 == id || 54 == id || 55 == id || 57 == id || 58 == id || 59 == id || 60 == id || 61 == id || 67 == id || 68 == id || 69 == id || 70 == id || 71 == id || 72 == id || 73 == id || 74 == id || 75 == id || 76 == id || 77 == id || 113 == id || 114 == id || 117 == id || 118 == id || 119 == id || 122 == id || 93 == id || 96 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 106 == id || 111 == id return 0 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 37 == id || 38 == id || 49 == id || 50 == id || 51 == id || 43 == id || 52 == id || 54 == id || 55 == id || 57 == id || 58 == id || 59 == id || 60 == id || 61 == id || 67 == id || 68 == id || 69 == id || 70 == id || 71 == id || 72 == id || 73 == id || 74 == id || 75 == id || 76 == id || 77 == id || 113 == id || 114 == id || 117 == id || 118 == id || 119 == id || 122 == id || 93 == id || 96 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 106 == id || 111 == id || 89 == id
} }

View file

@ -58,6 +58,9 @@ func GetTimestamp(message Message) uint64 {
case *MobileIssueEvent: case *MobileIssueEvent:
return msg.Timestamp return msg.Timestamp
case *MobileGraphQL:
return msg.Timestamp
} }
return uint64(message.Meta().Timestamp) return uint64(message.Meta().Timestamp)
} }

View file

@ -117,6 +117,7 @@ const (
MsgMobileBatchMeta = 107 MsgMobileBatchMeta = 107
MsgMobilePerformanceAggregated = 110 MsgMobilePerformanceAggregated = 110
MsgMobileIssueEvent = 111 MsgMobileIssueEvent = 111
MsgMobileGraphQL = 89
) )
type Timestamp struct { type Timestamp struct {
@ -3210,3 +3211,36 @@ func (msg *MobileIssueEvent) Decode() Message {
func (msg *MobileIssueEvent) TypeID() int { func (msg *MobileIssueEvent) TypeID() int {
return 111 return 111
} }
type MobileGraphQL struct {
message
Timestamp uint64
Length uint64
OperationKind string
OperationName string
Variables string
Response string
Duration uint64
}
func (msg *MobileGraphQL) Encode() []byte {
buf := make([]byte, 71+len(msg.OperationKind)+len(msg.OperationName)+len(msg.Variables)+len(msg.Response))
buf[0] = 89
p := 1
p = WriteUint(msg.Timestamp, buf, p)
p = WriteUint(msg.Length, buf, p)
p = WriteString(msg.OperationKind, buf, p)
p = WriteString(msg.OperationName, buf, p)
p = WriteString(msg.Variables, buf, p)
p = WriteString(msg.Response, buf, p)
p = WriteUint(msg.Duration, buf, p)
return buf[:p]
}
func (msg *MobileGraphQL) Decode() Message {
return msg
}
func (msg *MobileGraphQL) TypeID() int {
return 89
}

View file

@ -2055,6 +2055,33 @@ func DecodeMobileIssueEvent(reader BytesReader) (Message, error) {
return msg, err return msg, err
} }
func DecodeMobileGraphQL(reader BytesReader) (Message, error) {
var err error = nil
msg := &MobileGraphQL{}
if msg.Timestamp, err = reader.ReadUint(); err != nil {
return nil, err
}
if msg.Length, err = reader.ReadUint(); err != nil {
return nil, err
}
if msg.OperationKind, err = reader.ReadString(); err != nil {
return nil, err
}
if msg.OperationName, err = reader.ReadString(); err != nil {
return nil, err
}
if msg.Variables, err = reader.ReadString(); err != nil {
return nil, err
}
if msg.Response, err = reader.ReadString(); err != nil {
return nil, err
}
if msg.Duration, err = reader.ReadUint(); err != nil {
return nil, err
}
return msg, err
}
func ReadMessage(t uint64, reader BytesReader) (Message, error) { func ReadMessage(t uint64, reader BytesReader) (Message, error) {
switch t { switch t {
case 0: case 0:
@ -2287,6 +2314,8 @@ func ReadMessage(t uint64, reader BytesReader) (Message, error) {
return DecodeMobilePerformanceAggregated(reader) return DecodeMobilePerformanceAggregated(reader)
case 111: case 111:
return DecodeMobileIssueEvent(reader) return DecodeMobileIssueEvent(reader)
case 89:
return DecodeMobileGraphQL(reader)
} }
return nil, fmt.Errorf("unknown message code: %v", t) return nil, fmt.Errorf("unknown message code: %v", t)
} }

View file

@ -1150,3 +1150,16 @@ class MobileIssueEvent(Message):
self.payload = payload self.payload = payload
class MobileGraphQL(Message):
__id__ = 89
def __init__(self, timestamp, length, operation_kind, operation_name, variables, response, duration):
self.timestamp = timestamp
self.length = length
self.operation_kind = operation_kind
self.operation_name = operation_name
self.variables = variables
self.response = response
self.duration = duration

View file

@ -1723,3 +1723,24 @@ cdef class MobileIssueEvent(PyMessage):
self.payload = payload self.payload = payload
cdef class MobileGraphQL(PyMessage):
cdef public int __id__
cdef public unsigned long timestamp
cdef public unsigned long length
cdef public str operation_kind
cdef public str operation_name
cdef public str variables
cdef public str response
cdef public unsigned long duration
def __init__(self, unsigned long timestamp, unsigned long length, str operation_kind, str operation_name, str variables, str response, unsigned long duration):
self.__id__ = 89
self.timestamp = timestamp
self.length = length
self.operation_kind = operation_kind
self.operation_name = operation_name
self.variables = variables
self.response = response
self.duration = duration

View file

@ -1007,3 +1007,14 @@ class MessageCodec(Codec):
payload=self.read_string(reader) payload=self.read_string(reader)
) )
if message_id == 89:
return MobileGraphQL(
timestamp=self.read_uint(reader),
length=self.read_uint(reader),
operation_kind=self.read_string(reader),
operation_name=self.read_string(reader),
variables=self.read_string(reader),
response=self.read_string(reader),
duration=self.read_uint(reader)
)

View file

@ -1105,3 +1105,14 @@ cdef class MessageCodec:
payload=self.read_string(reader) payload=self.read_string(reader)
) )
if message_id == 89:
return MobileGraphQL(
timestamp=self.read_uint(reader),
length=self.read_uint(reader),
operation_kind=self.read_string(reader),
operation_name=self.read_string(reader),
variables=self.read_string(reader),
response=self.read_string(reader),
duration=self.read_uint(reader)
)

View file

@ -23,12 +23,12 @@ import {
NETWORK, NETWORK,
OVERVIEW, OVERVIEW,
PERFORMANCE, PERFORMANCE,
STACKEVENTS STACKEVENTS,
GRAPHQL,
} from 'App/mstore/uiPlayerStore'; } from 'App/mstore/uiPlayerStore';
import { useStore } from 'App/mstore'; import { useStore } from 'App/mstore';
import { session as sessionRoute, withSiteId } from 'App/routes'; import { session as sessionRoute, withSiteId } from 'App/routes';
import { SummaryButton } from 'Components/Session_/Player/Controls/Controls'; import { SummaryButton } from 'Components/Session_/Player/Controls/Controls';
import { MobEventsList, WebEventsList } from "../../../Session_/Player/Controls/EventsList";
import useShortcuts from '../ReplayPlayer/useShortcuts'; import useShortcuts from '../ReplayPlayer/useShortcuts';
export const SKIP_INTERVALS = { export const SKIP_INTERVALS = {
@ -261,6 +261,12 @@ const DevtoolsButtons = observer(({
active={bottomBlock === STACKEVENTS} active={bottomBlock === STACKEVENTS}
label="Events" label="Events"
/> />
<ControlButton
disabled={messagesLoading}
onClick={() => toggleBottomTools(GRAPHQL)}
active={bottomBlock === GRAPHQL}
label="GraphQL"
/>
<ControlButton <ControlButton
popover={ popover={
<div className={'flex gap-2 items-center'}> <div className={'flex gap-2 items-center'}>

View file

@ -7,10 +7,8 @@ import { observer } from 'mobx-react-lite';
import styles from 'Components/Session_/playerBlock.module.css'; import styles from 'Components/Session_/playerBlock.module.css';
interface IProps { interface IProps {
fullscreen: boolean; fullscreen?: boolean;
sessionId: string;
activeTab: string; activeTab: string;
jiraConfig: Record<string, any>
fullView?: boolean fullView?: boolean
setActiveTab: (tab: string) => void setActiveTab: (tab: string) => void
} }

View file

@ -10,6 +10,7 @@ import {
PERFORMANCE, PERFORMANCE,
EXCEPTIONS, EXCEPTIONS,
OVERVIEW, OVERVIEW,
GRAPHQL,
} from 'App/mstore/uiPlayerStore'; } from 'App/mstore/uiPlayerStore';
import { MobileNetworkPanel } from 'Shared/DevTools/NetworkPanel'; import { MobileNetworkPanel } from 'Shared/DevTools/NetworkPanel';
import { MobilePerformance } from 'Components/Session_/Performance'; import { MobilePerformance } from 'Components/Session_/Performance';
@ -18,6 +19,7 @@ import MobileControls from './MobileControls';
import Overlay from './MobileOverlay' import Overlay from './MobileOverlay'
import stl from 'Components/Session_/Player/player.module.css'; import stl from 'Components/Session_/Player/player.module.css';
import { MobileOverviewPanel } from 'Components/Session_/OverviewPanel'; import { MobileOverviewPanel } from 'Components/Session_/OverviewPanel';
import GraphQL from 'Components/Session_/GraphQL';
import MobileConsolePanel from 'Shared/DevTools/ConsolePanel/MobileConsolePanel'; import MobileConsolePanel from 'Shared/DevTools/ConsolePanel/MobileConsolePanel';
import { MobilePlayerContext } from 'App/components/Session/playerContext'; import { MobilePlayerContext } from 'App/components/Session/playerContext';
import { MobileStackEventPanel } from 'Shared/DevTools/StackEventPanel'; import { MobileStackEventPanel } from 'Shared/DevTools/StackEventPanel';
@ -32,7 +34,6 @@ interface IProps {
isMultiview?: boolean; isMultiview?: boolean;
activeTab: string; activeTab: string;
setActiveTab: (tab: string) => void; setActiveTab: (tab: string) => void;
bottomBlock: any;
fullscreen?: boolean; fullscreen?: boolean;
} }
@ -146,6 +147,7 @@ function Player(props: IProps) {
{bottomBlock === NETWORK && <MobileNetworkPanel panelHeight={panelHeight} />} {bottomBlock === NETWORK && <MobileNetworkPanel panelHeight={panelHeight} />}
{bottomBlock === PERFORMANCE && <MobilePerformance />} {bottomBlock === PERFORMANCE && <MobilePerformance />}
{bottomBlock === EXCEPTIONS && <MobileExceptions />} {bottomBlock === EXCEPTIONS && <MobileExceptions />}
{bottomBlock === GRAPHQL && <GraphQL isMobile panelHeight={panelHeight} />}
</div> </div>
)} )}
{!fullView ? ( {!fullView ? (

View file

@ -2,13 +2,14 @@ import { Duration } from 'luxon';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { PlayerContext } from 'App/components/Session/playerContext'; import { PlayerContext, MobilePlayerContext } from 'App/components/Session/playerContext';
import { getRE } from 'App/utils'; import { getRE } from 'App/utils';
import TimeTable from 'Components/shared/DevTools/TimeTable'; import TimeTable from 'Components/shared/DevTools/TimeTable';
import { CloseButton, Input, NoContent, SlideModal } from 'UI'; import { CloseButton, Input, NoContent, SlideModal } from 'UI';
import BottomBlock from '../BottomBlock'; import BottomBlock from '../BottomBlock';
import GQLDetails from './GQLDetails'; import GQLDetails from './GQLDetails';
import { IWebPlayerStore, IIOSPlayerStore } from 'App/player/create';
export function renderStart(r) { export function renderStart(r) {
return ( return (
@ -28,15 +29,125 @@ export function renderStart(r) {
); );
} }
function renderDefaultStatus() { interface Props {
return '2xx-3xx'; onFilterChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
filteredList: Array<any>;
current: any;
renderName: (item: any) => React.ReactNode;
panelHeight: number;
closeModal: () => void;
currentIndex: number;
setCurrent: (item: any, index: number) => void;
lastActiveItem?: any;
onJump?: ({ time }: { time: number }) => void;
} }
function GraphQL({ panelHeight }: { panelHeight: number }) { const GraphQLComponent = ({
const { player, store } = React.useContext(PlayerContext); onFilterChange,
const { time, livePlay, tabStates, currentTab } = store.get(); filteredList,
const { graphqlList: list = [], graphqlListNow: listNow = [] } = current,
tabStates[currentTab]; renderName,
panelHeight,
closeModal,
currentIndex,
setCurrent,
lastActiveItem,
onJump,
}: Props) => {
return (
<React.Fragment>
<SlideModal
size="middle"
right
title={
<div className="flex justify-between">
<h1>GraphQL</h1>
<div className="flex items-center">
<CloseButton onClick={closeModal} size="18" className="ml-2" />
</div>
</div>
}
isDisplayed={current != null}
content={
current && (
<GQLDetails
gql={current}
first={currentIndex === 0}
last={currentIndex === filteredList.length - 1}
/>
)
}
onClose={closeModal}
/>
<BottomBlock>
<BottomBlock.Header>
<span className="font-semibold color-gray-medium mr-4">GraphQL</span>
<div className="flex items-center">
<Input
// className="input-small"
placeholder="Filter by name or type"
icon="search"
name="filter"
onChange={onFilterChange}
/>
</div>
</BottomBlock.Header>
<BottomBlock.Content>
<NoContent
size="small"
title="No recordings found"
show={filteredList.length === 0}
>
<TimeTable
rows={filteredList}
onRowClick={setCurrent}
tableHeight={panelHeight - 102}
hoverable
activeIndex={lastActiveItem}
onJump={onJump}
>
{[
{
label: 'Start',
width: 90,
render: renderStart,
},
{
label: 'Type',
dataKey: 'operationKind',
width: 80,
},
{
label: 'Name',
width: 300,
render: renderName,
},
]}
</TimeTable>
</NoContent>
</BottomBlock.Content>
</BottomBlock>
</React.Fragment>
);
};
function GraphQL({ panelHeight, isMobile }: { panelHeight: number, isMobile?: boolean }) {
const context = isMobile ? MobilePlayerContext : PlayerContext;
// @ts-ignore
const { player, store } = React.useContext(context);
const { time, livePlay } = store.get();
let list: any[] = [];
let listNow: any[] = [];
if (isMobile) {
const { graphqlList = [], graphqlListNow = [] } = (store as unknown as IIOSPlayerStore).get();
list = graphqlList;
listNow = graphqlListNow;
} else {
const { tabStates, currentTab } = (store as unknown as IWebPlayerStore).get();
const { graphqlList = [], graphqlListNow = [] } = tabStates[currentTab];
list = graphqlList;
listNow = graphqlListNow;
}
const defaultState = { const defaultState = {
filter: '', filter: '',
@ -121,79 +232,18 @@ function GraphQL({ panelHeight }: { panelHeight: number }) {
const { current, currentIndex, filteredList, lastActiveItem } = state; const { current, currentIndex, filteredList, lastActiveItem } = state;
return ( return (
<React.Fragment> <GraphQLComponent
<SlideModal onFilterChange={onFilterChange}
size="middle" filteredList={filteredList}
right current={current}
title={ renderName={renderName}
<div className="flex justify-between"> panelHeight={panelHeight}
<h1>GraphQL</h1> closeModal={closeModal}
<div className="flex items-center"> currentIndex={currentIndex}
<CloseButton onClick={closeModal} size="18" className="ml-2" /> setCurrent={setCurrent}
</div> lastActiveItem={lastActiveItem}
</div> onJump={onJump}
} />
isDisplayed={current != null}
content={
current && (
<GQLDetails
gql={current}
first={currentIndex === 0}
last={currentIndex === filteredList.length - 1}
/>
)
}
onClose={closeModal}
/>
<BottomBlock>
<BottomBlock.Header>
<span className="font-semibold color-gray-medium mr-4">GraphQL</span>
<div className="flex items-center">
<Input
// className="input-small"
placeholder="Filter by name or type"
icon="search"
name="filter"
onChange={onFilterChange}
/>
</div>
</BottomBlock.Header>
<BottomBlock.Content>
<NoContent
size="small"
title="No recordings found"
show={filteredList.length === 0}
>
<TimeTable
rows={filteredList}
onRowClick={setCurrent}
tableHeight={panelHeight - 102}
hoverable
activeIndex={lastActiveItem}
onJump={onJump}
>
{[
{
label: 'Start',
width: 90,
render: renderStart,
},
{
label: 'Type',
dataKey: 'operationKind',
width: 80,
},
{
label: 'Name',
width: 300,
render: renderName,
},
]}
</TimeTable>
</NoContent>
</BottomBlock.Content>
</BottomBlock>
</React.Fragment>
); );
} }

View file

@ -8,7 +8,8 @@ const SIMPLE_LIST_NAMES = [
"exceptions", "exceptions",
"profiles", "profiles",
"frustrations", "frustrations",
"performance" "performance",
"graphql",
] as const ] as const
const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack", "websocket" ] as const const MARKED_LIST_NAMES = [ "log", "resource", "fetch", "stack", "websocket" ] as const

View file

@ -286,6 +286,9 @@ export default class IOSMessageManager implements IMessageManager {
// @ts-ignore // @ts-ignore
this.lists.lists.log.append(Log(log)); this.lists.lists.log.append(Log(log));
break; break;
case MType.MobileGraphQl:
this.lists.lists.graphql.insert(msg);
break;
default: default:
console.log(msg); console.log(msg);
// stuff // stuff

View file

@ -997,6 +997,26 @@ export default class RawMessageReader extends PrimitiveReader {
}; };
} }
case 89: {
const timestamp = this.readUint(); if (timestamp === null) { return resetPointer() }
const length = this.readUint(); if (length === null) { return resetPointer() }
const operationKind = this.readString(); if (operationKind === null) { return resetPointer() }
const operationName = this.readString(); if (operationName === null) { return resetPointer() }
const variables = this.readString(); if (variables === null) { return resetPointer() }
const response = this.readString(); if (response === null) { return resetPointer() }
const duration = this.readUint(); if (duration === null) { return resetPointer() }
return {
tp: MType.MobileGraphQl,
timestamp,
length,
operationKind,
operationName,
variables,
response,
duration,
};
}
default: default:
throw new Error(`Unrecognizable message type: ${ tp }; Pointer at the position ${this.p} of ${this.buf.length}`) throw new Error(`Unrecognizable message type: ${ tp }; Pointer at the position ${this.p} of ${this.buf.length}`)
} }

View file

@ -3,7 +3,7 @@
import { MType } from './raw.gen' import { MType } from './raw.gen'
const IOS_TYPES = [90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,110,111] const IOS_TYPES = [90,91,92,93,94,95,96,97,98,100,101,102,103,104,105,106,107,110,111,89]
const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,37,38,49,50,51,43,52,54,55,57,58,59,60,61,67,68,69,70,71,72,73,74,75,76,77,113,114,117,118,119,122] const DOM_TYPES = [0,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,37,38,49,50,51,43,52,54,55,57,58,59,60,61,67,68,69,70,71,72,73,74,75,76,77,113,114,117,118,119,122]
export function isDOMType(t: MType) { export function isDOMType(t: MType) {
return DOM_TYPES.includes(t) return DOM_TYPES.includes(t)

View file

@ -80,6 +80,7 @@ import type {
RawMobileNetworkCall, RawMobileNetworkCall,
RawMobileSwipeEvent, RawMobileSwipeEvent,
RawMobileIssueEvent, RawMobileIssueEvent,
RawMobileGraphQl,
} from './raw.gen' } from './raw.gen'
export type Message = RawMessage & Timed export type Message = RawMessage & Timed
@ -237,3 +238,5 @@ export type MobileSwipeEvent = RawMobileSwipeEvent & Timed
export type MobileIssueEvent = RawMobileIssueEvent & Timed export type MobileIssueEvent = RawMobileIssueEvent & Timed
export type MobileGraphQl = RawMobileGraphQl & Timed

View file

@ -78,6 +78,7 @@ export const enum MType {
MobileNetworkCall = 105, MobileNetworkCall = 105,
MobileSwipeEvent = 106, MobileSwipeEvent = 106,
MobileIssueEvent = 111, MobileIssueEvent = 111,
MobileGraphQl = 89,
} }
@ -646,5 +647,16 @@ export interface RawMobileIssueEvent {
payload: string, payload: string,
} }
export interface RawMobileGraphQl {
tp: MType.MobileGraphQl,
timestamp: number,
length: number,
operationKind: string,
operationName: string,
variables: string,
response: string,
duration: number,
}
export type RawMessage = RawTimestamp | RawSetPageLocationDeprecated | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawReduxDeprecated | RawVuex | RawMobX | RawNgRx | RawGraphQlDeprecated | RawPerformanceTrack | RawStringDictDeprecated | RawSetNodeAttributeDictDeprecated | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawMouseClickDeprecated | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawRedux | RawSetPageLocation | RawGraphQl | RawMobileEvent | RawMobileScreenChanges | RawMobileClickEvent | RawMobileInputEvent | RawMobilePerformanceEvent | RawMobileLog | RawMobileInternalError | RawMobileNetworkCall | RawMobileSwipeEvent | RawMobileIssueEvent;
export type RawMessage = RawTimestamp | RawSetPageLocationDeprecated | RawSetViewportSize | RawSetViewportScroll | RawCreateDocument | RawCreateElementNode | RawCreateTextNode | RawMoveNode | RawRemoveNode | RawSetNodeAttribute | RawRemoveNodeAttribute | RawSetNodeData | RawSetCssData | RawSetNodeScroll | RawSetInputValue | RawSetInputChecked | RawMouseMove | RawNetworkRequestDeprecated | RawConsoleLog | RawCssInsertRule | RawCssDeleteRule | RawFetch | RawProfiler | RawOTable | RawReduxDeprecated | RawVuex | RawMobX | RawNgRx | RawGraphQlDeprecated | RawPerformanceTrack | RawStringDictDeprecated | RawSetNodeAttributeDictDeprecated | RawStringDict | RawSetNodeAttributeDict | RawResourceTimingDeprecated | RawConnectionInformation | RawSetPageVisibility | RawLoadFontFace | RawSetNodeFocus | RawLongTask | RawSetNodeAttributeURLBased | RawSetCssDataURLBased | RawCssInsertRuleURLBased | RawMouseClick | RawMouseClickDeprecated | RawCreateIFrameDocument | RawAdoptedSsReplaceURLBased | RawAdoptedSsReplace | RawAdoptedSsInsertRuleURLBased | RawAdoptedSsInsertRule | RawAdoptedSsDeleteRule | RawAdoptedSsAddOwner | RawAdoptedSsRemoveOwner | RawZustand | RawNetworkRequest | RawWsChannel | RawSelectionChange | RawMouseThrashing | RawResourceTiming | RawTabChange | RawTabData | RawCanvasNode | RawTagTrigger | RawRedux | RawSetPageLocation | RawGraphQl | RawMobileEvent | RawMobileScreenChanges | RawMobileClickEvent | RawMobileInputEvent | RawMobilePerformanceEvent | RawMobileLog | RawMobileInternalError | RawMobileNetworkCall | RawMobileSwipeEvent | RawMobileIssueEvent | RawMobileGraphQl;

View file

@ -79,4 +79,5 @@ export const TP_MAP = {
105: MType.MobileNetworkCall, 105: MType.MobileNetworkCall,
106: MType.MobileSwipeEvent, 106: MType.MobileSwipeEvent,
111: MType.MobileIssueEvent, 111: MType.MobileIssueEvent,
89: MType.MobileGraphQl,
} as const } as const

View file

@ -611,4 +611,4 @@ message 127, 'SessionSearch', :tracker => false, :replayer => false do
uint 'Partition' uint 'Partition'
end end
# FREE 2, 34, 35, 36, 65, 85, 86, 87, 88, 89 # FREE 2, 34, 35, 36, 65, 85, 86, 87, 88

View file

@ -168,3 +168,13 @@ message 111, 'MobileIssueEvent', :replayer => true do
string 'Context' string 'Context'
string 'Payload' string 'Payload'
end end
message 89, 'MobileGraphQL', :replayer => true do
uint 'Timestamp'
uint 'Length'
string 'OperationKind'
string 'OperationName'
string 'Variables'
string 'Response'
uint 'Duration'
end

View file

@ -9,3 +9,31 @@ npm install @openreplay/react-native
``` ```
Please see [the documentation](https://docs.openreplay.com/en/rn-sdk/) for more information about usage. Please see [the documentation](https://docs.openreplay.com/en/rn-sdk/) for more information about usage.
### React Native and GraphQL
You can use [@openreplay/tracker-graphql](https://www.npmjs.com/package/@openreplay/tracker-graphql) to handle graphql events in react-native applications as well, via
```js
import { createRelayMiddleware } from '@openreplay/tracker-graphql';
const appWrapper = {
active: () => true,
send: (gqlMsg) => {
const type = 'gql';
const msg = JSON.stringify({
operationKind: gqlMsg[1],
operationName: gqlMsg[2],
variables: gqlMsg[3],
response: gqlMsg[4],
duration: gqlMsg[5],
});
Openreplay.sendCustomMessage(type, msg);
},
};
// @ts-ignore - emulating web tracker here for middleware
const middleware = createRelayMiddleware(appWrapper);
// .. connect to relay network layer
```

View file

@ -9,14 +9,14 @@ PODS:
- hermes-engine (0.74.0): - hermes-engine (0.74.0):
- hermes-engine/Pre-built (= 0.74.0) - hermes-engine/Pre-built (= 0.74.0)
- hermes-engine/Pre-built (0.74.0) - hermes-engine/Pre-built (0.74.0)
- Openreplay (1.0.15): - Openreplay (1.0.17):
- DeviceKit - DeviceKit
- SWCompression - SWCompression
- openreplay-react-native (0.6.11): - openreplay-react-native (0.6.11):
- DoubleConversion - DoubleConversion
- glog - glog
- hermes-engine - hermes-engine
- Openreplay (= 1.0.15) - Openreplay (= 1.0.17)
- RCT-Folly (= 2024.01.01.00) - RCT-Folly (= 2024.01.01.00)
- RCTRequired - RCTRequired
- RCTTypeSafety - RCTTypeSafety
@ -1423,8 +1423,8 @@ SPEC CHECKSUMS:
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
hermes-engine: 6eae7edb2f563ee41d7c1f91f4f2e57c26d8a5c3 hermes-engine: 6eae7edb2f563ee41d7c1f91f4f2e57c26d8a5c3
Openreplay: ae72a7ca1a05d7da026b7ee8f4f84dcdfaa44021 Openreplay: 2bffa68a1606c95cd04618f0095864649d076d40
openreplay-react-native: 0cfa1842c5b2457f6e9d5fa564f746192317b20c openreplay-react-native: e77361570814a3c92483423b463011536b2346b6
RCT-Folly: 045d6ecaa59d826c5736dfba0b2f4083ff8d79df RCT-Folly: 045d6ecaa59d826c5736dfba0b2f4083ff8d79df
RCTDeprecation: 3ca8b6c36bfb302e1895b72cfe7db0de0c92cd47 RCTDeprecation: 3ca8b6c36bfb302e1895b72cfe7db0de0c92cd47
RCTRequired: 9fc183af555fd0c89a366c34c1ae70b7e03b1dc5 RCTRequired: 9fc183af555fd0c89a366c34c1ae70b7e03b1dc5

View file

@ -11,9 +11,9 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
1B3DD9657AF7850CB6FA1B53 /* libPods-ReactNativeExample-ReactNativeExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 68F0ED938F2BC97A9BFD4365 /* libPods-ReactNativeExample-ReactNativeExampleTests.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
8CFDEAD4DCE52688468D5C80 /* libPods-ReactNativeExample-ReactNativeExampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A889A4784F36428D3856494 /* libPods-ReactNativeExample-ReactNativeExampleTests.a */; }; DA89C15737C59610E15C36FD /* libPods-ReactNativeExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 900A29F732B0D23F50779293 /* libPods-ReactNativeExample.a */; };
C7F3F3B67AF0BA29F7DAC50F /* libPods-ReactNativeExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F4C922963FC6C79114841BE6 /* libPods-ReactNativeExample.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -30,6 +30,7 @@
00E356EE1AD99517003FC87E /* ReactNativeExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 00E356EE1AD99517003FC87E /* ReactNativeExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ReactNativeExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* ReactNativeExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeExampleTests.m; sourceTree = "<group>"; }; 00E356F21AD99517003FC87E /* ReactNativeExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactNativeExampleTests.m; sourceTree = "<group>"; };
01E603FB97835EE257543318 /* Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* ReactNativeExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactNativeExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* ReactNativeExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ReactNativeExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeExample/AppDelegate.h; sourceTree = "<group>"; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ReactNativeExample/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = ReactNativeExample/AppDelegate.mm; sourceTree = "<group>"; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = ReactNativeExample/AppDelegate.mm; sourceTree = "<group>"; };
@ -37,14 +38,13 @@
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeExample/Info.plist; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ReactNativeExample/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeExample/main.m; sourceTree = "<group>"; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ReactNativeExample/main.m; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeExample/PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = ReactNativeExample/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
212B4F1F0ABCF375B1354A0B /* Pods-ReactNativeExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample.debug.xcconfig"; sourceTree = "<group>"; }; 4ACA5716B98C6ABE43A0766D /* Pods-ReactNativeExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample.debug.xcconfig"; sourceTree = "<group>"; };
5A889A4784F36428D3856494 /* libPods-ReactNativeExample-ReactNativeExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeExample-ReactNativeExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 51D080F1BD4B263A1A494122 /* Pods-ReactNativeExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample.release.xcconfig"; sourceTree = "<group>"; };
6102A46EF47DB7D6762C9A2B /* Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig"; sourceTree = "<group>"; }; 68F0ED938F2BC97A9BFD4365 /* libPods-ReactNativeExample-ReactNativeExampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeExample-ReactNativeExampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
7237FA23C741052532F892F9 /* Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig"; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeExample/LaunchScreen.storyboard; sourceTree = "<group>"; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ReactNativeExample/LaunchScreen.storyboard; sourceTree = "<group>"; };
837561A5C741BC8B67930889 /* Pods-ReactNativeExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample.release.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample.release.xcconfig"; sourceTree = "<group>"; }; 900A29F732B0D23F50779293 /* libPods-ReactNativeExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
EA74B193F79CA13EE3B460C6 /* Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F4C922963FC6C79114841BE6 /* libPods-ReactNativeExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ReactNativeExample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -52,7 +52,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
8CFDEAD4DCE52688468D5C80 /* libPods-ReactNativeExample-ReactNativeExampleTests.a in Frameworks */, 1B3DD9657AF7850CB6FA1B53 /* libPods-ReactNativeExample-ReactNativeExampleTests.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -60,7 +60,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
C7F3F3B67AF0BA29F7DAC50F /* libPods-ReactNativeExample.a in Frameworks */, DA89C15737C59610E15C36FD /* libPods-ReactNativeExample.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -102,8 +102,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
F4C922963FC6C79114841BE6 /* libPods-ReactNativeExample.a */, 900A29F732B0D23F50779293 /* libPods-ReactNativeExample.a */,
5A889A4784F36428D3856494 /* libPods-ReactNativeExample-ReactNativeExampleTests.a */, 68F0ED938F2BC97A9BFD4365 /* libPods-ReactNativeExample-ReactNativeExampleTests.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -142,10 +142,10 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = { BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
212B4F1F0ABCF375B1354A0B /* Pods-ReactNativeExample.debug.xcconfig */, 4ACA5716B98C6ABE43A0766D /* Pods-ReactNativeExample.debug.xcconfig */,
837561A5C741BC8B67930889 /* Pods-ReactNativeExample.release.xcconfig */, 51D080F1BD4B263A1A494122 /* Pods-ReactNativeExample.release.xcconfig */,
EA74B193F79CA13EE3B460C6 /* Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig */, 7237FA23C741052532F892F9 /* Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig */,
6102A46EF47DB7D6762C9A2B /* Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig */, 01E603FB97835EE257543318 /* Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
@ -157,12 +157,12 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeExampleTests" */; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ReactNativeExampleTests" */;
buildPhases = ( buildPhases = (
D039EC238FC4B9FDBDAFB800 /* [CP] Check Pods Manifest.lock */, 79E2EA345A351A0B80042F86 /* [CP] Check Pods Manifest.lock */,
00E356EA1AD99517003FC87E /* Sources */, 00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */, 00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */, 00E356EC1AD99517003FC87E /* Resources */,
DD6933155112660ABBFB11CE /* [CP] Embed Pods Frameworks */, 1BBDA02DD72D4004B217A05F /* [CP] Embed Pods Frameworks */,
1038B73ED8C88C8CDF41F1FC /* [CP] Copy Pods Resources */, EDC55199BC23BF9C078EF29C /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@ -178,13 +178,13 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeExample" */; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ReactNativeExample" */;
buildPhases = ( buildPhases = (
BB963842CAFBE7B5A629B013 /* [CP] Check Pods Manifest.lock */, 1DEEBC761A9F0AD9D41C9B8F /* [CP] Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */, 13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
35D9F844561F84987A5EA09A /* [CP] Embed Pods Frameworks */, 102F54311A0D94A16229D75D /* [CP] Embed Pods Frameworks */,
3862ED11E8450BD142ECCA26 /* [CP] Copy Pods Resources */, 9C953D85F31015264F6EA5A7 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@ -267,24 +267,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
}; };
1038B73ED8C88C8CDF41F1FC /* [CP] Copy Pods Resources */ = { 102F54311A0D94A16229D75D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
35D9F844561F84987A5EA09A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -301,24 +284,24 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
3862ED11E8450BD142ECCA26 /* [CP] Copy Pods Resources */ = { 1BBDA02DD72D4004B217A05F /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-resources-${CONFIGURATION}-input-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Embed Pods Frameworks";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-resources-${CONFIGURATION}-output-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-resources.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
BB963842CAFBE7B5A629B013 /* [CP] Check Pods Manifest.lock */ = { 1DEEBC761A9F0AD9D41C9B8F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -340,7 +323,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D039EC238FC4B9FDBDAFB800 /* [CP] Check Pods Manifest.lock */ = { 79E2EA345A351A0B80042F86 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@ -362,21 +345,38 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DD6933155112660ABBFB11CE /* [CP] Embed Pods Frameworks */ = { 9C953D85F31015264F6EA5A7 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-resources-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Copy Pods Resources";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-resources-${CONFIGURATION}-output-files.xcfilelist",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample/Pods-ReactNativeExample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
EDC55199BC23BF9C078EF29C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ReactNativeExample-ReactNativeExampleTests/Pods-ReactNativeExample-ReactNativeExampleTests-resources.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -412,7 +412,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = { 00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = EA74B193F79CA13EE3B460C6 /* Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig */; baseConfigurationReference = 7237FA23C741052532F892F9 /* Pods-ReactNativeExample-ReactNativeExampleTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@ -439,7 +439,7 @@
}; };
00E356F71AD99517003FC87E /* Release */ = { 00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 6102A46EF47DB7D6762C9A2B /* Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig */; baseConfigurationReference = 01E603FB97835EE257543318 /* Pods-ReactNativeExample-ReactNativeExampleTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
@ -463,7 +463,7 @@
}; };
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 212B4F1F0ABCF375B1354A0B /* Pods-ReactNativeExample.debug.xcconfig */; baseConfigurationReference = 4ACA5716B98C6ABE43A0766D /* Pods-ReactNativeExample.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
@ -490,7 +490,7 @@
}; };
13B07F951A680F5B00A75B9A /* Release */ = { 13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 837561A5C741BC8B67930889 /* Pods-ReactNativeExample.release.xcconfig */; baseConfigurationReference = 51D080F1BD4B263A1A494122 /* Pods-ReactNativeExample.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;

View file

@ -46,6 +46,33 @@ export default function App() {
}); });
}; };
const gqlTest = () => {
const appWrapper = {
active: () => true,
send: (gqlMsg) => {
const type = 'gql';
const msg = JSON.stringify({
operationKind: gqlMsg[1],
operationName: gqlMsg[2],
variables: gqlMsg[3],
response: gqlMsg[4],
duration: gqlMsg[5],
});
Openreplay.sendCustomMessage(type, msg);
},
};
// just a mock
const mockMsg = [
'message id',
'query',
'operationName',
{ data: 'some imporatnt data' },
{ response: 'some response data' },
100,
];
appWrapper.send(mockMsg);
};
return ( return (
<Openreplay.ORTouchTrackingView style={styles.container}> <Openreplay.ORTouchTrackingView style={styles.container}>
<View style={styles.container}> <View style={styles.container}>
@ -69,6 +96,10 @@ export default function App() {
<Text>Request</Text> <Text>Request</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={gqlTest}>
<Text>GraphQL</Text>
</TouchableOpacity>
<Openreplay.ORTrackedInput <Openreplay.ORTrackedInput
style={styles.input} style={styles.input}
onChangeText={onChangeNumber} onChangeText={onChangeNumber}

View file

@ -22,6 +22,8 @@ RCT_EXTERN_METHOD(setUserID:(NSString *)userID)
RCT_EXTERN_METHOD(userAnonymousID:(NSString *)userID) RCT_EXTERN_METHOD(userAnonymousID:(NSString *)userID)
RCT_EXTERN_METHOD(sendMessage:(NSString *)msgType msg:(NSString *)msg)
RCT_EXTERN_METHOD(networkRequest:(NSString *)url RCT_EXTERN_METHOD(networkRequest:(NSString *)url
method:(NSString *)method method:(NSString *)method
requestJSON:(NSString *)requestJSON requestJSON:(NSString *)requestJSON

View file

@ -80,4 +80,9 @@ public class ORTrackerConnector: NSObject {
let sessionID = Openreplay.shared.getSessionID() let sessionID = Openreplay.shared.getSessionID()
resolve(sessionID) resolve(sessionID)
} }
@objc(sendMessage:msg:)
open func sendMessage(_ msgType: String, msg: String) {
Openreplay.shared.sendMessage(msgType, msg)
}
} }

View file

@ -19,11 +19,11 @@ Pod::Spec.new do |s|
# Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
# See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
if respond_to?(:install_modules_dependencies, true) if respond_to?(:install_modules_dependencies, true)
s.dependency "Openreplay", '1.0.15' s.dependency "Openreplay", '1.0.17'
install_modules_dependencies(s) install_modules_dependencies(s)
else else
s.dependency "React-Core" s.dependency "React-Core"
s.dependency "Openreplay", '1.0.15' s.dependency "Openreplay", '1.0.17'
# Don't install the dependencies when we run `pod install` in the old architecture. # Don't install the dependencies when we run `pod install` in the old architecture.
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
@ -38,7 +38,7 @@ Pod::Spec.new do |s|
s.dependency "RCTRequired" s.dependency "RCTRequired"
s.dependency "RCTTypeSafety" s.dependency "RCTTypeSafety"
s.dependency "ReactCommon/turbomodule/core" s.dependency "ReactCommon/turbomodule/core"
s.dependency "Openreplay", '1.0.15' s.dependency "Openreplay", '1.0.17'
end end
end end
end end

View file

@ -17,6 +17,8 @@ const LINKING_ERROR =
'- You rebuilt the app after installing the package\n' + '- You rebuilt the app after installing the package\n' +
'- You are not using Expo Go\n'; '- You are not using Expo Go\n';
console.log(Object.keys(ORTrackerConnector));
interface Options { interface Options {
crashes?: boolean; crashes?: boolean;
analytics?: boolean; analytics?: boolean;
@ -32,6 +34,11 @@ interface IORTrackerConnector {
optionsDict: Options, optionsDict: Options,
projectUrl?: string projectUrl?: string
) => void; ) => void;
/**
* @param type - type of message (only gql at the moment)
* @param msg - JSON string containing message to be sent
* */
sendMessage: (type: string, msg: string) => void;
stop: () => void; stop: () => void;
getSessionID: () => Promise<string>; getSessionID: () => Promise<string>;
setMetadata: (key: string, value: string) => void; setMetadata: (key: string, value: string) => void;
@ -77,6 +84,30 @@ export function setUserID(userID: string) {
ORTrackerConnector.setUserID(userID); ORTrackerConnector.setUserID(userID);
} }
/**
* Can be used with OR gql (Relay/Apollo) plugin:
* ```
* const appWrapper = {
* active: () => true,
* send: (gqlMsg) => {
* const type = 'gql';
* const msg = JSON.stringify({
* operationKind: gqlMsg[1],
* operationName: gqlMsg[2],
* variables: gqlMsg[3],
* response: gqlMsg[4],
* duration: gqlMsg[5],
* })
* sendMessage(type, msg);
* }
* }
* ```
* */
export function sendMessage(type: string, msg: string) {
ORTrackerConnector.sendMessage(type, msg);
}
let patched = false; let patched = false;
const patchNetwork = ( const patchNetwork = (
ctx = global, ctx = global,
@ -91,6 +122,7 @@ const patchNetwork = (
export default { export default {
tracker: ORTrackerConnector as IORTrackerConnector, tracker: ORTrackerConnector as IORTrackerConnector,
sendCustomMessage: sendMessage,
patchNetwork: patchNetwork, patchNetwork: patchNetwork,
ORTouchTrackingView: RnTrackerTouchTrackingView, ORTouchTrackingView: RnTrackerTouchTrackingView,
ORTrackedInput: ORTrackedInput, ORTrackedInput: ORTrackedInput,