Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions frontend/src/scenes/notebooks/Nodes/NotebookNodeQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,21 @@ import { insightDataLogic } from 'scenes/insights/insightDataLogic'
import { insightLogic } from 'scenes/insights/insightLogic'
import { useSummarizeInsight } from 'scenes/insights/summarizeInsight'
import { createPostHogWidgetNode } from 'scenes/notebooks/Nodes/NodeWrapper'
import { notebookLogic } from 'scenes/notebooks/Notebook/notebookLogic'
import { urls } from 'scenes/urls'

import { Query } from '~/queries/Query/Query'
import { DataTableNode, InsightQueryNode, InsightVizNode, NodeKind, QuerySchema } from '~/queries/schema/schema-general'
import { containsHogQLQuery, isHogQLQuery, isInsightVizNode, isNodeWithSource } from '~/queries/utils'
import {
containsHogQLQuery,
isActorsQuery,
isDataTableNode,
isEventsQuery,
isHogQLQuery,
isInsightVizNode,
isNodeWithSource,
isSavedInsightNode,
} from '~/queries/utils'
import { InsightLogicProps, InsightShortId } from '~/types'

import { NotebookNodeAttributeProperties, NotebookNodeProps, NotebookNodeType } from '../types'
Expand Down Expand Up @@ -77,17 +87,17 @@ const Component = ({
}, [query, insightName])

const modifiedQuery = useMemo(() => {
const modifiedQuery = { ...query, full: false }
let modifiedQuery = { ...query, full: false }

if (NodeKind.DataTableNode === modifiedQuery.kind || NodeKind.SavedInsightNode === modifiedQuery.kind) {
if (isDataTableNode(modifiedQuery) || isSavedInsightNode(modifiedQuery)) {
modifiedQuery.showOpenEditorButton = false
modifiedQuery.full = false
modifiedQuery.showHogQLEditor = false
modifiedQuery.embedded = true
modifiedQuery.showTimings = false
}

if (NodeKind.InsightVizNode === modifiedQuery.kind || NodeKind.SavedInsightNode === modifiedQuery.kind) {
if (isInsightVizNode(modifiedQuery) || isSavedInsightNode(modifiedQuery)) {
modifiedQuery.showFilters = false
modifiedQuery.showHeader = false
modifiedQuery.showTable = false
Expand Down Expand Up @@ -129,18 +139,21 @@ const Component = ({

type NotebookNodeQueryAttributes = {
query: QuerySchema
/* Whether canvasFiltersOverride is applied, as we should apply it only once */
isDefaultFilterApplied: boolean
}

export const Settings = ({
attributes,
updateAttributes,
}: NotebookNodeAttributeProperties<NotebookNodeQueryAttributes>): JSX.Element => {
const { query } = attributes
const { query, isDefaultFilterApplied } = attributes
const { canvasFiltersOverride } = useValues(notebookLogic)

const modifiedQuery = useMemo(() => {
const modifiedQuery = { ...query, full: false }

if (NodeKind.DataTableNode === modifiedQuery.kind || NodeKind.SavedInsightNode === modifiedQuery.kind) {
if (isDataTableNode(modifiedQuery) || isSavedInsightNode(modifiedQuery)) {
modifiedQuery.showOpenEditorButton = false
modifiedQuery.showHogQLEditor = true
modifiedQuery.showResultsTable = false
Expand All @@ -160,18 +173,33 @@ export const Settings = ({
modifiedQuery.showColumnConfigurator = true
}

if (NodeKind.InsightVizNode === modifiedQuery.kind || NodeKind.SavedInsightNode === modifiedQuery.kind) {
if (isInsightVizNode(modifiedQuery) || isSavedInsightNode(modifiedQuery)) {
modifiedQuery.showFilters = true
modifiedQuery.showHeader = true
modifiedQuery.showResults = false
modifiedQuery.embedded = true
}

if (
isInsightVizNode(modifiedQuery) &&
!isHogQLQuery(modifiedQuery.source) &&
!isActorsQuery(modifiedQuery.source) &&
!isDefaultFilterApplied
) {
modifiedQuery.source.properties = canvasFiltersOverride
updateAttributes({ ...attributes, isDefaultFilterApplied: true })
}

if (isDataTableNode(modifiedQuery) && isEventsQuery(modifiedQuery.source) && !isDefaultFilterApplied) {
modifiedQuery.source.fixedProperties = canvasFiltersOverride
updateAttributes({ ...attributes, isDefaultFilterApplied: true })
}
Comment on lines +183 to +196
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: updateAttributes is called inside useMemo, which violates React rules - useMemo should be pure and not have side effects. This can cause unexpected behavior and infinite render loops.

Move the filter application logic to a useEffect hook instead:

useEffect(() => {
    if (
        isInsightVizNode(modifiedQuery) &&
        !isHogQLQuery(modifiedQuery.source) &&
        !isActorsQuery(modifiedQuery.source) &&
        !isDefaultFilterApplied &&
        canvasFiltersOverride
    ) {
        updateAttributes({ ...attributes, isDefaultFilterApplied: true })
    }
    
    if (isDataTableNode(modifiedQuery) && isEventsQuery(modifiedQuery.source) && !isDefaultFilterApplied && canvasFiltersOverride) {
        updateAttributes({ ...attributes, isDefaultFilterApplied: true })
    }
}, [isDefaultFilterApplied, canvasFiltersOverride, query.kind, query.source?.kind])
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/scenes/notebooks/Nodes/NotebookNodeQuery.tsx
Line: 184:197

Comment:
**logic:** `updateAttributes` is called inside `useMemo`, which violates React rules - `useMemo` should be pure and not have side effects. This can cause unexpected behavior and infinite render loops.

Move the filter application logic to a `useEffect` hook instead:

```
useEffect(() => {
    if (
        isInsightVizNode(modifiedQuery) &&
        !isHogQLQuery(modifiedQuery.source) &&
        !isActorsQuery(modifiedQuery.source) &&
        !isDefaultFilterApplied &&
        canvasFiltersOverride
    ) {
        updateAttributes({ ...attributes, isDefaultFilterApplied: true })
    }
    
    if (isDataTableNode(modifiedQuery) && isEventsQuery(modifiedQuery.source) && !isDefaultFilterApplied && canvasFiltersOverride) {
        updateAttributes({ ...attributes, isDefaultFilterApplied: true })
    }
}, [isDefaultFilterApplied, canvasFiltersOverride, query.kind, query.source?.kind])
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this specific case is safe, as we're updating isDefaultFilterApplied, which is not in the dependency array.


return modifiedQuery
}, [query])
}, [query, canvasFiltersOverride])

const detachSavedInsight = (): void => {
if (attributes.query.kind === NodeKind.SavedInsightNode) {
if (isSavedInsightNode(attributes.query)) {
const insightProps: InsightLogicProps = { dashboardItemId: attributes.query.shortId }
const dataLogic = insightDataLogic.findMounted(insightProps)

Expand All @@ -181,7 +209,7 @@ export const Settings = ({
}
}

return attributes.query.kind === NodeKind.SavedInsightNode ? (
return isSavedInsightNode(attributes.query) ? (
<div className="p-3 deprecated-space-y-2">
<div className="text-lg font-semibold">Insight created outside of this notebook</div>
<div>
Expand Down Expand Up @@ -241,9 +269,12 @@ export const NotebookNodeQuery = createPostHogWidgetNode<NotebookNodeQueryAttrib
query: {
default: DEFAULT_QUERY,
},
isDefaultFilterApplied: {
default: false,
},
},
href: ({ query }) =>
query.kind === NodeKind.SavedInsightNode
isSavedInsightNode(query)
? urls.insightView(query.shortId)
: isInsightVizNode(query)
? urls.insightNew({ query })
Expand All @@ -257,6 +288,7 @@ export const NotebookNodeQuery = createPostHogWidgetNode<NotebookNodeQueryAttrib
kind: NodeKind.SavedInsightNode,
shortId: match[1] as InsightShortId,
},
isDefaultFilterApplied: false,
}
},
},
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/scenes/notebooks/Notebook/notebookLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
AccessControlLevel,
AccessControlResourceType,
ActivityScope,
AnyPropertyFilter,
CommentType,
InsightShortId,
SidePanelTab,
Expand Down Expand Up @@ -51,6 +52,7 @@ export type NotebookLogicProps = {
shortId: string
mode?: NotebookLogicMode
target?: NotebookTarget
canvasFiltersOverride?: AnyPropertyFilter[]
}

async function runWhenEditorIsReady(waitForEditor: () => boolean, fn: () => any): Promise<any> {
Expand Down Expand Up @@ -151,6 +153,7 @@ export const notebookLogic = kea<notebookLogicType>([
setAccessDeniedToNotebook: true,
}),
reducers(({ props }) => ({
canvasFiltersOverride: [props.canvasFiltersOverride ?? ([] as AnyPropertyFilter[])],
isShareModalOpen: [
false,
{
Expand Down
122 changes: 68 additions & 54 deletions frontend/src/scenes/persons/PersonFeedCanvas.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useValues } from 'kea'
import { BindLogic, useValues } from 'kea'

import { uuid } from 'lib/utils'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { Notebook } from 'scenes/notebooks/Notebook/Notebook'
import { notebookLogic } from 'scenes/notebooks/Notebook/notebookLogic'

import { PersonType } from '~/types'
import { AnyPropertyFilter, PersonType, PropertyFilterType, PropertyOperator } from '~/types'

type PersonFeedCanvasProps = {
person: PersonType
Expand All @@ -15,61 +16,74 @@ const PersonFeedCanvas = ({ person }: PersonFeedCanvasProps): JSX.Element => {

const id = person.id
const distinctId = person.distinct_ids[0]
const shortId = `canvas-${id}`
const mode = 'canvas'

const personFilter: AnyPropertyFilter[] = [
{
type: PropertyFilterType.EventMetadata,
key: 'person_id',
value: id,
operator: PropertyOperator.Exact,
},
]

return (
<Notebook
editable={false}
shortId={`canvas-${id}`}
mode="canvas"
initialContent={{
type: 'doc',
content: [
{ type: 'ph-usage-metrics', attrs: { personId: id, nodeId: uuid() } },
{
type: 'ph-person-feed',
attrs: {
height: null,
title: null,
nodeId: uuid(),
id,
distinctId,
__init: null,
children: [
{
type: 'ph-person',
attrs: { id, distinctId, nodeId: uuid(), title: 'Info' },
},
...(isCloudOrDev
? [
{
type: 'ph-map',
attrs: { id, distinctId, nodeId: uuid() },
},
]
: []),
{
type: 'ph-person-properties',
attrs: { id, distinctId, nodeId: uuid() },
},
{ type: 'ph-related-groups', attrs: { id, nodeId: uuid(), type: 'group' } },
],
<BindLogic logic={notebookLogic} props={{ shortId, mode, canvasFiltersOverride: personFilter }}>
<Notebook
editable={false}
shortId={shortId}
mode={mode}
initialContent={{
type: 'doc',
content: [
{ type: 'ph-usage-metrics', attrs: { personId: id, nodeId: uuid() } },
{
type: 'ph-person-feed',
attrs: {
height: null,
title: null,
nodeId: uuid(),
id,
distinctId,
__init: null,
children: [
{
type: 'ph-person',
attrs: { id, distinctId, nodeId: uuid(), title: 'Info' },
},
...(isCloudOrDev
? [
{
type: 'ph-map',
attrs: { id, distinctId, nodeId: uuid() },
},
]
: []),
{
type: 'ph-person-properties',
attrs: { id, distinctId, nodeId: uuid() },
},
{ type: 'ph-related-groups', attrs: { id, nodeId: uuid(), type: 'group' } },
],
},
},
{
type: 'ph-llm-trace',
attrs: { personId: id, nodeId: uuid() },
},
{
type: 'ph-zendesk-tickets',
attrs: { personId: id, nodeId: uuid() },
},
{
type: 'ph-issues',
attrs: { personId: id, nodeId: uuid() },
},
},
{
type: 'ph-llm-trace',
attrs: { personId: id, nodeId: uuid() },
},
{
type: 'ph-zendesk-tickets',
attrs: { personId: id, nodeId: uuid() },
},
{
type: 'ph-issues',
attrs: { personId: id, nodeId: uuid() },
},
],
}}
/>
],
}}
/>
</BindLogic>
)
}

Expand Down
Loading