Preskočiť na obsah

React Native (@frontmail/react-native)

@frontmail/react-native umožní aplikácii pre iOS alebo Android odosielať e-maily cez vaše šablóny s verejným kľúčom – bez vlastného backendu. Funguje s Expom aj s čistým React Native.

Expo:

Terminal window
npx expo install @frontmail/react-native @react-native-async-storage/async-storage react-native-webview

Čisté React Native:

Terminal window
npm install @frontmail/react-native @react-native-async-storage/async-storage react-native-webview
npx pod-install

Oba ďalšie balíky sú voliteľné peer závislosti:

Balík Na čo je potrebný
@react-native-async-storage/async-storage limitRate, ktorý prežije reštart aplikácie
react-native-webview <TurnstileWebView>

Natívne aplikácie neposielajú hlavičku Origin ani Referer, takže požiadavky z aplikácie (bez ohľadu na to, či máte nastavené povolené weby) skončia chybou 403 origin_not_allowed, kým v dashboarde nezapnete Zabezpečenie → Mobilné aplikácie → Povoliť mobilné aplikácie a nekliknete na Uložiť. Pozrite Nastavenia zabezpečenia.

Požiadavky z webov sa naďalej kontrolujú podľa zoznamu, takže prepínač ochranu vašich webových formulárov neoslabí. Prepínač je potrebný aj pri prázdnom zozname.

Obaľte aplikáciu do FrontmailProvider a odosielajte pomocou useSendEmail. Objekt s voľbami udržujte stabilný – definujte ho na úrovni modulu alebo pomocou useMemo:

import { useState } from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { FrontmailProvider, useSendEmail } from '@frontmail/react-native';
const frontmailOptions = { publicKey: 'pk_4f2a…', limitRate: { id: 'contact', throttle: 30_000 } };
export default function App() {
return (
<FrontmailProvider options={frontmailOptions}>
<ContactScreen />
</FrontmailProvider>
);
}
function ContactScreen() {
const { send, status, error } = useSendEmail('svc_01J9…', 'tpl_contact');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
return (
<View>
<TextInput value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" />
<TextInput value={message} onChangeText={setMessage} multiline />
<Button title="Odoslať" disabled={status === 'sending'} onPress={() => send({ email, message })} />
{status === 'sent' && <Text>Ďakujeme!</Text>}
{status === 'held' && <Text>Prijaté – čoskoro ju doručíme.</Text>}
{error && <Text>{error.message}</Text>}
</View>
);
}

options prijíma publicKey, apiUrl, retry, limitRate, storageProvider a blockList. privateKey neprijíma. Prípadne môžete odovzdať existujúceho klienta: <FrontmailProvider client={client}>.

useSendEmail(serviceId, templateId) vracia { send, getStatus, status, error, result, reset }. status prechádza idle → sending → sent | held | error. send(params, options?) sa resolvne s výsledkom alebo pri chybe s undefined (chyba je v error) – nikdy nevyhodí výnimku, takže try/catch nepotrebujete. reset() vráti hook do stavu idle.

Ak chcete e-mail poslať v niektorom jazyku šablóny, odovzdajte locale vo voľbách: send({ email, message }, { locale: 'de' }) – napríklad jazyk zariadenia z Intl.DateTimeFormat().resolvedOptions().locale. Pri neznámom jazyku sa použije rovnaký základný jazyk a potom predvolený jazyk šablóny; result.locale je použitý jazyk.

Každá požiadavka nesie hlavičku X-Frontmail-Client: @frontmail/react-native/<verzia>.

Ak šablóna vyžaduje Turnstile, vykreslite <TurnstileWebView> a token odovzdajte do send. Site key nepotrebujete: komponent v predvolenom stave používa zdieľaný mobilný kľúč Frontmailu. Načíta ho z GET /v1/public-config API nastaveného v provideri (s cache, takže len raz) a widget Cloudflare zobrazí v react-native-webview ako vloženú HTML stránku s adresou https://mobile.frontmail.dev – z tej adresy sa nič nesťahuje.

import { useRef, useState } from 'react';
import { Button } from 'react-native';
import { TurnstileWebView, useSendEmail } from '@frontmail/react-native';
import type { TurnstileWebViewHandle } from '@frontmail/react-native';
function ContactScreen() {
const { send, status } = useSendEmail('svc_01J9…', 'tpl_contact');
const turnstile = useRef<TurnstileWebViewHandle>(null);
const [token, setToken] = useState<string>();
const onSubmit = async () => {
await send({ email, message }, { turnstileToken: token });
// Token sa dá použiť len raz – na ďalší pokus si vyžiadajte nový.
setToken(undefined);
turnstile.current?.reset();
};
return (
<>
{/* …polia formulára… */}
<TurnstileWebView
ref={turnstile}
onToken={setToken}
onExpire={() => setToken(undefined)}
onError={(e) => console.warn(e.message)}
/>
<Button title="Odoslať" disabled={!token || status === 'sending'} onPress={onSubmit} />
</>
);
}

Props: onToken a voliteľne siteKey, baseUrl, onError, onExpire, theme (auto | light | dark), size (normal | compact | flexible), action, language, style a webViewProps (odovzdajú sa WebView).

WebView je uzamknuté na widget: načíta len vloženú stránku (baseUrl) a výzvu Cloudflaru, odkazy vo widgete sa otvoria v systémovom prehliadači a do vašich callbackov sa dostanú len správy zo stránky widgetu. webViewProps nemôžu prepísať source, originWhitelist, stráženie navigácie, onMessage ani nastavenia JavaScriptu a prístupu k súborom.

Požiadavky z aplikácie nemajú hlavičku Origin, takže ich tokeny Frontmail overí svojím zdieľaným mobilným tajným kľúčom. Vlastné kľúče Turnstile na to nepotrebujete – so zapnutým Povoliť mobilné aplikácie môžete CAPTCHA pri šablónach aplikácie zapnúť, aj keď organizácia kľúče zatiaľ nemá.

Ak chcete namiesto toho použiť vlastný widget Turnstile, odovzdajte siteKey aj baseUrl:

<TurnstileWebView
ref={turnstile}
siteKey="YOUR_TURNSTILE_SITE_KEY"
baseUrl="https://example.com"
onToken={setToken}
/>

SDK potom tokeny z tohto widgetu automaticky posiela s turnstile_key: "org" a Frontmail ich overí secret key zo Zabezpečenie → Ochrana pred botmi (Turnstile).

Token platí približne 5 minút a dá sa použiť len raz: po každom odoslaní, úspešnom aj neúspešnom, zavolajte ref.current.reset().

limitRate obmedzuje odosielanie priamo v zariadení, napríklad na najviac jednu správu za 30 sekúnd. V React Native neexistuje location.pathname, takže vždy nastavte limitRate.id:

import AsyncStorage from '@react-native-async-storage/async-storage';
import { asyncStorageProvider } from '@frontmail/react-native';
const frontmailOptions = {
publicKey: 'pk_4f2a…',
limitRate: { id: 'contact', throttle: 30_000 },
storageProvider: asyncStorageProvider(AsyncStorage),
};

Bez storageProvider použije SDK AsyncStorage, ak je nainštalovaný; inak si limit drží len v pamäti (po reštarte aplikácie sa vynuluje) a pri vývoji vypíše varovanie. Pamäťové úložisko zvolíte výslovne pomocou memoryStorageProvider().

limitRate chráni len pred nechceným opakovaným ťuknutím – skutočná ochrana je na serveri (pozrite Zabezpečenie).

getStatus() z hooku zistí stav doručenia poslednej prijatej správy. Na všetko ostatné vracia useFrontmail() klienta z providera (send, getStatus):

import { useFrontmail } from '@frontmail/react-native';
const client = useFrontmail();
const status = await client.getStatus(messageId, { token });

Sieťové chyby, 5xx a 429 sa automaticky opakujú a všetky pokusy jedného odoslania zdieľajú rovnaký idempotency key, takže opakovanie e-mail nikdy nepošle dvakrát. Kľúče sú UUID v4 – SDK používa crypto.randomUUID a na Hermese, ktorý ho nemá, prejde na vlastný generátor. Pozrite Opakovanie a idempotencia.

S vygenerovanými typmi (npx frontmail types rozšíri @frontmail/react-native) typovo kontroluje useSendEmail('svc_…', 'tpl_contact').send({...}) parametre danej šablóny.

error je FrontmailError s vlastnosťami code, status a message. Ako reagovať na jednotlivé kódy, opisuje Spracovanie chýb.

  • Do aplikácie patrí len verejný kľúč (pk_…). Čokoľvek v balíku aplikácie sa dá vytiahnuť, takže súkromný kľúč v nej je ako zverejnený. Pozrite Verejný a súkromný kľúč.
  • So zapnutým Povoliť mobilné aplikácie prijmeme požiadavky bez hlavičky Origin odkiaľkoľvek – poslať ich môže akýkoľvek skript, ktorý pozná váš verejný kľúč. Spoľahnite sa na limit na IP adresu, Turnstile pri všetkých šablónach, ktoré aplikácia používa, a na zoznam blokovaných hodnôt.

Ukážkový projekt: examples/react-native.