Skip to content

Commit 1786032

Browse files
Share inbox and automation presentation semantics
Generated-By: PostHog Code Task-Id: 40c57a59-b4e1-4760-8e56-ecd03e9c2f0f
1 parent 52fcd91 commit 1786032

14 files changed

Lines changed: 97 additions & 163 deletions

File tree

apps/mobile/src/app/(tabs)/inbox.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { buildInboxViewedProperties } from "@posthog/core/inbox/engagement";
12
import { INBOX_PIPELINE_STATUSES } from "@posthog/core/inbox/reportFiltering";
23
import type { SignalReport } from "@posthog/shared/domain-types";
34
import { useFocusEffect, useRouter } from "expo-router";
@@ -24,7 +25,6 @@ import {
2425
} from "@/features/inbox/stores/dismissedReportsStore";
2526
import { useInboxFilterStore } from "@/features/inbox/stores/inboxFilterStore";
2627
import { useInboxStore } from "@/features/inbox/stores/inboxStore";
27-
import { buildInboxViewedProperties } from "@/features/inbox/utils";
2828
import { useIntegrations } from "@/features/tasks/hooks/useIntegrations";
2929
import { ANALYTICS_EVENTS, useAnalytics } from "@/lib/analytics";
3030

@@ -67,12 +67,16 @@ export default function InboxScreen() {
6767
viewedFiredForFocusRef.current = focusVersion;
6868
analytics.track(
6969
ANALYTICS_EVENTS.INBOX_VIEWED,
70-
buildInboxViewedProperties(reports, totalCount, {
71-
sourceProductFilter,
72-
statusFilter,
73-
suggestedReviewerFilter,
74-
priorityFilter,
75-
defaultStatusFilter: INBOX_PIPELINE_STATUSES,
70+
buildInboxViewedProperties({
71+
visibleReports: reports,
72+
totalCount,
73+
filters: {
74+
sourceProductFilter,
75+
statusFilter,
76+
suggestedReviewerFilter,
77+
priorityFilter,
78+
defaultStatusFilter: INBOX_PIPELINE_STATUSES,
79+
},
7680
}),
7781
);
7882
}, [

apps/mobile/src/app/automation/[id].tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Text } from "@components/text";
22
import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client";
3+
import { parseSkillTemplateId } from "@posthog/core/automations/automationTemplatePresentation";
34
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
45
import { useState } from "react";
56
import {
@@ -20,7 +21,6 @@ import {
2021
useUpdateTaskAutomation,
2122
} from "@/features/tasks/hooks/useAutomations";
2223
import { useTask } from "@/features/tasks/hooks/useTasks";
23-
import { parseSkillTemplateId } from "@/features/tasks/skills/skillTemplateIds";
2424
import { useThemeColors } from "@/lib/theme";
2525

2626
export default function AutomationDetailScreen() {

apps/mobile/src/app/automation/create.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client";
2+
import { formatSkillTemplateId } from "@posthog/core/automations/automationTemplatePresentation";
23
import { getCalendars } from "expo-localization";
34
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
45
import { useMemo, useRef, useState } from "react";
@@ -14,7 +15,6 @@ import { Text } from "@/components/text";
1415
import { AutomationForm } from "@/features/tasks/components/AutomationForm";
1516
import { useCreateTaskAutomation } from "@/features/tasks/hooks/useAutomations";
1617
import { useSkillStoreSkill } from "@/features/tasks/skills/hooks";
17-
import { formatSkillTemplateId } from "@/features/tasks/skills/skillTemplateIds";
1818
import { useScreenInsets } from "@/hooks/useScreenInsets";
1919
import { useThemeColors } from "@/lib/theme";
2020

apps/mobile/src/features/inbox/components/ArchivedReportList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Text } from "@components/text";
2+
import { isRestorableReport } from "@posthog/core/inbox/reportMembership";
23
import { inboxStatusLabel } from "@posthog/core/inbox/reportPresentation";
34
import { dismissalReasonLabel } from "@posthog/shared";
45
import type { SignalReport } from "@posthog/shared/domain-types";
@@ -14,7 +15,7 @@ import {
1415
} from "react-native";
1516
import { useThemeColors } from "@/lib/theme";
1617
import { useArchivedReports, useRestoreReport } from "../hooks/useInboxReports";
17-
import { formatReportTimestamp, isRestorableReport } from "../utils";
18+
import { formatReportTimestamp } from "../utils";
1819

1920
interface ArchivedReportListProps {
2021
onReportPress?: (report: SignalReport) => void;

apps/mobile/src/features/inbox/hooks/useInboxReports.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
INBOX_DISMISSED_STATUS_FILTER,
88
INBOX_REFETCH_INTERVAL_MS,
99
} from "@posthog/core/inbox/reportFiltering";
10+
import { isRestorableReport } from "@posthog/core/inbox/reportMembership";
1011
import type { DismissalReasonOptionValue } from "@posthog/shared";
1112
import type {
1213
AvailableSuggestedReviewersResponse,
@@ -31,7 +32,6 @@ import { useMemo } from "react";
3132
import { useAuthStore } from "@/features/auth";
3233
import { getPostHogApiClient } from "@/lib/posthogApiClient";
3334
import { useInboxFilterStore } from "../stores/inboxFilterStore";
34-
import { isRestorableReport } from "../utils";
3535

3636
export const inboxKeys = {
3737
all: ["inbox", "signal-reports"] as const,

apps/mobile/src/features/inbox/utils.test.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import { buildInboxViewedProperties } from "@posthog/core/inbox/engagement";
12
import {
23
buildArchiveListOrdering,
34
buildPriorityFilterParam,
45
buildSignalReportListOrdering,
56
INBOX_PIPELINE_STATUSES,
67
} from "@posthog/core/inbox/reportFiltering";
8+
import { isRestorableReport } from "@posthog/core/inbox/reportMembership";
79
import { formatSignalReportSummaryMarkdown } from "@posthog/core/inbox/reportPresentation";
810
import { dismissalReasonLabel } from "@posthog/shared";
911
import type {
@@ -12,7 +14,24 @@ import type {
1214
SignalReportStatus,
1315
} from "@posthog/shared/domain-types";
1416
import { describe, expect, it } from "vitest";
15-
import { buildInboxViewedProperties, isRestorableReport } from "./utils";
17+
18+
function buildMobileInboxViewedProperties(
19+
reports: SignalReport[],
20+
totalCount: number,
21+
filters: {
22+
sourceProductFilter: string[];
23+
statusFilter: readonly SignalReportStatus[];
24+
suggestedReviewerFilter: string[];
25+
priorityFilter: string[];
26+
defaultStatusFilter: readonly SignalReportStatus[];
27+
},
28+
) {
29+
return buildInboxViewedProperties({
30+
visibleReports: reports,
31+
totalCount,
32+
filters,
33+
});
34+
}
1635

1736
function makeReport(
1837
partial: Partial<SignalReport> & Pick<SignalReport, "id">,
@@ -70,7 +89,7 @@ describe("formatSignalReportSummaryMarkdown", () => {
7089

7190
describe("buildInboxViewedProperties", () => {
7291
it("emits zero counts for an empty list", () => {
73-
const props = buildInboxViewedProperties([], 0, {
92+
const props = buildMobileInboxViewedProperties([], 0, {
7493
sourceProductFilter: [],
7594
statusFilter: INBOX_PIPELINE_STATUSES,
7695
suggestedReviewerFilter: [],
@@ -119,7 +138,7 @@ describe("buildInboxViewedProperties", () => {
119138
makeReport({ id: "4", status: "failed" }),
120139
];
121140

122-
const props = buildInboxViewedProperties(reports, 4, {
141+
const props = buildMobileInboxViewedProperties(reports, 4, {
123142
sourceProductFilter: [],
124143
statusFilter: INBOX_PIPELINE_STATUSES,
125144
suggestedReviewerFilter: [],
@@ -140,7 +159,7 @@ describe("buildInboxViewedProperties", () => {
140159
});
141160

142161
it("marks filters active when any of status/source/reviewer/priority differs from defaults", () => {
143-
const narrowed = buildInboxViewedProperties([], 0, {
162+
const narrowed = buildMobileInboxViewedProperties([], 0, {
144163
sourceProductFilter: [],
145164
statusFilter: ["ready"],
146165
suggestedReviewerFilter: [],
@@ -150,7 +169,7 @@ describe("buildInboxViewedProperties", () => {
150169
expect(narrowed.has_active_filters).toBe(true);
151170
expect(narrowed.status_filter_count).toBe(1);
152171

153-
const sourced = buildInboxViewedProperties([], 0, {
172+
const sourced = buildMobileInboxViewedProperties([], 0, {
154173
sourceProductFilter: ["error_tracking"],
155174
statusFilter: INBOX_PIPELINE_STATUSES,
156175
suggestedReviewerFilter: [],
@@ -160,7 +179,7 @@ describe("buildInboxViewedProperties", () => {
160179
expect(sourced.has_active_filters).toBe(true);
161180
expect(sourced.source_product_filter).toEqual(["error_tracking"]);
162181

163-
const reviewer = buildInboxViewedProperties([], 0, {
182+
const reviewer = buildMobileInboxViewedProperties([], 0, {
164183
sourceProductFilter: [],
165184
statusFilter: INBOX_PIPELINE_STATUSES,
166185
suggestedReviewerFilter: ["uuid-1"],
@@ -169,7 +188,7 @@ describe("buildInboxViewedProperties", () => {
169188
});
170189
expect(reviewer.has_active_filters).toBe(true);
171190

172-
const prioritized = buildInboxViewedProperties([], 0, {
191+
const prioritized = buildMobileInboxViewedProperties([], 0, {
173192
sourceProductFilter: [],
174193
statusFilter: INBOX_PIPELINE_STATUSES,
175194
suggestedReviewerFilter: [],
@@ -180,7 +199,7 @@ describe("buildInboxViewedProperties", () => {
180199
});
181200

182201
it("treats a reordered default status set as not filtered", () => {
183-
const props = buildInboxViewedProperties([], 0, {
202+
const props = buildMobileInboxViewedProperties([], 0, {
184203
sourceProductFilter: [],
185204
statusFilter: [...INBOX_PIPELINE_STATUSES].reverse(),
186205
suggestedReviewerFilter: [],
Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,8 @@
1-
import type {
2-
SignalReport,
3-
SignalReportPriority,
4-
SignalReportStatus,
5-
} from "@posthog/shared/domain-types";
61
import { differenceInHours, format, formatDistanceToNow } from "date-fns";
7-
import type { InboxViewedProperties } from "@/lib/analytics";
82

93
/** Relative time for the last day, absolute "MMM d" beyond it. */
104
export function formatReportTimestamp(date: Date): string {
115
return differenceInHours(new Date(), date) < 24
126
? formatDistanceToNow(date, { addSuffix: true })
137
: format(date, "MMM d");
148
}
15-
16-
/**
17-
* Archive membership: `suppressed` (user-archived) and `resolved` (PR merged).
18-
* Only `suppressed` is restorable; `resolved` is terminal, shown for reference.
19-
*/
20-
export function isRestorableReport(
21-
report: Pick<SignalReport, "status">,
22-
): boolean {
23-
return report.status === "suppressed";
24-
}
25-
26-
/**
27-
* Returns only reports that are actionable for the tinder-like card deck:
28-
* ready, immediately actionable, not already addressed.
29-
*/
30-
export function getActionableReports(reports: SignalReport[]): SignalReport[] {
31-
return reports.filter(
32-
(r) =>
33-
r.status === "ready" &&
34-
r.actionability === "immediately_actionable" &&
35-
!r.already_addressed,
36-
);
37-
}
38-
39-
interface InboxViewedFilterState {
40-
sourceProductFilter: string[];
41-
statusFilter: readonly SignalReportStatus[];
42-
suggestedReviewerFilter: string[];
43-
priorityFilter: SignalReportPriority[];
44-
/** Default status filter as defined in the filter store, used to detect whether the user has narrowed it. */
45-
defaultStatusFilter: readonly SignalReportStatus[];
46-
}
47-
48-
/**
49-
* Build the property payload for the `Inbox viewed` analytics event.
50-
*
51-
* Mirrors packages/ui/src/features/inbox/components/InboxSignalsTab.tsx so
52-
* desktop and mobile send the same shape into PostHog.
53-
*/
54-
export function buildInboxViewedProperties(
55-
reports: SignalReport[],
56-
totalCount: number,
57-
filters: InboxViewedFilterState,
58-
): InboxViewedProperties {
59-
const priorityCounts = {
60-
P0: 0,
61-
P1: 0,
62-
P2: 0,
63-
P3: 0,
64-
P4: 0,
65-
unknown: 0,
66-
};
67-
const actionabilityCounts = {
68-
immediately_actionable: 0,
69-
requires_human_input: 0,
70-
not_actionable: 0,
71-
unknown: 0,
72-
};
73-
let readyCount = 0;
74-
for (const r of reports) {
75-
if (r.status === "ready") readyCount += 1;
76-
const p = r.priority;
77-
if (p === "P0" || p === "P1" || p === "P2" || p === "P3" || p === "P4") {
78-
priorityCounts[p] += 1;
79-
} else {
80-
priorityCounts.unknown += 1;
81-
}
82-
const a = r.actionability;
83-
if (
84-
a === "immediately_actionable" ||
85-
a === "requires_human_input" ||
86-
a === "not_actionable"
87-
) {
88-
actionabilityCounts[a] += 1;
89-
} else {
90-
actionabilityCounts.unknown += 1;
91-
}
92-
}
93-
94-
const statusFiltered =
95-
filters.statusFilter.length !== filters.defaultStatusFilter.length ||
96-
filters.statusFilter.some((s) => !filters.defaultStatusFilter.includes(s));
97-
const hasActiveFilters =
98-
statusFiltered ||
99-
filters.sourceProductFilter.length > 0 ||
100-
filters.suggestedReviewerFilter.length > 0 ||
101-
filters.priorityFilter.length > 0;
102-
103-
return {
104-
report_count: reports.length,
105-
total_count: totalCount,
106-
ready_count: readyCount,
107-
has_active_filters: hasActiveFilters,
108-
source_product_filter: filters.sourceProductFilter,
109-
status_filter_count: filters.statusFilter.length,
110-
is_empty: totalCount === 0,
111-
priority_p0_count: priorityCounts.P0,
112-
priority_p1_count: priorityCounts.P1,
113-
priority_p2_count: priorityCounts.P2,
114-
priority_p3_count: priorityCounts.P3,
115-
priority_p4_count: priorityCounts.P4,
116-
priority_unknown_count: priorityCounts.unknown,
117-
actionability_immediately_actionable_count:
118-
actionabilityCounts.immediately_actionable,
119-
actionability_requires_human_input_count:
120-
actionabilityCounts.requires_human_input,
121-
actionability_not_actionable_count: actionabilityCounts.not_actionable,
122-
actionability_unknown_count: actionabilityCounts.unknown,
123-
};
124-
}

apps/mobile/src/features/tasks/components/AutomationDetail.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Text } from "@components/text";
22
import type { TaskAutomation } from "@posthog/api-client/posthog-client";
33
import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule";
4+
import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation";
45
import type { TaskRun } from "@posthog/shared";
56
import { ActivityIndicator, Pressable, View } from "react-native";
6-
import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation";
77
import { AutomationStatusBadge } from "./AutomationStatusBadge";
88

99
interface AutomationDetailProps {

apps/mobile/src/features/tasks/components/AutomationItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Text } from "@components/text";
22
import type { TaskAutomation } from "@posthog/api-client/posthog-client";
33
import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule";
4+
import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation";
45
import type { TaskRun } from "@posthog/shared";
56
import { format, formatDistanceToNow } from "date-fns";
67
import { memo } from "react";
78
import { Pressable, View } from "react-native";
8-
import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation";
99
import { AutomationStatusBadge } from "./AutomationStatusBadge";
1010

1111
interface AutomationItemProps {

apps/mobile/src/features/tasks/skills/skillTemplateIds.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)