diff --git a/bin/upgrade-hobby b/bin/upgrade-hobby index 0e75aeb6352b1..2c882978618e9 100644 --- a/bin/upgrade-hobby +++ b/bin/upgrade-hobby @@ -11,6 +11,13 @@ else exit fi +if [ "$REGISTRY_URL" == "" ] +then +export REGISTRY_URL="posthog/posthog" +fi + +export POSTHOG_APP_TAG="${POSTHOG_APP_TAG:-latest}" + echo "Checking for named postgres and clickhouse volumes to avoid data loss when upgrading from < 1.39" if docker volume ls | grep -Pzoq 'clickhouse-data\n(.|\n)*postgres-data\n' then diff --git a/ee/billing/quota_limiting.py b/ee/billing/quota_limiting.py index fbf35a09e2dd3..f91811d866a40 100644 --- a/ee/billing/quota_limiting.py +++ b/ee/billing/quota_limiting.py @@ -122,7 +122,7 @@ def org_quota_limited_until( if organization.never_drop_data or trust_score == 15: return None - team_tokens = get_team_attribute_by_quota_resource(organization, resource) + team_tokens = get_team_attribute_by_quota_resource(organization) team_being_limited = any(x in previously_quota_limited_team_tokens for x in team_tokens) if team_being_limited: @@ -237,7 +237,7 @@ def sync_org_quota_limits(organization: Organization): previously_quota_limited_team_tokens = list_limited_team_attributes( resource, QuotaLimitingCaches.QUOTA_LIMITER_CACHE_KEY ) - team_attributes = get_team_attribute_by_quota_resource(organization, resource) + team_attributes = get_team_attribute_by_quota_resource(organization) result = org_quota_limited_until(organization, resource, previously_quota_limited_team_tokens) if result: @@ -264,24 +264,14 @@ def sync_org_quota_limits(organization: Organization): remove_limited_team_tokens(resource, team_attributes, QuotaLimitingCaches.QUOTA_LIMITING_SUSPENDED_KEY) -def get_team_attribute_by_quota_resource(organization: Organization, resource: QuotaResource): - if resource in [QuotaResource.EVENTS, QuotaResource.RECORDINGS]: - team_tokens: list[str] = [x for x in list(organization.teams.values_list("api_token", flat=True)) if x] +def get_team_attribute_by_quota_resource(organization: Organization): + team_tokens: list[str] = [x for x in list(organization.teams.values_list("api_token", flat=True)) if x] - if not team_tokens: - capture_exception(Exception(f"quota_limiting: No team tokens found for organization: {organization.id}")) - return + if not team_tokens: + capture_exception(Exception(f"quota_limiting: No team tokens found for organization: {organization.id}")) + return - return team_tokens - - if resource == QuotaResource.ROWS_SYNCED: - team_ids: list[str] = [x for x in list(organization.teams.values_list("id", flat=True)) if x] - - if not team_ids: - capture_exception(Exception(f"quota_limiting: No team ids found for organization: {organization.id}")) - return - - return team_ids + return team_tokens def set_org_usage_summary( diff --git a/ee/billing/test/test_quota_limiting.py b/ee/billing/test/test_quota_limiting.py index 3e8b5105767d3..926e3441c4f73 100644 --- a/ee/billing/test/test_quota_limiting.py +++ b/ee/billing/test/test_quota_limiting.py @@ -92,7 +92,7 @@ def test_quota_limiting_feature_flag_enabled(self, patch_feature_enabled, patch_ patch_capture.reset_mock() # Add this org to the redis cache. - team_tokens = get_team_attribute_by_quota_resource(self.organization, QuotaResource.EVENTS) + team_tokens = get_team_attribute_by_quota_resource(self.organization) add_limited_team_tokens( QuotaResource.EVENTS, {x: 1612137599 for x in team_tokens}, @@ -715,7 +715,7 @@ def test_sync_org_quota_limits(self): # rows_synced uses teams, not tokens assert sorted( list_limited_team_attributes(QuotaResource.ROWS_SYNCED, QuotaLimitingCaches.QUOTA_LIMITER_CACHE_KEY) - ) == sorted(["1337", str(self.team.pk), str(other_team.pk)]) + ) == sorted(["1337", str(self.team.api_token), str(other_team.api_token)]) self.organization.usage["events"]["usage"] = 80 self.organization.usage["rows_synced"]["usage"] = 36 @@ -748,7 +748,7 @@ def test_sync_org_quota_limits(self): list_limited_team_attributes( QuotaResource.ROWS_SYNCED, QuotaLimitingCaches.QUOTA_LIMITING_SUSPENDED_KEY ) - ) == sorted([str(self.team.pk), str(other_team.pk)]) + ) == sorted([str(self.team.api_token), str(other_team.api_token)]) self.organization.usage["events"]["usage"] = 80 self.organization.usage["rows_synced"]["usage"] = 36 diff --git a/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png b/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png index a1b68be569fea..0672903871c50 100644 Binary files a/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png and b/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png differ diff --git a/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png b/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png index a74328c260a96..4873b57513359 100644 Binary files a/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png and b/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png differ diff --git a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png index cc74b8c8cc166..4a7f487fb6b79 100644 Binary files a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png and b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png b/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png index b213cb8e6c970..1308913c32079 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png and b/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png differ diff --git a/frontend/src/layout/navigation-3000/components/TopBar.scss b/frontend/src/layout/navigation-3000/components/TopBar.scss index 1628b3135ea86..3f05046fa8599 100644 --- a/frontend/src/layout/navigation-3000/components/TopBar.scss +++ b/frontend/src/layout/navigation-3000/components/TopBar.scss @@ -49,9 +49,14 @@ .TopBar3000__trail { display: flex; align-items: center; - height: 1rem; margin-top: calc(0.25rem * (1 - var(--breadcrumbs-compaction-rate))); overflow: visible; + + .TopBar3000:not(.TopBar3000--compact) & { + // 1rem of trail height ensures nice tight spacing in the full or transitioning state, + // but we don't want it in the compact state, as it causes title edit buttons to be cut off at top&bottom + height: 1rem; + } } .TopBar3000__here { @@ -65,7 +70,12 @@ font-size: 1rem; font-weight: 700; line-height: 1.2; - visibility: var(--breadcrumbs-title-large-visibility); + + .TopBar3000--compact & { + // It wouldn't be necessary to set visibility, but for some reason without this positioning + // of breadcrumbs becomes borked when entering title editing mode + visibility: hidden; + } > * { position: absolute; @@ -90,7 +100,12 @@ &.TopBar3000__breadcrumb--here { flex-shrink: 1; cursor: default; - visibility: var(--breadcrumbs-title-small-visibility); + + .TopBar3000--full & { + // It wouldn't be necessary to set visibility, but for some reason without this positioning + // of breadcrumbs becomes borked when entering title editing mode + visibility: hidden; + } > * { opacity: 1; diff --git a/frontend/src/layout/navigation-3000/components/TopBar.tsx b/frontend/src/layout/navigation-3000/components/TopBar.tsx index 805e52ad04e74..705f3d155bdc4 100644 --- a/frontend/src/layout/navigation-3000/components/TopBar.tsx +++ b/frontend/src/layout/navigation-3000/components/TopBar.tsx @@ -66,17 +66,13 @@ export function TopBar(): JSX.Element | null { return breadcrumbs.length ? (
{mobileLayout && ( diff --git a/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts b/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts index 210204936359e..5eeaff0109f63 100644 --- a/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts +++ b/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts @@ -117,7 +117,7 @@ export const dataManagementSidebarLogic = kea([ menuItems: [ { label: 'View recordings', - to: urls.replay(ReplayTabs.Recent, { + to: urls.replay(ReplayTabs.Home, { filter_group: { type: FilterLogicalOperator.And, values: [ diff --git a/frontend/src/lib/colors.ts b/frontend/src/lib/colors.ts index 1413b241800a0..cc9530b5bc524 100644 --- a/frontend/src/lib/colors.ts +++ b/frontend/src/lib/colors.ts @@ -1,3 +1,5 @@ +import { captureException } from '@sentry/react' + import { LifecycleToggle } from '~/types' import { LemonTagType } from './lemon-ui/LemonTag' @@ -39,7 +41,9 @@ export const tagColors: LemonTagType[] = [ export function getColorVar(variable: string): string { const colorValue = getComputedStyle(document.body).getPropertyValue('--' + variable) if (!colorValue) { - throw new Error(`Couldn't find color variable --${variable}`) + captureException(new Error(`Couldn't find color variable --${variable}`)) + // Fall back to black or white depending on the theme + return document.body.getAttribute('theme') === 'light' ? '#000' : '#fff' } return colorValue.trim() } diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss b/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss index b21e228a3ef63..9a8afaa08b7af 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss +++ b/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss @@ -16,14 +16,6 @@ outline: 1px solid var(--primary-3000); } - &.react-draggable::after { - // During dashboard layout editing, we need an overlay to prevent accidental interaction with card content - position: absolute; - inset: 0; - z-index: var(--z-content-overlay); - content: ''; - } - .ErrorBoundary { width: 100%; height: 100%; diff --git a/frontend/src/lib/components/Cards/handles.tsx b/frontend/src/lib/components/Cards/handles.tsx index 5e25bd8f244a7..68d82e2b59853 100644 --- a/frontend/src/lib/components/Cards/handles.tsx +++ b/frontend/src/lib/components/Cards/handles.tsx @@ -5,8 +5,8 @@ export function ResizeHandle1D({ orientation }: { orientation: 'horizontal' | 'v return (
- - + + @@ -16,7 +16,7 @@ export function ResizeHandle1D({ orientation }: { orientation: 'horizontal' | 'v - +
) @@ -27,8 +27,8 @@ export function ResizeHandle2D(): JSX.Element { return (
- - + + @@ -36,7 +36,7 @@ export function ResizeHandle2D(): JSX.Element { - +
) diff --git a/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx b/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx new file mode 100644 index 0000000000000..3e34575b31c96 --- /dev/null +++ b/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx @@ -0,0 +1,75 @@ +import { LemonBanner, Spinner } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { useEffect } from 'react' +import useResizeObserver from 'use-resize-observer' + +import { ToolbarUserIntent } from '~/types' + +import { appEditorUrl } from '../AuthorizedUrlList/authorizedUrlListLogic' +import { iframedToolbarBrowserLogic } from './iframedToolbarBrowserLogic' + +function IframeErrorOverlay(): JSX.Element | null { + const logic = iframedToolbarBrowserLogic() + const { iframeBanner } = useValues(logic) + return iframeBanner ? ( +
+ + {iframeBanner.message}. Your site might not allow being embedded in an iframe. You can click "Open in + toolbar" above to visit your site and view the heatmap there. + +
+ ) : null +} + +function LoadingOverlay(): JSX.Element | null { + const logic = iframedToolbarBrowserLogic() + const { loading } = useValues(logic) + return loading ? ( +
+ +
+ ) : null +} + +export function IframedToolbarBrowser({ + iframeRef, + userIntent, +}: { + iframeRef?: React.MutableRefObject + userIntent: ToolbarUserIntent +}): JSX.Element | null { + const logic = iframedToolbarBrowserLogic() + + const { browserUrl } = useValues(logic) + const { onIframeLoad, setIframeWidth } = useActions(logic) + + const { width: iframeWidth } = useResizeObserver({ ref: iframeRef }) + useEffect(() => { + setIframeWidth(iframeWidth ?? null) + }, [iframeWidth]) + + return browserUrl ? ( +
+ + +