1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Copyright 2023 Sebastian Dobe <sebastiandobe@mailbox.org>

#![doc = include_str!("../README.md")]

use core::time::Duration;
use leptos::*;

/// The component accepts an optional honeypot email address / link you can use, if you want to have a
/// sophisticated setup and blacklist any sender that sends an E-Mail to it.
///
/// The `delay_seconds` can be set as well. After this timeout, when mounted inside the browser,
/// the honeypot address will be exchanged with the real one. This means the link will not work with
/// HTML only, but there is no good way to prevent bots without Javascript / WASM.
///
/// # Panics
/// If the given String does not contain '@'
#[component]
pub fn ObfuscateEmail(
    email: ReadSignal<String>,
    #[prop(default = "mailto:honeypot@example.com")] honeypot: &'static str,
    #[prop(default = 3)] delay_seconds: u64,
) -> impl IntoView {
    let mailto = create_rw_signal(honeypot.to_string());

    create_effect(move |_| {
        let mail = format!("mailto:{}", email.get());
        set_timeout(move || mailto.set(mail), Duration::from_secs(delay_seconds));
    });

    let one = move || {
        let plain = email.get();
        let (one, _) = plain.split_once('@').unwrap();
        one.chars().rev().collect::<String>()
    };
    let two = move || {
        let plain = email.get();
        let (_, two) = plain.split_once('@').unwrap();
        two.chars().rev().collect::<String>()
    };

    view! {
        <a href=move || mailto.get()>
            <span aria-label="E-Mail" class="obfuscate">
                {two}
                <i>"%/#"</i>
                <span></span>
                {one}
            </span>
        </a>
    }
}