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 collaborative counter example #91

Closed

Conversation

milochen0418
Copy link
Contributor

@milochen0418 milochen0418 commented Jun 3, 2023

2023-06-03.17-25-31.mp4

This counter demonstrates a simple Collaborative web app. And it can also support IoT applications too if you need.

  • Tick: Use the Tick pattern to update UI whenever the state is changing
  • State Manager: Use state manager to update every state for every client
  • API Routes: use API Routes to allow changing state from another place.

For this collaborative web app, how does MQTT Support?
For example, you may use an MQTT subscriber to receive messages from IoT devices and send requests to the API route.

I show one pattern to implement a collaborative counter-example in this PR
Someone may know how to improve it in a better way.

That's better to use squash merge because I have a messy repo. 😝

@milochen0418 milochen0418 changed the title Collaborative counter Add collaborative counter example Jun 3, 2023
@milochen0418
Copy link
Contributor Author

I try to improve the code in to real time without calling sleep by using await event.wait(), event.set(), event.clear()
But the code cannot work because the on_click will not invoke the function directly.

But I can use localhost:8000/state_count/1122 to invoke the set_state_count function.

For more observation,
If I click count up button 10 time, the count_up function will not be called.
Then I can send request on localhost:8000/state_count/11
You can find out count become 11 first and there will be 10 time count up function happened.
I don't know why, but it look like we cannot use await tickevent.wait() to optimized our code.

import pynecone as pc
import asyncio
from .styles import base_style

# create an instance of an event
tickevent = asyncio.Event()
tickevent.clear()

async def set_state_count(count_val:int):
    print("set_state_count with count_val = " + str(count_val))
    keys = list(app.state_manager.states) #convert the keys of dict into the list
    ret_count_val: int = count_val
    for token in keys: # change state.count for all current running frontend
        state = app.state_manager.get_state(token)
        state.count = count_val
        app.state_manager.set_state(token, state)
    print("start to call tickevent.set()")
    tickevent.set()
    print("end of call tickevent.set()")
    return {"state_count":ret_count_val}


class State(pc.State):
    count: int = 0
    is_run_tick:bool = False
    async def count_up(self):
        print("count_up start")
        self.count = self.count + 1
        await set_state_count(self.count)
        print("count_up end")
        

    async def count_down(self):
        print("count_up start")
        self.count = self.count - 1
        await set_state_count(self.count)
        print("count_down end")
        
    async def tick(self):
        print("tick")
        # run tick for update frontend ui for updating count
        if self.is_run_tick:
            # wait for the event to be set
            await tickevent.wait()
            tickevent.clear()
            return self.tick
        
    async def onload(self):
        self.is_run_tick = True
        return self.tick
    
        
def index():
    return pc.center(
        pc.vstack(
            pc.heading("Counter"),
            pc.text("Collaborative Count"),
            pc.hstack(
                pc.button("+", on_click=State.count_up),
                pc.button("-", on_click=State.count_down),
            ),
            pc.link(State.count),
            spacing="1.5em",
            font_size="2em",
        ),
        padding_top="10%",
    )

print("Hint:You can open http://localhost:8000/state_count/33 to set count value as 33")
# Add state and page to the app.

app = pc.App(state=State, style=base_style)
app.api.add_api_route("/state_count/{count_val}", set_state_count)
app.add_page(
    index,
    title = "Collaborative Counter App",
    description = "A collaborative counter app",
    meta = [
        {"name": "theme_color", "content": "#FFFFFF"},
        {"char_set": "UTF-8"},
        {"property": "og:url", "content": "url"},
    ],
    on_load = State.onload,
)
app.compile()

@Lendemor
Copy link
Collaborator

Lendemor commented Jul 1, 2023

If you still want this example merged, you can upgrade it to use reflex==0.2.0, then we'll review it.

@milochen0418
Copy link
Contributor Author

milochen0418 commented Jul 2, 2023

If you still want this example merged, you can upgrade it to use reflex==0.2.0, then we'll review it.
Thanks,

I have updated it to support reflex 0.2.0 now.
We can start to review it.

Copy link
Collaborator

@Lendemor Lendemor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need fix

Initial value when opening a new tab does not synchronize to other already existing tabs.

improvement

I think it is working for now, but we should consider adding a built in way to synchronize some variable between many states (or use database Model to keep synchronization working)

@milochen0418
Copy link
Contributor Author

need fix

Initial value when opening a new tab does not synchronize to other already existing tabs.

improvement

I think it is working for now, but we should consider adding a built in way to synchronize some variable between many states (or use database Model to keep synchronization working)

@Lendemor
Thanks you so much !
Thanks for your nice testing and the nice suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants