diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts index 86058fe4b0f..a12b55544a2 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+layout.ts @@ -1,8 +1,13 @@ import { fetchMagicAuthToken } from "@rilldata/web-admin/features/projects/selectors"; +import { getDashboardStateFromUrl } from "@rilldata/web-common/features/dashboards/proto-state/fromProto"; +import { getUpdatedUrlForExploreState } from "@rilldata/web-common/features/dashboards/url-state/getUpdatedUrlForExploreState"; import { fetchExploreSpec } from "@rilldata/web-common/features/explores/selectors"; import { error } from "@sveltejs/kit"; -export const load = async ({ params: { token }, parent }) => { +export const load = async ({ + params: { token, organization, project }, + parent, +}) => { const { runtime } = await parent(); try { @@ -12,16 +17,48 @@ export const load = async ({ params: { token }, parent }) => { throw new Error("Token does not have an associated resource name"); } - const { explore, metricsView, defaultExplorePreset } = - await fetchExploreSpec( - runtime.instanceId as string, - tokenData.token.resourceName, + const exploreName = tokenData.token?.resourceName; + + const { + explore, + metricsView, + defaultExplorePreset, + initExploreState, + initLoadedOutsideOfURL, + } = await fetchExploreSpec( + runtime?.instanceId, + exploreName, + `__${organization}__${project}`, + ); + const metricsViewSpec = metricsView.metricsView?.state?.validSpec ?? {}; + const exploreSpec = explore.explore?.state?.validSpec ?? {}; + + if (tokenData.token?.state) { + const exploreStateFromToken = getDashboardStateFromUrl( + tokenData.token?.state, + metricsViewSpec, + exploreSpec, + {}, // TODO ); + Object.assign(initExploreState, exploreStateFromToken); + } + const initUrlSearch = + initLoadedOutsideOfURL || !!tokenData.token?.state + ? getUpdatedUrlForExploreState( + exploreSpec, + defaultExplorePreset, + initExploreState, + new URLSearchParams(), + ) + : ""; + console.log(tokenData.token?.state, initExploreState, initUrlSearch); return { explore, metricsView, defaultExplorePreset, + initExploreState, + initUrlSearch, token: tokenData?.token, }; } catch (e) { diff --git a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte index 36b60944f6a..ec331c74d4b 100644 --- a/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte +++ b/web-admin/src/routes/[organization]/[project]/-/share/[token]/+page.svelte @@ -15,8 +15,10 @@ $: ({ defaultExplorePreset, + initExploreState, + initUrlSearch, partialExploreState, - loadedOutsideOfURL, + urlSearchForPartial, token: { resourceName }, } = data); $: ({ organization, project } = $page.params); @@ -57,9 +59,13 @@ exploreName={resourceName} > { const { explore, metricsView, defaultExplorePreset, token } = await parent(); const { organization, project } = params; - const exploreName = token.resourceName; + const exploreName = token?.resourceName; const metricsViewSpec = metricsView.metricsView?.state?.validSpec; const exploreSpec = explore.explore?.state?.validSpec; - // On the first dashboard load, if there are no URL params, append the token's state (in human-readable format) to the URL. - if ( - token.state && - ![...url.searchParams.keys()].length && - !(exploreName in get(metricsExplorerStore).entities) - ) { - const exploreState = getDashboardStateFromUrl( - token.state, - metricsViewSpec, - exploreSpec, - {}, // TODO - ); - const newUrl = new URL(url); - newUrl.search = convertExploreStateToURLSearchParams( - exploreState as MetricsExplorerEntity, - exploreSpec, - defaultExplorePreset, - ); - throw redirect(307, `${newUrl.pathname}${newUrl.search}`); - } - // Get Explore state from URL params let partialExploreState: Partial = {}; - let loadedOutsideOfURL = false; + let urlSearchForPartial = ""; const errors: Error[] = []; if (metricsViewSpec && exploreSpec) { const { partialExploreState: partialExploreStateFromUrl, - loadedOutsideOfURL: partialLoadedOutsideOfURL, + urlSearchForPartial: _urlSearchForPartial, errors: errorsFromConvert, } = convertURLToExploreState( exploreName, @@ -53,12 +27,13 @@ export const load = async ({ url, parent, params }) => { ); partialExploreState = partialExploreStateFromUrl; errors.push(...errorsFromConvert); - loadedOutsideOfURL = partialLoadedOutsideOfURL; + urlSearchForPartial = _urlSearchForPartial; } + console.log(partialExploreState, urlSearchForPartial); return { partialExploreState, - loadedOutsideOfURL, + urlSearchForPartial, errors, }; }; diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts index 6ef8f266610..607f9f2eca5 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+layout.ts @@ -24,16 +24,17 @@ export const load = async ({ params, depends, parent }) => { let initExploreState: Partial = {}; let initLoadedOutsideOfURL = false; try { - const fetchedExploreSpecDetails = await fetchExploreSpec( + ({ + explore, + metricsView, + defaultExplorePreset, + initExploreState, + initLoadedOutsideOfURL, + } = await fetchExploreSpec( runtime?.instanceId, exploreName, `__${organization}__${projectName}`, - ); - explore = fetchedExploreSpecDetails.explore; - metricsView = fetchedExploreSpecDetails.metricsView; - defaultExplorePreset = fetchedExploreSpecDetails.defaultExplorePreset; - initExploreState = fetchedExploreSpecDetails.initExploreState; - initLoadedOutsideOfURL = fetchedExploreSpecDetails.initLoadedOutsideOfURL; + )); } catch { // error handled in +page.svelte for now // TODO: move it here diff --git a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts index 905a43429eb..48e0520c4e2 100644 --- a/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts +++ b/web-admin/src/routes/[organization]/[project]/explore/[dashboard]/+page.ts @@ -1,5 +1,4 @@ import { convertURLToExploreState } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; -import { getUpdatedUrlForExploreState } from "@rilldata/web-common/features/dashboards/url-state/getUpdatedUrlForExploreState"; export const load = async ({ url, parent, params }) => { const { explore, metricsView, defaultExplorePreset } = await parent(); @@ -7,7 +6,7 @@ export const load = async ({ url, parent, params }) => { const metricsViewSpec = metricsView.metricsView?.state?.validSpec; const exploreSpec = explore.explore?.state?.validSpec; - const { partialExploreState, loadedOutsideOfURL, errors } = + const { partialExploreState, urlSearchForPartial, errors } = convertURLToExploreState( exploreName, `__${organization}__${project}`, @@ -16,16 +15,8 @@ export const load = async ({ url, parent, params }) => { exploreSpec, defaultExplorePreset, ); - const urlSearchForPartial = loadedOutsideOfURL - ? getUpdatedUrlForExploreState( - exploreSpec, - defaultExplorePreset, - partialExploreState, - url.searchParams, - ) - : url.searchParams.toString(); + console.log(partialExploreState, urlSearchForPartial); - console.log(exploreName, urlSearchForPartial); return { partialExploreState, urlSearchForPartial, diff --git a/web-common/src/features/dashboards/proto-state/fromProto.ts b/web-common/src/features/dashboards/proto-state/fromProto.ts index 3084b27fb22..d52d0f76a9f 100644 --- a/web-common/src/features/dashboards/proto-state/fromProto.ts +++ b/web-common/src/features/dashboards/proto-state/fromProto.ts @@ -214,7 +214,9 @@ export function getDashboardStateFromProto( entity.dashboardSortType = dashboard.leaderboardSortType; } - entity.pivot = fromPivotProto(dashboard, metricsView); + if (dashboard.pivotIsActive !== undefined) { + entity.pivot = fromPivotProto(dashboard, metricsView); + } Object.assign(entity, fromActivePageProto(dashboard)); diff --git a/web-common/src/features/dashboards/state-managers/state-managers.ts b/web-common/src/features/dashboards/state-managers/state-managers.ts index 3e98415ee8c..7e412588712 100644 --- a/web-common/src/features/dashboards/state-managers/state-managers.ts +++ b/web-common/src/features/dashboards/state-managers/state-managers.ts @@ -124,6 +124,8 @@ export function createStateManagers({ query: { queryClient, enabled: !!validSpec?.data?.metricsView?.timeDimension, + staleTime: Infinity, + cacheTime: Infinity, }, }, ).subscribe(set), diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte index 36aa8ca1716..af073e5d567 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSync.svelte @@ -36,8 +36,6 @@ $: ({ error, data: timeRangeSummary } = $timeRangeSummaryStore); $: timeRangeSummaryError = error as HTTPError; - $: console.log(exploreName, initUrlSearch, urlSearchForPartial); - let prevUrl = ""; function gotoNewState() { if (!exploreSpec) return; diff --git a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte index 791857a8ba8..d9445700b3f 100644 --- a/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte +++ b/web-common/src/features/dashboards/url-state/DashboardURLStateSyncWrapper.svelte @@ -2,10 +2,15 @@ import { page } from "$app/stores"; import { useMetricsViewTimeRange } from "@rilldata/web-common/features/dashboards/selectors"; import { getStateManagers } from "@rilldata/web-common/features/dashboards/state-managers/state-managers"; + import { restorePersistedDashboardState } from "@rilldata/web-common/features/dashboards/stores/dashboard-store-defaults"; import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; - import { convertURLToExploreState } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; + import { + convertPresetToExploreState, + convertURLToExploreState, + } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; import DashboardURLStateSync from "@rilldata/web-common/features/dashboards/url-state/DashboardURLStateSync.svelte"; import { getDefaultExplorePreset } from "@rilldata/web-common/features/dashboards/url-state/getDefaultExplorePreset"; + import { getUpdatedUrlForExploreState } from "@rilldata/web-common/features/dashboards/url-state/getUpdatedUrlForExploreState"; import type { V1ExplorePreset } from "@rilldata/web-common/runtime-client"; import { runtime } from "@rilldata/web-common/runtime-client/runtime-store"; @@ -32,14 +37,42 @@ exploreSpec, $metricsViewTimeRange.data, ); + let initExploreState: Partial = {}; + let initUrlSearch = ""; + $: { + ({ partialExploreState: initExploreState } = convertPresetToExploreState( + metricsViewSpec, + exploreSpec, + defaultExplorePreset, + )); + + let initLoadedOutsideOfURL = false; + const stateFromLocalStorage = restorePersistedDashboardState( + exploreSpec, + storeKeyPrefix + $exploreName, + ); + if (stateFromLocalStorage) { + initLoadedOutsideOfURL = true; + Object.assign(initExploreState, stateFromLocalStorage); + } + + initUrlSearch = initLoadedOutsideOfURL + ? getUpdatedUrlForExploreState( + exploreSpec, + defaultExplorePreset, + initExploreState, + new URLSearchParams(), + ) + : ""; + } let partialExploreState: Partial = {}; - let loaded = false; + let urlSearchForPartial = ""; function parseUrl(url: URL, defaultExplorePreset: V1ExplorePreset) { // Get Explore state from URL params const { partialExploreState: partialExploreStateFromUrl, - loaded: loadedFromStorage, + urlSearchForPartial: _urlSearchForPartial, } = convertURLToExploreState( $exploreName, storeKeyPrefix, @@ -49,7 +82,7 @@ defaultExplorePreset, ); partialExploreState = partialExploreStateFromUrl; - loaded = loadedFromStorage; + urlSearchForPartial = _urlSearchForPartial; } // only reactive to url and defaultExplorePreset @@ -57,7 +90,15 @@ {#if !$validSpecStore.isLoading && !$metricsViewTimeRange.isLoading} - + {/if} diff --git a/web-common/src/features/dashboards/url-state/convertExploreStateToURLSearchParams.ts b/web-common/src/features/dashboards/url-state/convertExploreStateToURLSearchParams.ts index dce27b5f553..8a94f16bf77 100644 --- a/web-common/src/features/dashboards/url-state/convertExploreStateToURLSearchParams.ts +++ b/web-common/src/features/dashboards/url-state/convertExploreStateToURLSearchParams.ts @@ -381,7 +381,7 @@ function shouldSetParam( // both preset and state value is non-truthy. // EG: one is "" and another is undefined then we should not set param as empty string - if (!presetValue && !exploreStateValue) { + if ((!presetValue && !exploreStateValue) || exploreStateValue === undefined) { return false; } diff --git a/web-common/src/features/dashboards/url-state/convertPresetToExploreState.ts b/web-common/src/features/dashboards/url-state/convertPresetToExploreState.ts index 6cdca48f865..97aea9a43bb 100644 --- a/web-common/src/features/dashboards/url-state/convertPresetToExploreState.ts +++ b/web-common/src/features/dashboards/url-state/convertPresetToExploreState.ts @@ -11,6 +11,7 @@ import { getMultiFieldError, getSingleFieldError, } from "@rilldata/web-common/features/dashboards/url-state/error-message-helpers"; +import { getUpdatedUrlForExploreState } from "@rilldata/web-common/features/dashboards/url-state/getUpdatedUrlForExploreState"; import { ToLegacySortTypeMap } from "@rilldata/web-common/features/dashboards/url-state/legacyMappers"; import { FromURLParamTDDChartMap, @@ -68,7 +69,17 @@ export function convertURLToExploreState( const { partialExploreState, errors: errorsFromEntity } = convertPresetToExploreState(metricsView, exploreSpec, preset); errors.push(...errorsFromEntity); - return { partialExploreState, loadedOutsideOfURL, errors }; + + const urlSearchForPartial = loadedOutsideOfURL + ? getUpdatedUrlForExploreState( + exploreSpec, + defaultExplorePreset, + partialExploreState, + searchParams, + ) + : searchParams.toString(); + + return { partialExploreState, urlSearchForPartial, errors }; } /** @@ -316,9 +327,15 @@ function fromTimeDimensionUrlParams( partialExploreState: Partial; errors: Error[]; } { - if (preset.timeDimensionMeasure === undefined) { + if (!preset.timeDimensionMeasure) { return { - partialExploreState: {}, + partialExploreState: { + tdd: { + expandedMeasureName: "", + chartType: TDDChart.DEFAULT, + pinIndex: -1, + }, + }, errors: [], }; } @@ -331,20 +348,6 @@ function fromTimeDimensionUrlParams( errors.push(getSingleFieldError("expanded measure", expandedMeasureName)); } - // unset - if (expandedMeasureName === "") { - return { - partialExploreState: { - tdd: { - expandedMeasureName: "", - chartType: TDDChart.DEFAULT, - pinIndex: -1, - }, - }, - errors, - }; - } - const partialExploreState: Partial = { tdd: { expandedMeasureName, @@ -435,7 +438,25 @@ function fromPivotUrlParams( if (!hasSomePivotFields && !pivotIsActive) { return { - partialExploreState: {}, + partialExploreState: { + pivot: { + active: false, + rows: { + dimension: [], + }, + columns: { + measure: [], + dimension: [], + }, + sorting: [], + expanded: {}, + columnPage: 1, + rowPage: 1, + enableComparison: true, + activeCell: null, + rowJoinType: "nest", + }, + }, errors, }; } diff --git a/web-common/src/features/dashboards/url-state/getUpdatedUrlForExploreState.ts b/web-common/src/features/dashboards/url-state/getUpdatedUrlForExploreState.ts index 69e512e008c..1b0fd5183ea 100644 --- a/web-common/src/features/dashboards/url-state/getUpdatedUrlForExploreState.ts +++ b/web-common/src/features/dashboards/url-state/getUpdatedUrlForExploreState.ts @@ -1,7 +1,9 @@ import type { MetricsExplorerEntity } from "@rilldata/web-common/features/dashboards/stores/metrics-explorer-entity"; +import { TDDChart } from "@rilldata/web-common/features/dashboards/time-dimension-details/types"; import { convertExploreStateToURLSearchParams } from "@rilldata/web-common/features/dashboards/url-state/convertExploreStateToURLSearchParams"; import { FromURLParamViewMap } from "@rilldata/web-common/features/dashboards/url-state/mappers"; import { ExploreStateURLParams } from "@rilldata/web-common/features/dashboards/url-state/url-params"; +import { DashboardState_ActivePage } from "@rilldata/web-common/proto/gen/rill/ui/v1/dashboard_pb"; import type { V1ExplorePreset, V1ExploreSpec, diff --git a/web-local/src/routes/(viz)/explore/[name]/+page.ts b/web-local/src/routes/(viz)/explore/[name]/+page.ts index bd8003db3e6..80e535f3462 100644 --- a/web-local/src/routes/(viz)/explore/[name]/+page.ts +++ b/web-local/src/routes/(viz)/explore/[name]/+page.ts @@ -1,5 +1,4 @@ import { convertURLToExploreState } from "@rilldata/web-common/features/dashboards/url-state/convertPresetToExploreState"; -import { getUpdatedUrlForExploreState } from "@rilldata/web-common/features/dashboards/url-state/getUpdatedUrlForExploreState"; export const load = async ({ url, parent, params }) => { const { explore, metricsView, defaultExplorePreset } = await parent(); @@ -7,7 +6,7 @@ export const load = async ({ url, parent, params }) => { const metricsViewSpec = metricsView.metricsView?.state?.validSpec; const exploreSpec = explore.explore?.state?.validSpec; - const { partialExploreState, loadedOutsideOfURL, errors } = + const { partialExploreState, urlSearchForPartial, errors } = convertURLToExploreState( exploreName, undefined, @@ -16,14 +15,6 @@ export const load = async ({ url, parent, params }) => { exploreSpec, defaultExplorePreset, ); - const urlSearchForPartial = loadedOutsideOfURL - ? getUpdatedUrlForExploreState( - exploreSpec, - defaultExplorePreset, - partialExploreState, - url.searchParams, - ) - : url.searchParams.toString(); return { partialExploreState,