Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add backend disabled dialog #4715

Merged
merged 9 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions reflex/.templates/web/utils/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ export const getBackendURL = (url_str) => {
return endpoint;
};

/**
* Check if the backend is disabled.
*
* @returns True if the backend is disabled, false otherwise.
*/
export const isBackendDisabled = () => {
const cookie = document.cookie
.split("; ")
.find((row) => row.startsWith("backend-enabled="));
return cookie !== undefined && cookie.split("=")[1] == "false";
};

/**
* Determine if any event in the event queue is stateful.
*
Expand Down Expand Up @@ -301,10 +313,7 @@ export const applyEvent = async (event, socket) => {

// Send the event to the server.
if (socket) {
socket.emit(
"event",
event,
);
socket.emit("event", event);
return true;
}

Expand Down Expand Up @@ -497,7 +506,7 @@ export const uploadFiles = async (
return false;
}

const upload_ref_name = `__upload_controllers_${upload_id}`
const upload_ref_name = `__upload_controllers_${upload_id}`;

if (refs[upload_ref_name]) {
console.log("Upload already in progress for ", upload_id);
Expand Down Expand Up @@ -815,7 +824,7 @@ export const useEventLoop = (
return;
}
// only use websockets if state is present
if (Object.keys(initialState).length > 1) {
if (Object.keys(initialState).length > 1 && !isBackendDisabled()) {
// Initialize the websocket connection.
if (!socket.current) {
connect(
Expand Down
9 changes: 8 additions & 1 deletion reflex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@
ComponentStyle,
evaluate_style_namespaces,
)
from reflex.components.core.banner import connection_pulser, connection_toaster
from reflex.components.core.banner import (
backend_disabled,
connection_pulser,
connection_toaster,
)
from reflex.components.core.breakpoints import set_breakpoints
from reflex.components.core.client_side_routing import (
Default404Page,
Expand Down Expand Up @@ -158,9 +162,12 @@ def default_overlay_component() -> Component:
Returns:
The default overlay_component, which is a connection_modal.
"""
config = get_config()

return Fragment.create(
connection_pulser(),
connection_toaster(),
*([backend_disabled()] if config.is_reflex_cloud else []),
*codespaces.codespaces_auto_redirect(),
)

Expand Down
79 changes: 79 additions & 0 deletions reflex/components/core/banner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

from typing import Optional

from reflex import constants
from reflex.components.component import Component
from reflex.components.core.cond import cond
from reflex.components.datadisplay.logo import svg_logo
from reflex.components.el.elements.typography import Div
from reflex.components.lucide.icon import Icon
from reflex.components.radix.themes.components.dialog import (
Expand Down Expand Up @@ -293,7 +295,84 @@ def create(cls, **props) -> Component:
)


class BackendDisabled(Div):
"""A component that displays a message when the backend is disabled."""

@classmethod
def create(cls, **props) -> Component:
"""Create a backend disabled component.

Args:
**props: The properties of the component.

Returns:
The backend disabled component.
"""
import reflex as rx

is_backend_disabled = Var(
"backendDisabled",
_var_type=bool,
_var_data=VarData(
hooks={
"const [backendDisabled, setBackendDisabled] = useState(false);": None,
"useEffect(() => { setBackendDisabled(isBackendDisabled()); }, []);": None,
},
imports={
f"$/{constants.Dirs.STATE_PATH}": [
ImportVar(tag="isBackendDisabled")
],
},
),
)

return super().create(
rx.cond(
is_backend_disabled,
rx.box(
rx.box(
rx.card(
rx.vstack(
svg_logo(),
rx.text(
"You ran out of compute credits.",
),
rx.callout(
rx.fragment(
"Please upgrade your plan or raise your compute credits at ",
rx.link(
"Reflex Cloud.",
href="https://cloud.reflex.dev/",
),
),
width="100%",
icon="info",
variant="surface",
),
),
font_size="20px",
font_family='"Inter", "Helvetica", "Arial", sans-serif',
variant="classic",
),
position="fixed",
top="50%",
left="50%",
transform="translate(-50%, -50%)",
width="40ch",
max_width="90vw",
),
position="fixed",
z_index=9999,
backdrop_filter="grayscale(1) blur(5px)",
width="100dvw",
height="100dvh",
),
)
)


connection_banner = ConnectionBanner.create
connection_modal = ConnectionModal.create
connection_toaster = ConnectionToaster.create
connection_pulser = ConnectionPulser.create
backend_disabled = BackendDisabled.create
86 changes: 86 additions & 0 deletions reflex/components/core/banner.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,93 @@ class ConnectionPulser(Div):
"""
...

class BackendDisabled(Div):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
content_editable: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
context_menu: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[[], BASE_STATE]] = None,
on_click: Optional[EventType[[], BASE_STATE]] = None,
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
on_double_click: Optional[EventType[[], BASE_STATE]] = None,
on_focus: Optional[EventType[[], BASE_STATE]] = None,
on_mount: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
on_scroll: Optional[EventType[[], BASE_STATE]] = None,
on_unmount: Optional[EventType[[], BASE_STATE]] = None,
**props,
) -> "BackendDisabled":
"""Create a backend disabled component.

Args:
access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
draggable: Defines whether the element can be dragged.
enter_key_hint: Hints what media types the media element is able to play.
hidden: Defines whether the element is hidden.
input_mode: Defines the type of the element.
item_prop: Defines the name of the element for metadata purposes.
lang: Defines the language used in the element.
role: Defines the role of the element.
slot: Assigns a slot in a shadow DOM shadow tree to an element.
spell_check: Defines whether the element may be checked for spelling errors.
tab_index: Defines the position of the current element in the tabbing order.
title: Defines a tooltip for the element.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: The properties of the component.

Returns:
The backend disabled component.
"""
...

connection_banner = ConnectionBanner.create
connection_modal = ConnectionModal.create
connection_toaster = ConnectionToaster.create
connection_pulser = ConnectionPulser.create
backend_disabled = BackendDisabled.create
2 changes: 1 addition & 1 deletion reflex/components/radix/themes/components/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Card(elements.Div, RadixThemesComponent):
# Card size: "1" - "5"
size: Var[Responsive[Literal["1", "2", "3", "4", "5"],]]

# Variant of Card: "solid" | "soft" | "outline" | "ghost"
# Variant of Card: "surface" | "classic" | "ghost"
variant: Var[Literal["surface", "classic", "ghost"]]


Expand Down
2 changes: 1 addition & 1 deletion reflex/components/radix/themes/components/card.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class Card(elements.Div, RadixThemesComponent):
*children: Child components.
as_child: Change the default rendered element for the one passed as a child, merging their props and behavior.
size: Card size: "1" - "5"
variant: Variant of Card: "solid" | "soft" | "outline" | "ghost"
variant: Variant of Card: "surface" | "classic" | "ghost"
access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable.
Expand Down
3 changes: 3 additions & 0 deletions reflex/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,9 @@ class Config: # pyright: ignore [reportIncompatibleVariableOverride]
# Path to file containing key-values pairs to override in the environment; Dotenv format.
env_file: Optional[str] = None

# Whether the app is running in the reflex cloud environment.
is_reflex_cloud: bool = False

def __init__(self, *args, **kwargs):
"""Initialize the config values.

Expand Down
Loading