Skip to content

Commit

Permalink
Introduce version names
Browse files Browse the repository at this point in the history
  • Loading branch information
apata committed Jan 14, 2025
1 parent 97d72a9 commit c20fbd3
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 266 deletions.
4 changes: 2 additions & 2 deletions assets/js/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { createAppRouter } from './dashboard/router'
import ErrorBoundary from './dashboard/error/error-boundary'
import * as api from './dashboard/api'
import * as timer from './dashboard/util/realtime-update-timer'
import { filtersBackwardsCompatibilityRedirect } from './dashboard/query'
import { redirectForLegacyParams } from './dashboard/util/url-search-params'
import SiteContextProvider, {
parseSiteFromDataset
} from './dashboard/site-context'
Expand Down Expand Up @@ -38,7 +38,7 @@ if (container && container.dataset) {
}

try {
filtersBackwardsCompatibilityRedirect(window.location, window.history)
redirectForLegacyParams(window.location, window.history)
} catch (e) {
console.error('Error redirecting in a backwards compatible way', e)
}
Expand Down
13 changes: 10 additions & 3 deletions assets/js/dashboard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/** @format */

import React, { useState } from 'react'

import { useIsRealtimeDashboard } from './util/filters'
import React, { useMemo, useState } from 'react'
import VisitorGraph from './stats/graph/visitor-graph'
import Sources from './stats/sources'
import Pages from './stats/pages'
Expand All @@ -11,6 +9,8 @@ import Devices from './stats/devices'
import { TopBar } from './nav-menu/top-bar'
import Behaviours from './stats/behaviours'
import { FiltersBar } from './nav-menu/filters-bar'
import { useQueryContext } from './query-context'
import { isRealTimeDashboard } from './util/filters'

function DashboardStats({
importedDataInView,
Expand Down Expand Up @@ -48,6 +48,13 @@ function DashboardStats({
)
}

function useIsRealtimeDashboard() {
const {
query: { period }
} = useQueryContext()
return useMemo(() => isRealTimeDashboard({ period }), [period])
}

function Dashboard() {
const isRealTimeDashboard = useIsRealtimeDashboard()
const [importedDataInView, setImportedDataInView] = useState(false)
Expand Down
58 changes: 0 additions & 58 deletions assets/js/dashboard/query.test.ts

This file was deleted.

108 changes: 1 addition & 107 deletions assets/js/dashboard/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,11 @@ import {
parseUTCDate,
isAfter
} from './util/date'
import {
FILTER_OPERATIONS,
getFiltersByKeyPrefix,
parseLegacyFilter,
parseLegacyPropsFilter
} from './util/filters'
import { FILTER_OPERATIONS, getFiltersByKeyPrefix } from './util/filters'
import { PlausibleSite } from './site-context'
import { ComparisonMode, QueryPeriod } from './query-time-periods'
import { AppNavigationTarget } from './navigation/use-app-navigate'
import { Dayjs } from 'dayjs'
import { legacyParseSearch } from './util/legacy-jsonurl-url-search-params'
import {
FILTER_URL_PARAM_NAME,
stringifySearch
} from './util/url-search-params'

export type FilterClause = string | number

Expand Down Expand Up @@ -71,29 +61,6 @@ export function addFilter(
return { ...query, filters: [...query.filters, filter] }
}

const LEGACY_URL_PARAMETERS = {
goal: null,
source: null,
utm_medium: null,
utm_source: null,
utm_campaign: null,
utm_content: null,
utm_term: null,
referrer: null,
screen: null,
browser: null,
browser_version: null,
os: null,
os_version: null,
country: 'country_labels',
region: 'region_labels',
city: 'city_labels',
page: null,
hostname: null,
entry_page: null,
exit_page: null
}

export function postProcessFilters(filters: Array<Filter>): Array<Filter> {
return filters.map(([operation, dimension, clauses]) => {
// Rename old name of the operation
Expand All @@ -104,79 +71,6 @@ export function postProcessFilters(filters: Array<Filter>): Array<Filter> {
})
}

export function maybeGetRedirectTargetFromLegacySearchParams(
windowLocation: Location
): null | string {
const searchParams = new URLSearchParams(windowLocation.search)
const isCurrentVersion = searchParams.get(FILTER_URL_PARAM_NAME)
if (isCurrentVersion) {
return null
}

const isJsonURLVersion = searchParams.get('filters')
if (isJsonURLVersion) {
return `${windowLocation.pathname}${stringifySearch(legacyParseSearch(windowLocation.search))}`
}

const searchRecord = legacyParseSearch(windowLocation.search)
const searchRecordEntries = Object.entries(
legacyParseSearch(windowLocation.search)
)

const isBeforeJsonURLVersion = searchRecordEntries.some(
([k]) => k === 'props' || LEGACY_URL_PARAMETERS.hasOwnProperty(k)
)

if (!isBeforeJsonURLVersion) {
return null
}

const changedSearchRecordEntries = []
const filters: DashboardQuery['filters'] = []
let labels: DashboardQuery['labels'] = {}

for (const [key, value] of searchRecordEntries) {
if (LEGACY_URL_PARAMETERS.hasOwnProperty(key)) {
const filter = parseLegacyFilter(key, value) as Filter
filters.push(filter)
const labelsKey: string | null | undefined =
LEGACY_URL_PARAMETERS[key as keyof typeof LEGACY_URL_PARAMETERS]
if (labelsKey && searchRecord[labelsKey]) {
const clauses = filter[2]
const labelsValues = (searchRecord[labelsKey] as string)
.split('|')
.filter((label) => !!label)
const newLabels = Object.fromEntries(
clauses.map((clause, index) => [clause, labelsValues[index]])
)

labels = Object.assign(labels, newLabels)
}
} else {
changedSearchRecordEntries.push([key, value])
}
}

if (searchRecord['props']) {
filters.push(...(parseLegacyPropsFilter(searchRecord['props']) as Filter[]))
}
changedSearchRecordEntries.push(['filters', filters], ['labels', labels])
return `${windowLocation.pathname}${stringifySearch(Object.fromEntries(changedSearchRecordEntries))}`
}

/** Called once before React app mounts. If legacy url search params are present, does a redirect to new format. */
export function filtersBackwardsCompatibilityRedirect(
windowLocation: Location,
windowHistory: History
) {
const redirectTargetURL =
maybeGetRedirectTargetFromLegacySearchParams(windowLocation)
if (redirectTargetURL === null) {
return
}
windowHistory.pushState({}, '', redirectTargetURL)
}

// Returns a boolean indicating whether the given query includes a
// non-empty goal filterset containing a single, or multiple revenue
// goals with the same currency. Used to decide whether to render
Expand Down
60 changes: 1 addition & 59 deletions assets/js/dashboard/util/filters.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/** @format */

import React, { useMemo } from 'react'
import React from 'react'
import * as api from '../api'
import { useQueryContext } from '../query-context'

export const FILTER_MODAL_TO_FILTER_GROUP = {
page: ['page', 'entry_page', 'exit_page'],
Expand Down Expand Up @@ -40,12 +39,6 @@ export const FILTER_OPERATIONS_DISPLAY_NAMES = {
[FILTER_OPERATIONS.contains_not]: 'does not contain'
}

const OPERATION_PREFIX = {
[FILTER_OPERATIONS.isNot]: '!',
[FILTER_OPERATIONS.contains]: '~',
[FILTER_OPERATIONS.is]: ''
}

export function supportsIsNot(filterName) {
return !['goal', 'prop_key'].includes(filterName)
}
Expand All @@ -62,17 +55,6 @@ export function isFreeChoiceFilterOperation(operation) {
)
}

// As of March 2023, Safari does not support negative lookbehind regexes. In case it throws an error, falls back to plain | matching. This means
// escaping pipe characters in filters does not currently work in Safari
let NON_ESCAPED_PIPE_REGEX
try {
NON_ESCAPED_PIPE_REGEX = new RegExp('(?<!\\\\)\\|', 'g')
} catch (_e) {
NON_ESCAPED_PIPE_REGEX = '|'
}

const ESCAPED_PIPE = '\\|'

export function getLabel(labels, filterKey, value) {
if (['country', 'region', 'city'].includes(filterKey)) {
return labels[value]
Expand Down Expand Up @@ -118,27 +100,10 @@ export function hasGoalFilter(query) {
return getFiltersByKeyPrefix(query, 'goal').length > 0
}

export function useHasGoalFilter() {
const {
query: { filters }
} = useQueryContext()
return useMemo(
() => getFiltersByKeyPrefix({ filters }, 'goal').length > 0,
[filters]
)
}

export function isRealTimeDashboard(query) {
return query?.period === 'realtime'
}

export function useIsRealtimeDashboard() {
const {
query: { period }
} = useQueryContext()
return useMemo(() => isRealTimeDashboard({ period }), [period])
}

export function plainFilterText(query, [operation, filterKey, clauses]) {
const formattedFilter = formattedFilters[filterKey]

Expand Down Expand Up @@ -295,26 +260,3 @@ export const formattedFilters = {
entry_page: 'Entry Page',
exit_page: 'Exit Page'
}

export function parseLegacyFilter(filterKey, rawValue) {
const operation =
Object.keys(OPERATION_PREFIX).find(
(operation) => OPERATION_PREFIX[operation] === rawValue[0]
) || FILTER_OPERATIONS.is

const value =
operation === FILTER_OPERATIONS.is ? rawValue : rawValue.substring(1)

const clauses = value
.split(NON_ESCAPED_PIPE_REGEX)
.filter((clause) => !!clause)
.map((val) => val.replaceAll(ESCAPED_PIPE, '|'))

return [operation, filterKey, clauses]
}

export function parseLegacyPropsFilter(rawValue) {
return Object.entries(JSON.parse(rawValue)).map(([key, propVal]) => {
return parseLegacyFilter(`${EVENT_PROPS_PREFIX}${key}`, propVal)
})
}
Loading

0 comments on commit c20fbd3

Please sign in to comment.