Compare commits

...

1 Commits

Author SHA1 Message Date
ncoelho
ea265b7462 feat: Replace screenshot blocker with warning 2026-05-12 14:38:56 +02:00
30 changed files with 95 additions and 244 deletions

View File

@ -79,8 +79,6 @@ interface SettingsContextType {
setLanguageStorage: (language: string) => Promise<void>;
isHandOffUseEnabled: boolean;
setIsHandOffUseEnabledAsyncStorage: (value: boolean) => Promise<void>;
isPrivacyBlurEnabled: boolean;
setIsPrivacyBlurEnabled: (value: boolean) => void;
isDoNotTrackEnabled: boolean;
setDoNotTrackStorage: (value: boolean) => Promise<void>;
isWidgetBalanceDisplayAllowed: boolean;
@ -108,8 +106,6 @@ const defaultSettingsContext: SettingsContextType = {
setLanguageStorage: async () => {},
isHandOffUseEnabled: false,
setIsHandOffUseEnabledAsyncStorage: async () => {},
isPrivacyBlurEnabled: true,
setIsPrivacyBlurEnabled: () => {},
isDoNotTrackEnabled: false,
setDoNotTrackStorage: async () => {},
isWidgetBalanceDisplayAllowed: true,
@ -136,7 +132,6 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = React.m
const [preferredFiatCurrency, setPreferredFiatCurrencyState] = useState<TFiatUnit>(FiatUnit.USD);
const [language, setLanguage] = useState<string>('en');
const [isHandOffUseEnabled, setIsHandOffUseEnabledState] = useState<boolean>(false);
const [isPrivacyBlurEnabled, setIsPrivacyBlurEnabled] = useState<boolean>(true);
const [isDoNotTrackEnabled, setIsDoNotTrackEnabled] = useState<boolean>(false);
const [isWidgetBalanceDisplayAllowed, setIsWidgetBalanceDisplayAllowed] = useState<boolean>(true);
const [isLegacyURv1Enabled, setIsLegacyURv1Enabled] = useState<boolean>(false);
@ -344,8 +339,6 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = React.m
setLanguageStorage,
isHandOffUseEnabled,
setIsHandOffUseEnabledAsyncStorage,
isPrivacyBlurEnabled,
setIsPrivacyBlurEnabled,
isDoNotTrackEnabled,
setDoNotTrackStorage,
isWidgetBalanceDisplayAllowed,
@ -372,8 +365,6 @@ export const SettingsProvider: React.FC<{ children: React.ReactNode }> = React.m
setLanguageStorage,
isHandOffUseEnabled,
setIsHandOffUseEnabledAsyncStorage,
isPrivacyBlurEnabled,
setIsPrivacyBlurEnabled,
isDoNotTrackEnabled,
setDoNotTrackStorage,
isWidgetBalanceDisplayAllowed,

View File

@ -1,26 +0,0 @@
import { CaptureProtection } from 'react-native-capture-protection';
import { isDesktop } from '../blue_modules/environment';
import { useCallback } from 'react';
export const useScreenProtect = () => {
const enableScreenProtect = useCallback(async () => {
if (isDesktop) return;
await CaptureProtection.prevent();
}, []);
const disableScreenProtect = useCallback(async () => {
if (isDesktop) return;
await CaptureProtection.allow();
}, []);
const isScreenBeingRecorded = useCallback(async () => {
if (isDesktop) return false;
return await CaptureProtection.isScreenRecording();
}, []);
return {
enableScreenProtect,
disableScreenProtect,
isScreenBeingRecorded,
};
};

View File

@ -0,0 +1,62 @@
import { useEffect } from 'react';
import { Platform } from 'react-native';
import { CaptureEventType, CaptureProtection } from 'react-native-capture-protection';
import presentAlert from '../components/Alert';
import { isDesktop } from '../blue_modules/environment';
import loc from '../loc';
export const useScreenshotWarning = (enabled: boolean = true) => {
useEffect(() => {
if (!enabled) return;
if (isDesktop) return;
if (Platform.OS !== 'ios' && Platform.OS !== 'android') return;
let listener: ReturnType<typeof CaptureProtection.addListener> | undefined;
let cancelled = false;
const setup = async () => {
// On iOS, hide the screen from the app switcher preview so the seed does
// not appear in the recent-apps card. Screenshots and screen recording
// remain allowed - the listener below shows a warning on capture.
if (Platform.OS === 'ios') {
try {
await CaptureProtection.prevent({ appSwitcher: true });
} catch (e) {
console.debug('useScreenshotWarning: prevent(appSwitcher) failed', e);
}
}
if (Platform.OS === 'android') {
try {
await CaptureProtection.requestPermission();
} catch (e) {
console.debug('useScreenshotWarning: requestPermission failed', e);
}
}
// bail out if the effect was cleaned up while awaiting permission
if (cancelled) return;
listener = CaptureProtection.addListener(eventType => {
if (eventType === CaptureEventType.CAPTURED) {
presentAlert({
title: loc.wallets.seed_screenshot_title,
message: loc.wallets.seed_screenshot_warning,
});
}
});
};
setup();
return () => {
cancelled = true;
listener?.remove?.();
if (Platform.OS === 'ios') {
CaptureProtection.allow({ appSwitcher: true }).catch(e => {
console.debug('useScreenshotWarning: allow(appSwitcher) cleanup failed', e);
});
}
};
}, [enabled]);
};

View File

@ -205,7 +205,6 @@
"about_selftest_ok": "پوی واجۊریا منی ب خۊوی ٱنجوم وابین. کیف پیل ب خۊوی کار اکونه.",
"about_sm_github": "گیت هاب",
"about_sm_telegram": "تورگه تلگرام",
"privacy_temporary_screenshots": "هشتن زفت کردن بلگه نمایش",
"biometrics": "بیومتریک",
"biom_conf_identity": "هۊویته خوته تاییڌ کو.",
"currency": "واهڌ پیل",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Všechny interní testy úspěšně prošly. Peněženka funguje správně.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram kanál",
"privacy_temporary_screenshots": "Umožnit zachycení snímku obrazovky",
"privacy_temporary_screenshots_instructions": "Ochrana proti zachycení snímku obrazovky bude dočasně vypnuta, aby bylo možné pořizovat snímky obrazovky a nahrávání obrazovky. Ochrana se znovu automaticky aktivuje, když zavřete a znovu otevřete aplikaci BlueWallet.",
"biometrics": "Biometrie",
"biometrics_no_longer_available": "Nastavení vašeho zařízení se změnily a již neodpovídají zvoleným nastavením zabezpečení v aplikaci. Aktivujte prosím znovu biometrii nebo přístupový kód, a poté aplikaci restartujte, aby se tyto změny projevily.",
"biom_10times": "Pokusili jste se zadat heslo 10×. Chcete obnovit úložiště? Tím odstraníte všechny peněženky a dešifrujete úložiště.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Alle internen Tests verliefen erfolgreich. Das Wallet funktioniert gut.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram-Channel",
"privacy_temporary_screenshots": "Bildschirmaufnahme erlauben",
"privacy_temporary_screenshots_instructions": "Der Schutz vor Bildschirmaufnahmen wird vorübergehend deaktiviert, wodurch Screenshots und Bildschirmaufzeichnungen möglich sind. Der Schutz wird automatisch reaktiviert, wenn Sie BlueWallet schließen und erneut öffnen.",
"biometrics": "Biometrie",
"biometrics_no_longer_available": "Die Geräteeinstellungen stimmen nicht mehr mit den App Sicherheitseinstellungen überein. Um die Änderungen zu übernehmen, die Biometrie oder den Passcode einrichten, dann die App neu starten.",
"biom_10times": "Es wurde 10 Mal versucht, das Passwort einzugeben. Soll der Speicher zurückgesetzt werden? Dabei werden alle Wallets entfernt der Speicher entschlüsselt.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "All internal tests have passed successfully. The wallet works well.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram channel",
"privacy_temporary_screenshots": "Allow Screen Capture",
"privacy_temporary_screenshots_instructions": "Screen capture protection will be temporarily turned off, enabling screenshots and screen recordings. The protection will automatically reactivate when you close and reopen BlueWallet.",
"biometrics": "Biometrics",
"biometrics_no_longer_available": "Your device settings have changed and no longer match the selected security settings in the app. Please re-enable biometrics or passcode, then restart the app to apply these changes.",
"biom_10times": "You have attempted to enter your password 10 times. Would you like to reset your storage? This will remove all wallets and decrypt your storage.",
@ -504,6 +502,8 @@
"select_wallet": "Select Wallet",
"pull_to_refresh": "Pull to Refresh",
"warning_do_not_disclose": "Never share the information below",
"seed_screenshot_title": "Screenshot Detected",
"seed_screenshot_warning": "You are taking a screenshot of your seed. This can compromise your seed as malicious apps can access your photos. We recommend you always use physical items, such as paper, signing devices or hardware wallets to backup your seed phrase.",
"scan_import": "Scan this QR code to import your wallet in another application.",
"write_down_header": "Create a manual backup",
"write_down": "Write down and securely store these words. Use them to restore your wallet at a later time.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Todas las pruebas internas han pasado satisfactoriamente. La billetera funciona bien.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Chat de Telegram ",
"privacy_temporary_screenshots": "Permitir captura de pantalla",
"privacy_temporary_screenshots_instructions": "La protección contra capturas de pantalla se desactivará temporalmente, lo que permitirá realizar capturas y grabaciones de pantalla. La protección se reactivará automáticamente cuando cierre y vuelva a abrir BlueWallet.",
"biometrics": "Biometría",
"biometrics_no_longer_available": "La configuración de tu dispositivo cambió y ya no coincide con la configuración de seguridad seleccionada en la aplicación. Vuelve a habilitar los datos biométricos o el código de acceso, luego reinicia la aplicación para aplicar estos cambios.",
"biom_10times": "Has intentado ingresar tu contraseña 10 veces. ¿Te gustaría restablecer tu almacenamiento? Esto eliminará todas las billeteras y descifrará tu almacenamiento.",

View File

@ -221,8 +221,6 @@
"about_selftest_ok": "Kaikki sisäiset testit on läpäisty onnistuneesti. Lompakko toimii hyvin. ",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram-kanava",
"privacy_temporary_screenshots": "Salli näytönkaappaus",
"privacy_temporary_screenshots_instructions": "Näytönkaappausesto poistetaan tilapäisesti käytöstä, jolloin näytönkaappaukset ja näytön tallenteet ovat käytössä. Esto aktivoituu automaattisesti uudelleen, kun suljet BlueWalletin ja avaat sen uudelleen.",
"biometrics": "Biometriset tiedot",
"biometrics_no_longer_available": "Laitteesi asetukset ovat muuttuneet, eivätkä ne enää vastaa sovelluksessa valittuja suojausasetuksia. Ota biometria tai salasana uudelleen käyttöön ja käynnistä sovellus uudelleen, jotta muutokset tulevat voimaan.",
"biom_10times": "Olet yrittänyt antaa salasanasi 10 kertaa. Haluatko nollata tallennustilan? Tämä poistaa kaikki lompakot ja purkaa tallennustilan salauksen.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Allar innankerviskanningarnar eydnaðust. Tí koyrur appin álítandi.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram rás",
"privacy_temporary_screenshots": "Loyv skíggjaavmyndan",
"privacy_temporary_screenshots_instructions": "Skíggjaavmyndan verður fyribils loyvd, soleiðis at skíggjamyndir og upptøkur kunnu takast. Verndin verður sjálvvirkandi virkja aftur, undir næstu koyring av BlueWallet.",
"biometrics": "Lívmátilig atgongd",
"biometrics_no_longer_available": "Kervisstillingarnar á tíni eind eru broyttar og samsvara ikki longur við trygdarstillingarnar í appini. Vanliga áset lívmátiliga atgongd ella atgonguloynital aftur, og endurbyrja appina fyri at virkja stillingarnar.",
"biom_10times": "Tú hevur roynt títt loyniorð 10 ferðir, uttan at tað eydnaðist. Ynskir tú at tómstilla goymsluna? Tað strikar allar mappurnar og avbronglar goymsluna.",

View File

@ -220,7 +220,6 @@
"about_selftest_ok": "כל הבדיקות הפנימיות עברו בהצלחה. הארנק פועל כיאות.",
"about_sm_github": "GitHub",
"about_sm_telegram": "צ'אט טלגרם",
"privacy_temporary_screenshots": "אפשר צילומי מסך",
"biometrics": "זיהוי ביומטרי",
"biometrics_no_longer_available": "הגדרות מכשירכם השתנו ולא מתאימים יותר להגדרות אבטחה הנבחרות ביישומון. אנא אפשרו מחדש זיהוי ביומטרי או סיסמה, ולאחר מכן הפעילו מחדש את היישומון כדי להחיל את השינויים.",
"biom_10times": "ניסיתם להכניס את הסיסמה שלכם 10 פעמים. האם תרצו לאפס את האחסון שלכם? פעולה זאת תמחק את כל הארנקים ותפענח את האחסון שלכם.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "全ての内部テストが成功しました。ウォレットは正しく機能しています。",
"about_sm_github": "GitHub",
"about_sm_telegram": "テレグラムチャット",
"privacy_temporary_screenshots": "スクリーンキャプチャを許可",
"privacy_temporary_screenshots_instructions": "スクリーンキャプチャ保護が一時的にオフになり、スクリーンショットと録画が可能です。BlueWalletを閉じて再度開くと保護は自動的に有効になります。",
"biometrics": "生体認証",
"biometrics_no_longer_available": "デバイスの設定が変更され、アプリ内で選択したセキュリティ設定に適合しなくなりました。生体認証かパスコードを再度有効にしてから、アプリを再起動して変更を適用してください。",
"biom_10times": "パスワードを10回入力しようとしました。ストレージをリセットしますかこれにより全てのウォレットが削除され、ストレージが復号化されます。",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "모든 내부 테스트를 성공적으로 마쳤습니다. 월렛은 정상적으로 기능합니다.",
"about_sm_github": "GitHub",
"about_sm_telegram": "텔레그램 채널",
"privacy_temporary_screenshots": "화면 캡처 허용",
"privacy_temporary_screenshots_instructions": "화면 캡처 보호 기능이 일시적으로 비활성화되어, 스크린샷 및 화면 녹화가 가능해집니다. BlueWallet을 닫았다가 다시 열면 보호 기능이 자동으로 다시 활성화됩니다.",
"biometrics": "바이오메트릭",
"biometrics_no_longer_available": "기기의 설정이 변경되어 앱에서 선택한 보안 설정과 일치하지 않습니다. 지문 또는 암호 잠금을 다시 활성화한 후, 앱을 재시작하여 변경 사항을 적용해 주세요.",
"biom_10times": "비밀번호를 10번 실패했습니다. 스토리지를 초기화시키겠습니까? 이는 모든 월렛을 지우고 스토리지를 암호화합니다. ",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Wszystkie testy wewnętrzne przebiegły pomyślnie. Portfel działa dobrze.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Chat na Telegramie",
"privacy_temporary_screenshots": "Zezwól na przechwytywanie ekranu",
"privacy_temporary_screenshots_instructions": "Ochrona przed przechwytywaniem ekranu zostanie tymczasowo wyłączona, umożliwiając wykonywanie zrzutów ekranu i nagrań ekranu. Ochrona automatycznie włączy się ponownie po zamknięciu i ponownym uruchomieniu BlueWallet.",
"biometrics": "Biometria",
"biometrics_no_longer_available": "Ustawienia twojego urządzenia zostały zmienione i nie są już zgodne z wybranymi ustawieniami bezpieczeństwa w aplikacji. Proszę ponownie włączyć biometrię lub kod dostępu, a następnie uruchomić ponownie aplikację, aby zastosować te zmiany.",
"biom_10times": "Próbowałeś podać hasło 10 razy. Czy chcesz zresetować magazyn danych? To usunie wszystkie portfele i odszyfruje dane.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Todos os testes internos foram aprovados com sucesso. A carteira funciona bem.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Canal Telegram",
"privacy_temporary_screenshots": "Permitir captura de ecrã",
"privacy_temporary_screenshots_instructions": "A proteção contra captura de ecrã será temporariamente desativada, permitindo capturas e gravaç de ecrã e gravações de ecrã. A proteção será reativada automaticamente quando fechar e reabrir o BlueWallet.",
"biometrics": "Biometria",
"biometrics_no_longer_available": "As configurações do seu dispositivo foram alteradas e já não correspondem às configurações de segurança selecionadas na aplicação. Reative a biometria ou a senha e reinicie a aplicação para aplicar essas alterações.",
"biom_10times": "Tentou introduzir a sua palavra-passe 10 vezes. Deseja redefinir o seu armazenamento? Isto irá remover todas as carteiras e desencriptar o seu armazenamento.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "Все внутренние тесты прошли успешно. Кошелёк работает отлично.",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram-канал",
"privacy_temporary_screenshots": "Разрешить снимки экрана",
"privacy_temporary_screenshots_instructions": "Защита от снимков экрана будет временно отключена, что позволит делать скриншоты и записи экрана. Защита автоматически включится снова после закрытия и повторного открытия BlueWallet.",
"biometrics": "Биометрия",
"biometrics_no_longer_available": "Настройки устройства изменились и больше не соответствуют выбранным в приложении. Включите биометрию или пароль и перезапустите приложение.",
"biom_10times": "Вы 10 раз ввели неверный пароль. Очистить хранилище? Это удалит все кошельки.",

View File

@ -224,8 +224,6 @@
"about_selftest_ok": "所有内部测试均已通过,钱包运行正常。",
"about_sm_github": "Github",
"about_sm_telegram": "电报频道",
"privacy_temporary_screenshots": "允许屏幕录制",
"privacy_temporary_screenshots_instructions": "屏幕录制保护将被暂时关闭,从而允许截图和屏幕录制。关闭并重新打开 BlueWallet 后,保护功能将自动恢复。",
"biometrics": "生物识别认证",
"biometrics_no_longer_available": "您的设备设置已更改,与应用程序中选择的安全设置不再匹配。请重新启用生物识别认证或密码,然后重启应用以应用这些更改。",
"biom_10times": "您已尝试输入密码 10 次。是否要重置存储?此操作将移除所有钱包并解密存储。",

View File

@ -16,8 +16,6 @@ import { DynamicQRCode } from '../../components/DynamicQRCode';
import { useTheme } from '../../components/themes';
import loc from '../../loc';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { useSettings } from '../../hooks/context/useSettings';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import { BlueSpacing20 } from '../../components/BlueSpacing';
import { SendDetailsStackParamList } from '../../navigation/SendDetailsStackParamList';
import { CreateTransactionTarget } from '../../class/wallets/types';
@ -34,7 +32,6 @@ const SendCreate = () => {
} = useRoute<RouteProp<SendDetailsStackParamList, 'CreateTransaction'>>().params;
const transaction = bitcoin.Transaction.fromHex(tx);
const size = transaction.virtualSize();
const { isPrivacyBlurEnabled } = useSettings();
const { colors } = useTheme();
const { setOptions } = useNavigation();
@ -56,18 +53,6 @@ const SendCreate = () => {
},
});
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
useEffect(() => {
console.log('send/create - useEffect');
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
return () => {
disableScreenProtect();
};
}, [isPrivacyBlurEnabled, enableScreenProtect, disableScreenProtect]);
const exportTXN = useCallback(async () => {
const fileName = `${Date.now()}.txn`;
if (Platform.OS === 'ios') {

View File

@ -5,7 +5,6 @@ import A from '../../blue_modules/analytics';
import loc from '../../loc';
import { useStorage } from '../../hooks/context/useStorage';
import { useSettings } from '../../hooks/context/useSettings';
import { isDesktop } from '../../blue_modules/environment';
import {
SettingsFlatList,
SettingsListItem,
@ -20,7 +19,6 @@ enum SettingsPrivacySection {
ReadClipboard,
QuickActions,
Widget,
TemporaryScreenshots,
TotalBalance,
}
@ -36,8 +34,6 @@ const GeneralSettings: React.FC = () => {
const {
isDoNotTrackEnabled,
setDoNotTrackStorage,
isPrivacyBlurEnabled,
setIsPrivacyBlurEnabled,
isWidgetBalanceDisplayAllowed,
setIsWidgetBalanceDisplayAllowedStorage,
isClipboardGetContentEnabled,
@ -116,15 +112,6 @@ const GeneralSettings: React.FC = () => {
[setIsTotalBalanceEnabledStorage],
);
const onTemporaryScreenshotsValueChange = useCallback(
(value: boolean) => {
setIsLoading(SettingsPrivacySection.TemporaryScreenshots);
setIsPrivacyBlurEnabled(!value);
setIsLoading(SettingsPrivacySection.None);
},
[setIsPrivacyBlurEnabled],
);
const openApplicationSettings = useCallback(() => {
openSettings();
}, []);
@ -191,21 +178,6 @@ const GeneralSettings: React.FC = () => {
},
];
if (!isDesktop) {
items.push({
id: 'temporaryScreenshots',
title: loc.settings.privacy_temporary_screenshots,
subtitle: <SettingsSubtitle>{loc.settings.privacy_temporary_screenshots_instructions}</SettingsSubtitle>,
switch: {
value: !isPrivacyBlurEnabled,
onValueChange: onTemporaryScreenshotsValueChange,
disabled: isLoading === SettingsPrivacySection.All,
},
Component: View,
showItem: true,
});
}
items.push({
id: 'doNotTrack',
title: loc.settings.privacy_do_not_track,
@ -283,7 +255,6 @@ const GeneralSettings: React.FC = () => {
isClipboardGetContentEnabled,
isQuickActionsEnabled,
isTotalBalanceEnabled,
isPrivacyBlurEnabled,
isDoNotTrackEnabled,
isWidgetBalanceDisplayAllowed,
isLoading,
@ -292,7 +263,6 @@ const GeneralSettings: React.FC = () => {
setIsClipboardGetContentEnabledStorage,
onDoNotTrackValueChange,
onQuickActionsValueChange,
onTemporaryScreenshotsValueChange,
onTotalBalanceEnabledValueChange,
onWidgetsTotalBalanceValueChange,
openApplicationSettings,

View File

@ -10,8 +10,6 @@ import { useTheme } from '../../components/themes';
import loc from '../../loc';
import { useStorage } from '../../hooks/context/useStorage';
import { ExportMultisigCoordinationSetupStackRootParamList } from '../../navigation/ExportMultisigCoordinationSetupStack';
import { useSettings } from '../../hooks/context/useSettings';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import SafeArea from '../../components/SafeArea';
import { BlueSpacing20 } from '../../components/BlueSpacing';
import { stringToUint8Array, uint8ArrayToHex } from '../../blue_modules/uint8array-extras';
@ -76,11 +74,9 @@ const ExportMultisigCoordinationSetup: React.FC = () => {
const { params } = useRoute<RouteProp<ExportMultisigCoordinationSetupStackRootParamList, 'ExportMultisigCoordinationSetup'>>();
const walletID = params.walletID;
const { wallets } = useStorage();
const { isPrivacyBlurEnabled } = useSettings();
const wallet: TWallet | undefined = wallets.find(w => w.getID() === walletID);
const dynamicQRCode = useRef<any>(null);
const { colors } = useTheme();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const navigation = useNavigation();
const stylesHook = StyleSheet.create({
@ -138,17 +134,6 @@ const ExportMultisigCoordinationSetup: React.FC = () => {
}, [walletID]),
);
useFocusEffect(
useCallback(() => {
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
return () => {
disableScreenProtect();
};
}, [isPrivacyBlurEnabled, enableScreenProtect, disableScreenProtect]),
);
useFocusEffect(
useCallback(() => {
if (closeButtonState) {

View File

@ -10,7 +10,6 @@ import {
} from '../../components/DoneAndDismissKeyboardInputAccessory';
import HeaderMenuButton from '../../components/HeaderMenuButton';
import { useTheme } from '../../components/themes';
import { useSettings } from '../../hooks/context/useSettings';
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import { useKeyboard } from '../../hooks/useKeyboard';
import loc from '../../loc';
@ -18,7 +17,6 @@ import { CommonToolTipActions } from '../../typings/CommonToolTipActions';
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { AddressInputScanButton } from '../../components/AddressInputScanButton';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import SafeAreaScrollView from '../../components/SafeAreaScrollView';
import { BlueSpacing20 } from '../../components/BlueSpacing';
@ -37,8 +35,6 @@ const ImportWallet = () => {
const [searchAccountsMenuState, setSearchAccountsMenuState] = useState<boolean>(false);
const [askPassphraseMenuState, setAskPassphraseMenuState] = useState<boolean>(false);
const [clearClipboardMenuState, setClearClipboardMenuState] = useState<boolean>(true);
const { isPrivacyBlurEnabled } = useSettings();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const styles = StyleSheet.create({
root: {
paddingTop: 10,
@ -156,15 +152,6 @@ const ImportWallet = () => {
[toolTipOnPressMenuItem, toolTipActions],
);
useEffect(() => {
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
return () => {
disableScreenProtect();
};
}, [isPrivacyBlurEnabled, enableScreenProtect, disableScreenProtect]);
useEffect(() => {
if (triggerImport) handleImport();
}, [triggerImport, handleImport]);

View File

@ -19,7 +19,6 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { THDWalletForWatchOnly, TWallet } from '../../class/wallets/types';
import { useSettings } from '../../hooks/context/useSettings';
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import { BlueSpacing10, BlueSpacing20, BlueSpacing40 } from '../../components/BlueSpacing';
type RouteProps = RouteProp<AddWalletStackParamList, 'ImportWalletDiscovery'>;
@ -36,8 +35,7 @@ const ImportWalletDiscovery: React.FC = () => {
const { colors } = useTheme();
const route = useRoute<RouteProps>();
const { importText, askPassphrase, searchAccounts } = route.params;
const { isElectrumDisabled, isPrivacyBlurEnabled } = useSettings();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const { isElectrumDisabled } = useSettings();
const task = useRef<TImport | null>(null);
const { addAndSaveWallet } = useStorage();
const [loading, setLoading] = useState<boolean>(true);
@ -141,15 +139,6 @@ const ImportWalletDiscovery: React.FC = () => {
};
}, [askPassphrase, importText, isElectrumDisabled, navigation, saveWallet, searchAccounts]);
useEffect(() => {
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
return () => {
disableScreenProtect();
};
}, [isPrivacyBlurEnabled, enableScreenProtect, disableScreenProtect]);
const handleCustomDerivation = () => {
task.current?.stop();
navigation.navigate('ImportCustomDerivationPath', { importText, password });

View File

@ -1,15 +1,14 @@
import { RouteProp, useFocusEffect, useLocale, useNavigation, useRoute } from '@react-navigation/native';
import { RouteProp, useLocale, useNavigation, useRoute } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import React, { useCallback, useEffect } from 'react';
import { BackHandler, ScrollView, StyleSheet, Text, View } from 'react-native';
import Button from '../../components/Button';
import { useTheme } from '../../components/themes';
import { useSettings } from '../../hooks/context/useSettings';
import { useStorage } from '../../hooks/context/useStorage';
import loc from '../../loc';
import { AddWalletStackParamList } from '../../navigation/AddWalletStack';
import SeedWords from '../../components/SeedWords';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import { useScreenshotWarning } from '../../hooks/useScreenshotWarning';
type RouteProps = RouteProp<AddWalletStackParamList, 'PleaseBackup'>;
type NavigationProp = NativeStackNavigationProp<AddWalletStackParamList, 'PleaseBackup'>;
@ -19,10 +18,10 @@ const PleaseBackup: React.FC = () => {
const { walletID } = useRoute<RouteProps>().params;
const wallet = wallets.find(w => w.getID() === walletID)!;
const navigation = useNavigation<NavigationProp>();
const { isPrivacyBlurEnabled } = useSettings();
const { colors } = useTheme();
const { direction } = useLocale();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
useScreenshotWarning();
const stylesHook = StyleSheet.create({
flex: {
@ -47,15 +46,6 @@ const PleaseBackup: React.FC = () => {
};
}, [handleBackButton]);
useFocusEffect(
useCallback(() => {
if (isPrivacyBlurEnabled) enableScreenProtect();
return () => {
disableScreenProtect();
};
}, [disableScreenProtect, enableScreenProtect, isPrivacyBlurEnabled]),
);
return (
<ScrollView
style={styles.root}

View File

@ -28,7 +28,6 @@ import { useTheme } from '../../components/themes';
import prompt from '../../helpers/prompt';
import { unlockWithBiometrics, useBiometrics } from '../../hooks/useBiometrics';
import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import loc from '../../loc';
import ActionSheet from '../ActionSheet';
import { useStorage } from '../../hooks/context/useStorage';
@ -47,8 +46,7 @@ const ViewEditMultisigCosigners: React.FC = () => {
const { colors } = useTheme();
const { wallets, setWalletsWithNewOrder } = useStorage();
const { isBiometricUseCapableAndEnabled } = useBiometrics();
const { isElectrumDisabled, isPrivacyBlurEnabled } = useSettings();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const { isElectrumDisabled } = useSettings();
const { dispatch, setOptions, navigate, navigateToWalletsList, setParams } = useExtendedNavigation<NavigationProp>();
const route = useRoute<RouteParams>();
const { walletID } = route.params;
@ -152,8 +150,6 @@ const ViewEditMultisigCosigners: React.FC = () => {
useFocusEffect(
useCallback(() => {
if (isPrivacyBlurEnabled) enableScreenProtect();
// useFocusEffect is called on willAppear (example: when camera dismisses). we want to avoid this.
if (!hasLoaded.current) {
setIsLoading(true);
@ -174,13 +170,9 @@ const ViewEditMultisigCosigners: React.FC = () => {
if (!cancelled) setIsLoading(false);
})();
return () => {
disableScreenProtect();
cancelled = true;
};
}
return () => {
disableScreenProtect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [walletID]),
);

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useLayoutEffect, useRef, useReducer, useMemo } from 'react';
import { useRoute, RouteProp, useFocusEffect } from '@react-navigation/native';
import { useRoute, RouteProp } from '@react-navigation/native';
import { ActivityIndicator, FlatList, StyleSheet, View } from 'react-native';
import { WatchOnlyWallet } from '../../class/wallets/watch-only-wallet';
import { AddressItem } from '../../components/addresses/AddressItem';
@ -11,8 +11,6 @@ import { useExtendedNavigation } from '../../hooks/useExtendedNavigation';
import SegmentedControl from '../../components/SegmentedControl';
import loc from '../../loc';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import { useSettings } from '../../hooks/context/useSettings';
import { useScreenProtect } from '../../hooks/useScreenProtect';
export const TABS = {
EXTERNAL: 'receive',
@ -127,8 +125,6 @@ const WalletAddresses: React.FC = () => {
const allowSignVerifyMessage = (wallet && 'allowSignVerifyMessage' in wallet && wallet.allowSignVerifyMessage()) ?? false;
const { colors } = useTheme();
const { isPrivacyBlurEnabled } = useSettings();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const { setOptions } = useExtendedNavigation<NavigationProps>();
const stylesHook = StyleSheet.create({
@ -139,15 +135,6 @@ const WalletAddresses: React.FC = () => {
},
});
useFocusEffect(
useCallback(() => {
if (isPrivacyBlurEnabled) enableScreenProtect();
return () => {
disableScreenProtect();
};
}, [disableScreenProtect, enableScreenProtect, isPrivacyBlurEnabled]),
);
const getAddresses = useMemo(() => {
if (!walletInstance) return [];
const newAddresses: Address[] = [];

View File

@ -1,9 +1,8 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import Clipboard from '@react-native-clipboard/clipboard';
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { RouteProp, useRoute } from '@react-navigation/native';
import Icon from '../../components/Icon';
import { LayoutChangeEvent, ScrollView, StyleSheet, Pressable, View } from 'react-native';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import { validateMnemonic } from '../../blue_modules/bip39';
import triggerHapticFeedback, { HapticFeedbackTypes } from '../../blue_modules/hapticFeedback';
import { BlueText } from '../../BlueComponents';
@ -14,9 +13,8 @@ import QRCode from '../../components/QRCode';
import SeedWords from '../../components/SeedWords';
import { useTheme } from '../../components/themes';
import { HandOffActivityType } from '../../components/types';
import { useSettings } from '../../hooks/context/useSettings';
import { useStorage } from '../../hooks/context/useStorage';
import useAppState from '../../hooks/useAppState';
import { useScreenshotWarning } from '../../hooks/useScreenshotWarning';
import loc from '../../loc';
import { WalletExportStackParamList } from '../../navigation/WalletExportStack';
import { WalletDescriptor } from '../../class/wallet-descriptor.ts';
@ -59,13 +57,9 @@ const DoNotDisclose: React.FC = () => {
const WalletExport: React.FC = () => {
const { wallets } = useStorage();
const { walletID } = useRoute<RouteProps>().params;
const navigation = useNavigation();
const { isPrivacyBlurEnabled } = useSettings();
const { colors } = useTheme();
const wallet = wallets.find(w => w.getID() === walletID)!;
const [qrCodeSize, setQRCodeSize] = useState(90);
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const { currentAppState, previousAppState } = useAppState();
const stylesHook = StyleSheet.create({
root: { backgroundColor: colors.elevated },
});
@ -96,26 +90,7 @@ const WalletExport: React.FC = () => {
return validateMnemonic(wallet.getSecret());
}, [wallet]);
useEffect(() => {
if (previousAppState === 'active' && currentAppState !== 'active') {
disableScreenProtect();
const timer = setTimeout(() => {
navigation.goBack();
}, 500);
return () => clearTimeout(timer);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentAppState, previousAppState]);
useEffect(() => {
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
return () => {
disableScreenProtect();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isPrivacyBlurEnabled]);
useScreenshotWarning(secretIsMnemonic);
const onLayout = useCallback((e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout;

View File

@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { RouteProp, useFocusEffect, useRoute } from '@react-navigation/native';
import { RouteProp, useRoute } from '@react-navigation/native';
import { ActivityIndicator, FlatList, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import Animated, { LinearTransition } from 'react-native-reanimated';
import Icon from '../../components/Icon';
@ -21,7 +21,6 @@ import MultipleStepsListItem, {
MultipleStepsListItemButtonType,
MultipleStepsListItemDashType,
} from '../../components/MultipleStepsListItem';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import { BlueSpacing20 } from '../../components/BlueSpacing';
type MultisigStep2Params = {
@ -44,7 +43,6 @@ const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const WalletsAddMultisigStep2 = () => {
const { addAndSaveWallet, sleep, currentSharedCosigner, setSharedCosigner } = useStorage();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const { colors } = useTheme();
const navigation = useExtendedNavigation();
@ -56,20 +54,9 @@ const WalletsAddMultisigStep2 = () => {
const [vaultKeyData, setVaultKeyData] = useState({ keyIndex: 1, xpub: '', seed: '', isLoading: false }); // string rendered in modal
const [importText, setImportText] = useState('');
const [askPassphrase, setAskPassphrase] = useState(false);
const { isPrivacyBlurEnabled, isElectrumDisabled } = useSettings();
const { isElectrumDisabled } = useSettings();
const data = useRef(new Array(n).fill(null));
useFocusEffect(
useCallback(() => {
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
return () => {
disableScreenProtect();
};
}, [isPrivacyBlurEnabled, enableScreenProtect, disableScreenProtect]),
);
useEffect(() => {
console.log(currentSharedCosigner);
if (currentSharedCosigner) {

View File

@ -8,8 +8,6 @@ import QRCode from '../../components/QRCode';
import SafeAreaScrollView from '../../components/SafeAreaScrollView';
import { useTheme } from '../../components/themes';
import loc from '../../loc';
import { useSettings } from '../../hooks/context/useSettings';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import { BlueSpacing20 } from '../../components/BlueSpacing';
import useWalletSubscribe from '../../hooks/useWalletSubscribe.tsx';
@ -23,8 +21,6 @@ const PleaseBackupLNDHub = () => {
const navigation = useNavigation();
const { colors } = useTheme();
const [qrCodeSize, setQRCodeSize] = useState(90);
const { isPrivacyBlurEnabled } = useSettings();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const dismiss = useCallback(() => {
navigation.getParent()?.goBack();
@ -48,19 +44,15 @@ const PleaseBackupLNDHub = () => {
});
useEffect(() => {
if (isPrivacyBlurEnabled) {
enableScreenProtect();
}
const subscription = BackHandler.addEventListener('hardwareBackPress', () => {
dismiss();
return true;
});
return () => {
disableScreenProtect();
subscription.remove();
};
}, [dismiss, isPrivacyBlurEnabled, enableScreenProtect, disableScreenProtect]);
}, [dismiss]);
const onLayout = (e: LayoutChangeEvent) => {
const { height, width } = e.nativeEvent.layout;

View File

@ -8,12 +8,10 @@ import CopyTextToClipboard from '../../components/CopyTextToClipboard';
import HandOffComponent from '../../components/HandOffComponent';
import QRCode from '../../components/QRCode';
import SafeArea from '../../components/SafeArea';
import { useScreenProtect } from '../../hooks/useScreenProtect';
import loc from '../../loc';
import { styles, useDynamicStyles } from './xpub.styles';
import { useStorage } from '../../hooks/context/useStorage';
import { HandOffActivityType } from '../../components/types';
import { useSettings } from '../../hooks/context/useSettings';
import { BlueSpacing20 } from '../../components/BlueSpacing';
import { HDTaprootWallet } from '../../class/wallets/hd-taproot-wallet';
import { WalletDescriptor } from '../../class/wallet-descriptor.ts';
@ -31,8 +29,6 @@ const WalletXpub: React.FC = () => {
const route = useRoute<WalletXpubRouteProp>();
const { walletID, xpub } = route.params;
const wallet = wallets.find(w => w.getID() === walletID);
const { isPrivacyBlurEnabled } = useSettings();
const { enableScreenProtect, disableScreenProtect } = useScreenProtect();
const [isLoading, setIsLoading] = useState<boolean>(true);
const [xPubText, setXPubText] = useState<string | undefined>(undefined);
const navigation = useNavigation<NavigationProp<RootStackParamList, 'WalletXpub'>>();
@ -42,8 +38,6 @@ const WalletXpub: React.FC = () => {
useFocusEffect(
useCallback(() => {
if (isPrivacyBlurEnabled) enableScreenProtect();
// Skip execution if walletID hasn't changed
if (lastWalletIdRef.current === walletID) {
return;
}
@ -61,10 +55,9 @@ const WalletXpub: React.FC = () => {
})();
lastWalletIdRef.current = walletID;
return () => {
disableScreenProtect();
cancelled = true;
};
}, [isPrivacyBlurEnabled, walletID, wallet, xpub, navigation, enableScreenProtect, disableScreenProtect]),
}, [walletID, wallet, xpub, navigation]),
);
useEffect(() => {

View File

@ -48,6 +48,22 @@ jest.mock('react-native-capture-protection', () => ({
prevent: jest.fn(),
allow: jest.fn(),
isScreenRecording: jest.fn(() => Promise.resolve(false)),
addListener: jest.fn(() => ({ remove: jest.fn() })),
removeListener: jest.fn(),
hasListener: jest.fn(() => Promise.resolve(false)),
requestPermission: jest.fn(() => Promise.resolve(true)),
},
CaptureEventType: {
NONE: 0,
RECORDING: 1,
END_RECORDING: 2,
CAPTURED: 3,
APP_SWITCHING: 4,
UNKNOWN: 5,
ALLOW: 8,
PREVENT_SCREEN_CAPTURE: 16,
PREVENT_SCREEN_RECORDING: 32,
PREVENT_SCREEN_APP_SWITCHING: 64,
},
}));