Skip to content

Commit

Permalink
move intervalFilterLogic side effects to handleQuerySourceUpdateSideE…
Browse files Browse the repository at this point in the history
…ffects
  • Loading branch information
thmsobrmlr committed Oct 18, 2023
1 parent a15da15 commit eed58c7
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 92 deletions.
79 changes: 3 additions & 76 deletions frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { kea } from 'kea'
import { objectsEqual, dateMapping } from 'lib/utils'
import type { intervalFilterLogicType } from './intervalFilterLogicType'
import { IntervalKeyType, Intervals, intervals } from 'lib/components/IntervalFilter/intervals'
import { BaseMathType, InsightLogicProps, IntervalType } from '~/types'
import { BaseMathType, InsightLogicProps } from '~/types'
import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils'
import { dayjs } from 'lib/dayjs'
import { InsightQueryNode, TrendsQuery } from '~/queries/schema'
import { lemonToast } from 'lib/lemon-ui/lemonToast'
import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic'
import { InsightQueryNode } from '~/queries/schema'
import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic'

export const intervalFilterLogic = kea<intervalFilterLogicType>({
Expand Down Expand Up @@ -49,80 +45,11 @@ export const intervalFilterLogic = kea<intervalFilterLogicType>({
},
],
}),
listeners: ({ values, actions, selectors }) => ({
listeners: ({ values, actions }) => ({
setInterval: ({ interval }) => {
if (values.interval !== interval) {
actions.updateQuerySource({ interval } as Partial<InsightQueryNode>)
}
},
updateQuerySource: ({ querySource }, _, __, previousState) => {
const { date_from, date_to } = querySource.dateRange || {}
const previousDateRange = selectors.querySource(previousState)?.dateRange || {}

// If the user just flipped an event action to use WAUs/MAUs math and their
// current interval is unsupported by the math type, switch their interval
// to an appropriate allowed interval and inform them of the change via a toast
if (
values.activeUsersMath &&
(values.querySource as TrendsQuery)?.interval &&
values.enabledIntervals[(values.querySource as TrendsQuery).interval as IntervalType].disabledReason
) {
if (values.interval === 'hour') {
lemonToast.info(
`Switched to grouping by day, because "${
BASE_MATH_DEFINITIONS[values.activeUsersMath].name
}" does not support grouping by ${values.interval}.`
)
actions.updateQuerySource({ interval: 'day' } as Partial<InsightQueryNode>)
} else {
lemonToast.info(
`Switched to grouping by week, because "${
BASE_MATH_DEFINITIONS[values.activeUsersMath].name
}" does not support grouping by ${values.interval}.`
)
actions.updateQuerySource({ interval: 'week' } as Partial<InsightQueryNode>)
}
return
}

if (
!date_from ||
(objectsEqual(date_from, previousDateRange.date_from) &&
objectsEqual(date_to, previousDateRange.date_to))
) {
return
}

// automatically set an interval for fixed date ranges
if (
date_from &&
date_to &&
dayjs(querySource.dateRange?.date_from).isValid() &&
dayjs(querySource.dateRange?.date_to).isValid()
) {
if (dayjs(date_to).diff(dayjs(date_from), 'day') <= 3) {
actions.updateQuerySource({ interval: 'hour' } as Partial<InsightQueryNode>)
} else if (dayjs(date_to).diff(dayjs(date_from), 'month') <= 3) {
actions.updateQuerySource({ interval: 'day' } as Partial<InsightQueryNode>)
} else {
actions.updateQuerySource({ interval: 'month' } as Partial<InsightQueryNode>)
}
return
}
// get a defaultInterval for dateOptions that have a default value
let interval: IntervalType = 'day'
for (const { key, values, defaultInterval } of dateMapping) {
if (
values[0] === date_from &&
values[1] === (date_to || undefined) &&
key !== 'Custom' &&
defaultInterval
) {
interval = defaultInterval
break
}
}
actions.updateQuerySource({ interval } as Partial<InsightQueryNode>)
},
}),
})
110 changes: 94 additions & 16 deletions frontend/src/scenes/insights/insightVizDataLogic.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import posthog from 'posthog-js'
import { actions, connect, kea, key, listeners, path, props, selectors, reducers } from 'kea'
import { BaseMathType, ChartDisplayType, InsightLogicProps } from '~/types'
import { BaseMathType, ChartDisplayType, InsightLogicProps, IntervalType } from '~/types'
import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils'
import {
BreakdownFilter,
Expand Down Expand Up @@ -48,9 +48,15 @@ import { sceneLogic } from 'scenes/sceneLogic'
import type { insightVizDataLogicType } from './insightVizDataLogicType'
import { parseProperties } from 'lib/components/PropertyFilters/utils'
import { filterTestAccountsDefaultsLogic } from 'scenes/project/Settings/filterTestAccountDefaultsLogic'
import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic'
import { lemonToast } from '@posthog/lemon-ui'
import { dayjs } from 'lib/dayjs'
import { dateMapping } from 'lib/utils'

const SHOW_TIMEOUT_MESSAGE_AFTER = 5000

type QuerySourceUpdate = Omit<Partial<InsightQueryNode>, 'kind'>

export const insightVizDataLogic = kea<insightVizDataLogicType>([
props({} as InsightLogicProps),
key(keyForInsightLogicProps('new')),
Expand All @@ -73,7 +79,7 @@ export const insightVizDataLogic = kea<insightVizDataLogicType>([

actions({
saveInsight: (redirectToViewMode = true) => ({ redirectToViewMode }),
updateQuerySource: (querySource: Omit<Partial<InsightQueryNode>, 'kind'>) => ({ querySource }),
updateQuerySource: (querySource: QuerySourceUpdate) => ({ querySource }),
updateInsightFilter: (insightFilter: InsightFilter) => ({ insightFilter }),
updateDateRange: (dateRange: DateRange) => ({ dateRange }),
updateBreakdown: (breakdown: BreakdownFilter) => ({ breakdown }),
Expand Down Expand Up @@ -186,19 +192,8 @@ export const insightVizDataLogic = kea<insightVizDataLogicType>([

activeUsersMath: [
(s) => [s.series],
(series): BaseMathType.MonthlyActiveUsers | BaseMathType.WeeklyActiveUsers | null => {
for (const seriesItem of series || []) {
if (seriesItem.math === BaseMathType.WeeklyActiveUsers) {
return BaseMathType.WeeklyActiveUsers
}

if (seriesItem.math === BaseMathType.MonthlyActiveUsers) {
return BaseMathType.MonthlyActiveUsers
}
}

return null
},
(series): BaseMathType.MonthlyActiveUsers | BaseMathType.WeeklyActiveUsers | null =>
getActiveUsersMath(series),
],

erroredQueryId: [
Expand Down Expand Up @@ -230,7 +225,10 @@ export const insightVizDataLogic = kea<insightVizDataLogicType>([
updateQuerySource: ({ querySource }) => {
actions.setQuery({
...values.query,
source: { ...values.querySource, ...querySource },
source: {
...values.querySource,
...handleQuerySourceUpdateSideEffects(querySource, values.querySource as InsightQueryNode),
},
} as Node)
},
setQuery: ({ query }) => {
Expand Down Expand Up @@ -266,3 +264,83 @@ export const insightVizDataLogic = kea<insightVizDataLogicType>([
},
})),
])

const getActiveUsersMath = (
series: TrendsQuery['series'] | null | undefined
): BaseMathType.WeeklyActiveUsers | BaseMathType.MonthlyActiveUsers | null => {
for (const seriesItem of series || []) {
if (seriesItem.math === BaseMathType.WeeklyActiveUsers) {
return BaseMathType.WeeklyActiveUsers
}

if (seriesItem.math === BaseMathType.MonthlyActiveUsers) {
return BaseMathType.MonthlyActiveUsers
}
}

return null
}

const handleQuerySourceUpdateSideEffects = (
update: QuerySourceUpdate,
currentState: InsightQueryNode
): QuerySourceUpdate => {
const mergedUpdate = { ...update }

const maybeChangedSeries = (update as TrendsQuery).series || null
const maybeChangedActiveUsersMath = maybeChangedSeries ? getActiveUsersMath(maybeChangedSeries) : null
const interval = (currentState as TrendsQuery).interval

// If the user just flipped an event action to use WAUs/MAUs math and their
// current interval is unsupported by the math type, switch their interval
// to an appropriate allowed interval and inform them of the change via a toast
if (maybeChangedActiveUsersMath !== null && (interval === 'hour' || interval === 'month')) {
if (interval === 'hour') {
lemonToast.info(
`Switched to grouping by day, because "${BASE_MATH_DEFINITIONS[maybeChangedActiveUsersMath].name}" does not support grouping by ${interval}.`
)
;(mergedUpdate as Partial<TrendsQuery>).interval = 'day'
} else if (interval === 'month' && maybeChangedActiveUsersMath === BaseMathType.WeeklyActiveUsers) {
lemonToast.info(
`Switched to grouping by week, because "${BASE_MATH_DEFINITIONS[maybeChangedActiveUsersMath].name}" does not support grouping by ${interval}.`
)
;(mergedUpdate as Partial<TrendsQuery>).interval = 'week'
}
}

if (
update.dateRange &&
update.dateRange.date_from &&
(update.dateRange.date_from !== currentState.dateRange?.date_from ||
update.dateRange.date_to !== currentState.dateRange?.date_to)
) {
const { date_from, date_to } = { ...currentState.dateRange, ...update.dateRange }

if (date_from && date_to && dayjs(date_from).isValid() && dayjs(date_to).isValid()) {
if (dayjs(date_to).diff(dayjs(date_from), 'day') <= 3) {
;(mergedUpdate as Partial<TrendsQuery>).interval = 'hour'
} else if (dayjs(date_to).diff(dayjs(date_from), 'month') <= 3) {
;(mergedUpdate as Partial<TrendsQuery>).interval = 'day'
} else {
;(mergedUpdate as Partial<TrendsQuery>).interval = 'month'
}
} else {
// get a defaultInterval for dateOptions that have a default value
let newDefaultInterval: IntervalType = 'day'
for (const { key, values, defaultInterval } of dateMapping) {
if (
values[0] === date_from &&
values[1] === (date_to || undefined) &&
key !== 'Custom' &&
defaultInterval
) {
newDefaultInterval = defaultInterval
break
}
}
;(mergedUpdate as Partial<TrendsQuery>).interval = newDefaultInterval
}
}

return mergedUpdate
}

0 comments on commit eed58c7

Please sign in to comment.