Skip to content

Commit 487d78a

Browse files
committed
update plain
1 parent 3bef3ac commit 487d78a

File tree

1 file changed

+75
-24
lines changed

1 file changed

+75
-24
lines changed

reflex_ui/blocks/plain.py

Lines changed: 75 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,89 @@
11
"""Plain chat integration for customer support.
22
33
This module provides integration with Plain.com chat widget for live customer support.
4+
See: https://plain.support.site/article/chat-customization
45
"""
56

7+
from typing import Any
8+
69
import reflex as rx
10+
from reflex.components.tags.tag import Tag
11+
12+
PLAIN_APP_ID = "liveChatApp_01KGG4JD5JHG8JY8X5CCN7811V"
13+
14+
15+
class PlainChat(rx.Component):
16+
"""Plain chat widget component.
17+
18+
See: https://plain.support.site/article/live-chat-authentication
19+
20+
Note: If you provide email, you MUST also provide email_hash.
21+
The hash must be generated server-side using HMAC-SHA256 with your Plain secret.
22+
"""
723

8-
PLAIN_INIT_SCRIPT = """
9-
(function(d, script) {{
10-
script = d.createElement('script');
24+
tag = "PlainChat"
25+
26+
# Customer details (all optional)
27+
full_name: rx.Var[str] = rx.Var.create("")
28+
short_name: rx.Var[str] = rx.Var.create("")
29+
chat_avatar_url: rx.Var[str] = rx.Var.create("")
30+
email: rx.Var[str] = rx.Var.create("")
31+
email_hash: rx.Var[str] = rx.Var.create("")
32+
33+
# Thread details (optional)
34+
external_id: rx.Var[str] = rx.Var.create("")
35+
36+
# Options
37+
hide_launcher: rx.Var[bool] = rx.Var.create(True)
38+
require_authentication: rx.Var[bool] = rx.Var.create(False)
39+
40+
def add_hooks(self) -> list[str | rx.Var]:
41+
"""Add hooks to initialize Plain chat widget."""
42+
return [
43+
rx.Var(
44+
f"""const PlainChatComponent = (() => {{
45+
// Load Plain chat script
46+
const script = document.createElement('script');
1147
script.async = false;
12-
script.onload = function(){{
13-
Plain.init({{
14-
appId: 'liveChatApp_01KGG4JD5JHG8JY8X5CCN7811V',
15-
hideLauncher: {hide_launcher},
48+
script.src = 'https://chat.cdn-plain.com/index.js';
49+
script.onload = function() {{
50+
// Build customer details, only including non-empty values
51+
const customerDetails = {{}};
52+
if ({self.full_name!s}) customerDetails.fullName = {self.full_name!s};
53+
if ({self.short_name!s}) customerDetails.shortName = {self.short_name!s};
54+
if ({self.chat_avatar_url!s}) customerDetails.chatAvatarUrl = {self.chat_avatar_url!s};
55+
if ({self.email!s}) customerDetails.email = {self.email!s};
56+
if ({self.email_hash!s}) customerDetails.emailHash = {self.email_hash!s};
57+
58+
// Build thread details, only including non-empty values
59+
const threadDetails = {{}};
60+
if ({self.external_id!s}) threadDetails.externalId = {self.external_id!s};
61+
62+
// Build init options
63+
const initOptions = {{
64+
appId: '{PLAIN_APP_ID}',
65+
hideLauncher: {self.hide_launcher!s},
1666
hideBranding: true,
1767
theme: 'auto',
18-
}});
68+
}};
69+
70+
if ({self.require_authentication!s}) initOptions.requireAuthentication = true;
71+
if (Object.keys(customerDetails).length > 0) initOptions.customerDetails = customerDetails;
72+
if (Object.keys(threadDetails).length > 0) initOptions.threadDetails = threadDetails;
73+
74+
Plain.init(initOptions);
1975
}};
20-
script.src = 'https://chat.cdn-plain.com/index.js';
21-
d.getElementsByTagName('head')[0].appendChild(script);
22-
}}(document));
23-
"""
76+
document.head.appendChild(script);
77+
return null;
78+
}})()"""
79+
)
80+
]
81+
82+
def _render(self, props: dict[str, Any] | None = None) -> Tag:
83+
return Tag("")
84+
85+
86+
plain_chat = PlainChat.create
2487

2588

2689
def open_plain_chat() -> rx.event.EventSpec:
@@ -32,15 +95,3 @@ def open_plain_chat() -> rx.event.EventSpec:
3295
return rx.call_script(
3396
"try { Plain.open(); } catch (e) { console.error('Plain chat not available:', e); }"
3497
)
35-
36-
37-
def get_plain_script(hide_launcher: bool = True) -> rx.Component:
38-
"""Get the Plain chat initialization script component.
39-
40-
Args:
41-
hide_launcher: Whether to hide the default Plain chat launcher button. Defaults to True.
42-
43-
Returns:
44-
A Reflex script component that initializes the Plain chat widget.
45-
"""
46-
return rx.script(PLAIN_INIT_SCRIPT.format(hide_launcher=str(hide_launcher).lower()))

0 commit comments

Comments
 (0)