Show persistent for local-storage trackers
Local storage, IndexedDB, and cache storage have no expiry yet persist until explicitly cleared, so a missing max-age should read as "persistent", not "session" (the latter only fits cookies and session storage, which end with the session or tab). Thread the tracker type through humanizeSeconds (helpers) and humanizeDuration (cookie-banner, with a localized persistent label) and pass it at every console and banner call site. The consent record query now selects trackerType so its duration column can make the same distinction. This mirrors the Go HumanizedDuration helper that already renders these types as persistent. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -49,6 +49,7 @@ export const cookieBannerConsentRecordPageQuery = graphql`
|
||||
kind
|
||||
cookies {
|
||||
name
|
||||
trackerType
|
||||
maxAgeSeconds
|
||||
description
|
||||
}
|
||||
@@ -235,7 +236,7 @@ export default function CookieBannerConsentRecordPage({
|
||||
{cookie.name}
|
||||
</td>
|
||||
<td className="py-1 pr-4 text-txt-secondary">
|
||||
{humanizeSeconds(cookie.maxAgeSeconds ?? null)}
|
||||
{humanizeSeconds(cookie.maxAgeSeconds ?? null, cookie.trackerType)}
|
||||
</td>
|
||||
<td className="py-1 text-txt-secondary">
|
||||
{cookie.description}
|
||||
|
||||
@@ -795,7 +795,7 @@ export function CategorySection({ categoryKey, connectionId }: CategorySectionPr
|
||||
})()}
|
||||
</Td>
|
||||
<Td className="text-sm text-muted-foreground">
|
||||
{humanizeSeconds(pattern.maxAgeSeconds ?? null)}
|
||||
{humanizeSeconds(pattern.maxAgeSeconds ?? null, pattern.trackerType)}
|
||||
</Td>
|
||||
<Td>
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
@@ -161,7 +161,7 @@ export function TrackerPatternPropertiesSection({
|
||||
</PropertyRow>
|
||||
<PropertyRow label={__("Max Age")}>
|
||||
<span className="text-sm">
|
||||
{humanizeSeconds(pattern.maxAgeSeconds ?? null)}
|
||||
{humanizeSeconds(pattern.maxAgeSeconds ?? null, pattern.trackerType)}
|
||||
</span>
|
||||
</PropertyRow>
|
||||
{pattern.description && (
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
} from "@probo/ui";
|
||||
import { useState } from "react";
|
||||
import { graphql, useFragment, useMutation } from "react-relay";
|
||||
import { ConnectionHandler } from "relay-runtime";
|
||||
|
||||
import type { TrackerPatternRowDeleteMutation } from "#/__generated__/core/TrackerPatternRowDeleteMutation.graphql";
|
||||
import type { TrackerPatternRowFragment$key } from "#/__generated__/core/TrackerPatternRowFragment.graphql";
|
||||
@@ -94,6 +93,8 @@ const movePatternMutation = graphql`
|
||||
id
|
||||
cookieCategory {
|
||||
id
|
||||
name
|
||||
kind
|
||||
}
|
||||
}
|
||||
cookieBanner {
|
||||
@@ -214,17 +215,6 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
|
||||
targetCookieCategoryId: targetCategoryId,
|
||||
},
|
||||
},
|
||||
updater(store) {
|
||||
const payload = store.getRootField("moveTrackerPatternToCategory");
|
||||
if (!payload?.getLinkedRecord("trackerPattern")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conn = store.get(connectionId);
|
||||
if (conn) {
|
||||
ConnectionHandler.deleteNode(conn, pattern.id);
|
||||
}
|
||||
},
|
||||
onCompleted(_, errors) {
|
||||
if (errors?.length) {
|
||||
toast({ title: __("Error"), description: errors[0].message, variant: "error" });
|
||||
@@ -392,7 +382,7 @@ export function TrackerPatternRow({ patternKey, connectionId }: TrackerPatternRo
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<span className="pl-2">{humanizeSeconds(pattern.maxAgeSeconds ?? null)}</span>
|
||||
<span className="pl-2">{humanizeSeconds(pattern.maxAgeSeconds ?? null, pattern.trackerType)}</span>
|
||||
</Td>
|
||||
<Td>
|
||||
{pattern.lastMatchedAt
|
||||
|
||||
@@ -52,8 +52,8 @@ export class ProboCookieList extends ProboElement {
|
||||
if (!this.template) return;
|
||||
|
||||
const duration = cookie.max_age_seconds != null
|
||||
? humanizeDuration(cookie.max_age_seconds, lang)
|
||||
: humanizeDuration(0, lang);
|
||||
? humanizeDuration(cookie.max_age_seconds, lang, cookie.tracker_type)
|
||||
: humanizeDuration(0, lang, cookie.tracker_type);
|
||||
|
||||
const type = getTrackerTypeLabel(cookie.tracker_type);
|
||||
|
||||
|
||||
@@ -13,6 +13,16 @@
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import { interpolate } from "./i18n";
|
||||
import type { TrackerType } from "./types";
|
||||
|
||||
// Tracker types whose data persists until explicitly cleared. When such a
|
||||
// tracker has no max-age, its lifetime is "persistent" rather than "session"
|
||||
// (cookies and session storage are cleared when the session/tab ends).
|
||||
const PERSISTENT_TRACKER_TYPES: ReadonlySet<TrackerType> = new Set([
|
||||
"LOCAL_STORAGE",
|
||||
"INDEXED_DB",
|
||||
"CACHE_STORAGE",
|
||||
]);
|
||||
|
||||
interface DurationTexts {
|
||||
[key: string]: string;
|
||||
@@ -35,6 +45,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
|
||||
duration_second_one: "{{count}} second",
|
||||
duration_second_other: "{{count}} seconds",
|
||||
duration_session: "session",
|
||||
duration_persistent: "persistent",
|
||||
},
|
||||
fr: {
|
||||
duration_year_one: "{{count}} an",
|
||||
@@ -52,6 +63,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
|
||||
duration_second_one: "{{count}} seconde",
|
||||
duration_second_other: "{{count}} secondes",
|
||||
duration_session: "session",
|
||||
duration_persistent: "persistant",
|
||||
},
|
||||
de: {
|
||||
duration_year_one: "{{count}} Jahr",
|
||||
@@ -69,6 +81,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
|
||||
duration_second_one: "{{count}} Sekunde",
|
||||
duration_second_other: "{{count}} Sekunden",
|
||||
duration_session: "Sitzung",
|
||||
duration_persistent: "dauerhaft",
|
||||
},
|
||||
es: {
|
||||
duration_year_one: "{{count}} año",
|
||||
@@ -86,6 +99,7 @@ const durationTextsByLanguage: Record<string, DurationTexts> = {
|
||||
duration_second_one: "{{count}} segundo",
|
||||
duration_second_other: "{{count}} segundos",
|
||||
duration_session: "sesión",
|
||||
duration_persistent: "persistente",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -107,9 +121,17 @@ const DURATION_UNITS: [number, string, number][] = [
|
||||
[1, "duration_second", 0],
|
||||
];
|
||||
|
||||
export function humanizeDuration(seconds: number, lang?: string): string {
|
||||
export function humanizeDuration(
|
||||
seconds: number,
|
||||
lang?: string,
|
||||
trackerType?: TrackerType,
|
||||
): string {
|
||||
const texts = getDurationTexts(lang);
|
||||
if (seconds <= 0) return texts.duration_session;
|
||||
if (seconds <= 0) {
|
||||
return trackerType && PERSISTENT_TRACKER_TYPES.has(trackerType)
|
||||
? texts.duration_persistent
|
||||
: texts.duration_session;
|
||||
}
|
||||
|
||||
let remaining = seconds;
|
||||
const parts: string[] = [];
|
||||
|
||||
@@ -22,8 +22,24 @@ export const DURATION_UNITS: { value: string; label: string; singular: string; s
|
||||
{ value: "years", label: "years", singular: "year", seconds: 31536000, snap: 21 * 24 * 3600 },
|
||||
] as const;
|
||||
|
||||
export function humanizeSeconds(seconds: number | null): string {
|
||||
if (seconds === null || seconds <= 0) return "session";
|
||||
// Tracker types whose data persists until explicitly cleared. When such a
|
||||
// tracker has no max-age, its lifetime is "persistent" rather than "session"
|
||||
// (cookies and session storage are cleared when the session/tab ends).
|
||||
const PERSISTENT_TRACKER_TYPES = new Set([
|
||||
"LOCAL_STORAGE",
|
||||
"INDEXED_DB",
|
||||
"CACHE_STORAGE",
|
||||
]);
|
||||
|
||||
export function humanizeSeconds(
|
||||
seconds: number | null,
|
||||
trackerType?: string | null,
|
||||
): string {
|
||||
if (seconds === null || seconds <= 0) {
|
||||
return trackerType && PERSISTENT_TRACKER_TYPES.has(trackerType)
|
||||
? "persistent"
|
||||
: "session";
|
||||
}
|
||||
|
||||
let remaining = seconds;
|
||||
const parts: string[] = [];
|
||||
|
||||
Reference in New Issue
Block a user