SELF-253 feat: add user email feedback (#889)

* feat: add sentry feedback

* add sentry feedback to web

* feat: add custom feedback modal & fix freeze on IOS

* yarn nice

* update lock

* feat: show feedback widget on NFC scan issues (#948)

* feat: show feedback widget on NFC scan issues

* fix ref

* clean up

* fix report issue screen

* abstract send user feedback email logic

* fixes

* change text to Report Issue

* sanitize email and track event messge

* remove unnecessary sanitization

* add sanitize error message tests

* fix tests

* save wip. almost done

* fix screen test

* fix screen test

* remove non working test

---------

Co-authored-by: Justin Hernandez <transphorm@gmail.com>
Co-authored-by: Justin Hernandez <justin.hernandez@self.xyz>
This commit is contained in:
Seshanth.S🐺
2025-08-25 22:04:18 +05:30
committed by GitHub
parent d8bf5f9db7
commit 610f195136
15 changed files with 932 additions and 21 deletions

55
app/src/utils/email.ts Normal file
View File

@@ -0,0 +1,55 @@
// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11
import { Linking, Platform } from 'react-native';
import { getCountry, getLocales, getTimeZone } from 'react-native-localize';
import { sanitizeErrorMessage } from '@/utils/utils';
import { version } from '../../package.json';
interface SendFeedbackEmailOptions {
message: string;
origin: string;
subject?: string;
recipient?: string;
}
/**
* Sends a feedback email with device information and user message
* @param options Configuration for the feedback email
*/
export const sendFeedbackEmail = async ({
message,
origin,
subject = 'SELF App Feedback',
recipient = 'team@self.xyz',
}: SendFeedbackEmailOptions): Promise<void> => {
const deviceInfo = [
['device', `${Platform.OS}@${Platform.Version}`],
['app', `v${version}`],
[
'locales',
getLocales()
.map(locale => `${locale.languageCode}-${locale.countryCode}`)
.join(','),
],
['country', getCountry()],
['tz', getTimeZone()],
['ts', new Date().toISOString()],
['origin', origin],
['error', sanitizeErrorMessage(message)],
] as [string, string][];
const body = `Please describe the issue you're experiencing:
---
Technical Details (do not modify):
${deviceInfo.map(([k, v]) => `${k}=${v}`).join('\n')}
---`;
await Linking.openURL(
`mailto:${recipient}?subject=${encodeURIComponent(
subject,
)}&body=${encodeURIComponent(body)}`,
);
};

View File

@@ -16,3 +16,14 @@ export function checkScannedInfo(
}
return true;
}
// Redacts 9+ consecutive digits and MRZ-like blocks to reduce PII exposure
export const sanitizeErrorMessage = (msg: string): string => {
try {
return msg
.replace(/\b\d{9,}\b/g, '[REDACTED]')
.replace(/[A-Z0-9<]{30,}/g, '[MRZ_REDACTED]');
} catch {
return 'redacted';
}
};