fix(usage): minimize session receipts and clarify privacy controls

This commit is contained in:
Paul Nothaft
2026-09-05 23:44:15 +02:00
parent cc263f2e87
commit e347f8f40f
12 changed files with 192 additions and 17 deletions
@@ -67,6 +67,31 @@ beforeEach(() => {
});
afterEach(cleanup);
describe('product usage controls', () => {
it('offers identity-free audit receipts after opt-out without restoring participation controls', async () => {
vi.mocked(service.status).mockResolvedValue({
...status,
privacy_receipts: {
last_deletion: {
receipt_version: 'local-audit.v1',
kind: 'deletion',
status: 'collector-confirmed'
}
}
});
mount();
expect(
await screen.findByRole('button', { name: 'productUsage.auditDownload' })
).toBeEnabled();
expect(
screen.getByText('productUsage.auditDescription')
).toBeInTheDocument();
expect(
screen.queryByLabelText('productUsage.hash')
).not.toBeInTheDocument();
expect(
screen.queryByText('productUsage.feedbackTitle')
).not.toBeInTheDocument();
});
it('requires the disclosure and an unchecked-by-default consent before enabling', async () => {
mount();
fireEvent.click(await screen.findByText('productUsage.review'));
@@ -191,13 +191,13 @@ export default function ProductUsageTab() {
await queryClient.invalidateQueries({ queryKey: ['productUsage'] });
}
};
const download = (value: unknown) => {
const download = (value: unknown, filename = 'picpeak-usage-packets.json') => {
const url = URL.createObjectURL(
new Blob([JSON.stringify(value, null, 2)], { type: 'application/json' })
);
const anchor = document.createElement('a');
anchor.href = url;
anchor.download = 'picpeak-usage-packets.json';
anchor.download = filename;
anchor.click();
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
};
@@ -300,6 +300,26 @@ export default function ProductUsageTab() {
)}
</div>
</Card>
{data.privacy_receipts &&
Object.keys(data.privacy_receipts).length > 0 && (
<Card padding="md" className="space-y-4">
<h3 className="text-lg font-semibold">
{t('productUsage.auditTitle')}
</h3>
<p>{t('productUsage.auditDescription')}</p>
<Button
variant="outline"
onClick={() =>
download(
data.privacy_receipts,
'picpeak-usage-privacy-receipts.json'
)
}
>
{t('productUsage.auditDownload')}
</Button>
</Card>
)}
{active && (
<>
<Card padding="md" className="space-y-4">