Use websockets for events (#150)
This commit is contained in:
parent
84ca907aac
commit
0b496fc0e0
87
poetry.lock
generated
87
poetry.lock
generated
@ -604,14 +604,14 @@ plugins = ["importlib-metadata"]
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.283"
|
||||
version = "1.1.284"
|
||||
description = "Command line wrapper for pyright"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pyright-1.1.283-py3-none-any.whl", hash = "sha256:d5b14bb50ccbfb7769e09a611960665021816ea1b0543e0ffb8c341388c73710"},
|
||||
{file = "pyright-1.1.283.tar.gz", hash = "sha256:57dcc7abf0b764d5f587a80faf20186f5666adffe5f34afa10d471e96bbd9509"},
|
||||
{file = "pyright-1.1.284-py3-none-any.whl", hash = "sha256:e3bfbd33c20af48eed9d20138767265161ba8a4b55c740476a36ce822bd482d1"},
|
||||
{file = "pyright-1.1.284.tar.gz", hash = "sha256:ef7c0e46e38be95687f5a0633e55c5171ca166048b9560558168a976162e287c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -1020,6 +1020,85 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
|
||||
[package.extras]
|
||||
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "10.4"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "websockets-10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d58804e996d7d2307173d56c297cf7bc132c52df27a3efaac5e8d43e36c21c48"},
|
||||
{file = "websockets-10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc0b82d728fe21a0d03e65f81980abbbcb13b5387f733a1a870672c5be26edab"},
|
||||
{file = "websockets-10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba089c499e1f4155d2a3c2a05d2878a3428cf321c848f2b5a45ce55f0d7d310c"},
|
||||
{file = "websockets-10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33d69ca7612f0ddff3316b0c7b33ca180d464ecac2d115805c044bf0a3b0d032"},
|
||||
{file = "websockets-10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62e627f6b6d4aed919a2052efc408da7a545c606268d5ab5bfab4432734b82b4"},
|
||||
{file = "websockets-10.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ea7b82bfcae927eeffc55d2ffa31665dc7fec7b8dc654506b8e5a518eb4d50"},
|
||||
{file = "websockets-10.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0cb5cc6ece6ffa75baccfd5c02cffe776f3f5c8bf486811f9d3ea3453676ce8"},
|
||||
{file = "websockets-10.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae5e95cfb53ab1da62185e23b3130e11d64431179debac6dc3c6acf08760e9b1"},
|
||||
{file = "websockets-10.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7c584f366f46ba667cfa66020344886cf47088e79c9b9d39c84ce9ea98aaa331"},
|
||||
{file = "websockets-10.4-cp310-cp310-win32.whl", hash = "sha256:b029fb2032ae4724d8ae8d4f6b363f2cc39e4c7b12454df8df7f0f563ed3e61a"},
|
||||
{file = "websockets-10.4-cp310-cp310-win_amd64.whl", hash = "sha256:8dc96f64ae43dde92530775e9cb169979f414dcf5cff670455d81a6823b42089"},
|
||||
{file = "websockets-10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47a2964021f2110116cc1125b3e6d87ab5ad16dea161949e7244ec583b905bb4"},
|
||||
{file = "websockets-10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e789376b52c295c4946403bd0efecf27ab98f05319df4583d3c48e43c7342c2f"},
|
||||
{file = "websockets-10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7d3f0b61c45c3fa9a349cf484962c559a8a1d80dae6977276df8fd1fa5e3cb8c"},
|
||||
{file = "websockets-10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f55b5905705725af31ccef50e55391621532cd64fbf0bc6f4bac935f0fccec46"},
|
||||
{file = "websockets-10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00c870522cdb69cd625b93f002961ffb0c095394f06ba8c48f17eef7c1541f96"},
|
||||
{file = "websockets-10.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f38706e0b15d3c20ef6259fd4bc1700cd133b06c3c1bb108ffe3f8947be15fa"},
|
||||
{file = "websockets-10.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f2c38d588887a609191d30e902df2a32711f708abfd85d318ca9b367258cfd0c"},
|
||||
{file = "websockets-10.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:fe10ddc59b304cb19a1bdf5bd0a7719cbbc9fbdd57ac80ed436b709fcf889106"},
|
||||
{file = "websockets-10.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:90fcf8929836d4a0e964d799a58823547df5a5e9afa83081761630553be731f9"},
|
||||
{file = "websockets-10.4-cp311-cp311-win32.whl", hash = "sha256:b9968694c5f467bf67ef97ae7ad4d56d14be2751000c1207d31bf3bb8860bae8"},
|
||||
{file = "websockets-10.4-cp311-cp311-win_amd64.whl", hash = "sha256:a7a240d7a74bf8d5cb3bfe6be7f21697a28ec4b1a437607bae08ac7acf5b4882"},
|
||||
{file = "websockets-10.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:74de2b894b47f1d21cbd0b37a5e2b2392ad95d17ae983e64727e18eb281fe7cb"},
|
||||
{file = "websockets-10.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3a686ecb4aa0d64ae60c9c9f1a7d5d46cab9bfb5d91a2d303d00e2cd4c4c5cc"},
|
||||
{file = "websockets-10.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d15c968ea7a65211e084f523151dbf8ae44634de03c801b8bd070b74e85033"},
|
||||
{file = "websockets-10.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00213676a2e46b6ebf6045bc11d0f529d9120baa6f58d122b4021ad92adabd41"},
|
||||
{file = "websockets-10.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e23173580d740bf8822fd0379e4bf30aa1d5a92a4f252d34e893070c081050df"},
|
||||
{file = "websockets-10.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:dd500e0a5e11969cdd3320935ca2ff1e936f2358f9c2e61f100a1660933320ea"},
|
||||
{file = "websockets-10.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4239b6027e3d66a89446908ff3027d2737afc1a375f8fd3eea630a4842ec9a0c"},
|
||||
{file = "websockets-10.4-cp37-cp37m-win32.whl", hash = "sha256:8a5cc00546e0a701da4639aa0bbcb0ae2bb678c87f46da01ac2d789e1f2d2038"},
|
||||
{file = "websockets-10.4-cp37-cp37m-win_amd64.whl", hash = "sha256:a9f9a735deaf9a0cadc2d8c50d1a5bcdbae8b6e539c6e08237bc4082d7c13f28"},
|
||||
{file = "websockets-10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c1289596042fad2cdceb05e1ebf7aadf9995c928e0da2b7a4e99494953b1b94"},
|
||||
{file = "websockets-10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0cff816f51fb33c26d6e2b16b5c7d48eaa31dae5488ace6aae468b361f422b63"},
|
||||
{file = "websockets-10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dd9becd5fe29773d140d68d607d66a38f60e31b86df75332703757ee645b6faf"},
|
||||
{file = "websockets-10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45ec8e75b7dbc9539cbfafa570742fe4f676eb8b0d3694b67dabe2f2ceed8aa6"},
|
||||
{file = "websockets-10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f72e5cd0f18f262f5da20efa9e241699e0cf3a766317a17392550c9ad7b37d8"},
|
||||
{file = "websockets-10.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185929b4808b36a79c65b7865783b87b6841e852ef5407a2fb0c03381092fa3b"},
|
||||
{file = "websockets-10.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d27a7e34c313b3a7f91adcd05134315002aaf8540d7b4f90336beafaea6217c"},
|
||||
{file = "websockets-10.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:884be66c76a444c59f801ac13f40c76f176f1bfa815ef5b8ed44321e74f1600b"},
|
||||
{file = "websockets-10.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:931c039af54fc195fe6ad536fde4b0de04da9d5916e78e55405436348cfb0e56"},
|
||||
{file = "websockets-10.4-cp38-cp38-win32.whl", hash = "sha256:db3c336f9eda2532ec0fd8ea49fef7a8df8f6c804cdf4f39e5c5c0d4a4ad9a7a"},
|
||||
{file = "websockets-10.4-cp38-cp38-win_amd64.whl", hash = "sha256:48c08473563323f9c9debac781ecf66f94ad5a3680a38fe84dee5388cf5acaf6"},
|
||||
{file = "websockets-10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:40e826de3085721dabc7cf9bfd41682dadc02286d8cf149b3ad05bff89311e4f"},
|
||||
{file = "websockets-10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56029457f219ade1f2fc12a6504ea61e14ee227a815531f9738e41203a429112"},
|
||||
{file = "websockets-10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fc088b7a32f244c519a048c170f14cf2251b849ef0e20cbbb0fdf0fdaf556f"},
|
||||
{file = "websockets-10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc8709c00704194213d45e455adc106ff9e87658297f72d544220e32029cd3d"},
|
||||
{file = "websockets-10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0154f7691e4fe6c2b2bc275b5701e8b158dae92a1ab229e2b940efe11905dff4"},
|
||||
{file = "websockets-10.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c6d2264f485f0b53adf22697ac11e261ce84805c232ed5dbe6b1bcb84b00ff0"},
|
||||
{file = "websockets-10.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9bc42e8402dc5e9905fb8b9649f57efcb2056693b7e88faa8fb029256ba9c68c"},
|
||||
{file = "websockets-10.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:edc344de4dac1d89300a053ac973299e82d3db56330f3494905643bb68801269"},
|
||||
{file = "websockets-10.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:84bc2a7d075f32f6ed98652db3a680a17a4edb21ca7f80fe42e38753a58ee02b"},
|
||||
{file = "websockets-10.4-cp39-cp39-win32.whl", hash = "sha256:c94ae4faf2d09f7c81847c63843f84fe47bf6253c9d60b20f25edfd30fb12588"},
|
||||
{file = "websockets-10.4-cp39-cp39-win_amd64.whl", hash = "sha256:bbccd847aa0c3a69b5f691a84d2341a4f8a629c6922558f2a70611305f902d74"},
|
||||
{file = "websockets-10.4-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:82ff5e1cae4e855147fd57a2863376ed7454134c2bf49ec604dfe71e446e2193"},
|
||||
{file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d210abe51b5da0ffdbf7b43eed0cfdff8a55a1ab17abbec4301c9ff077dd0342"},
|
||||
{file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:942de28af58f352a6f588bc72490ae0f4ccd6dfc2bd3de5945b882a078e4e179"},
|
||||
{file = "websockets-10.4-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b27d6c1c6cd53dc93614967e9ce00ae7f864a2d9f99fe5ed86706e1ecbf485"},
|
||||
{file = "websockets-10.4-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3d3cac3e32b2c8414f4f87c1b2ab686fa6284a980ba283617404377cd448f631"},
|
||||
{file = "websockets-10.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da39dd03d130162deb63da51f6e66ed73032ae62e74aaccc4236e30edccddbb0"},
|
||||
{file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389f8dbb5c489e305fb113ca1b6bdcdaa130923f77485db5b189de343a179393"},
|
||||
{file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09a1814bb15eff7069e51fed0826df0bc0702652b5cb8f87697d469d79c23576"},
|
||||
{file = "websockets-10.4-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff64a1d38d156d429404aaa84b27305e957fd10c30e5880d1765c9480bea490f"},
|
||||
{file = "websockets-10.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b343f521b047493dc4022dd338fc6db9d9282658862756b4f6fd0e996c1380e1"},
|
||||
{file = "websockets-10.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:932af322458da7e4e35df32f050389e13d3d96b09d274b22a7aa1808f292fee4"},
|
||||
{file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a4162139374a49eb18ef5b2f4da1dd95c994588f5033d64e0bbfda4b6b6fcf"},
|
||||
{file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c57e4c1349fbe0e446c9fa7b19ed2f8a4417233b6984277cce392819123142d3"},
|
||||
{file = "websockets-10.4-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b627c266f295de9dea86bd1112ed3d5fafb69a348af30a2422e16590a8ecba13"},
|
||||
{file = "websockets-10.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:05a7233089f8bd355e8cbe127c2e8ca0b4ea55467861906b80d2ebc7db4d6b72"},
|
||||
{file = "websockets-10.4.tar.gz", hash = "sha256:eef610b23933c54d5d921c92578ae5f89813438fded840c2e9809d378dc765d3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zipp"
|
||||
version = "3.11.0"
|
||||
@ -1039,4 +1118,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.7"
|
||||
content-hash = "bdbe03e00e254692c4037edb18af3f1c4be6562f95108c9f057691878d09885d"
|
||||
content-hash = "97dfea67e5b4dd4749628c44911fa43392206c1c77a90191b267f060f95d10c4"
|
||||
|
Binary file not shown.
@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
reactStrictMode: true
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
@ -18,7 +18,7 @@
|
||||
"gridjs-react": "^4.0.0",
|
||||
"next": "^12.1.0",
|
||||
"plotly.js": "2.6.4",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier": "^2.8.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-markdown": "^8.0.3",
|
||||
@ -28,4 +28,4 @@
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,16 @@
|
||||
import axios from "axios";
|
||||
// State management for Pynecone web apps.
|
||||
|
||||
// Global variable to hold the token.
|
||||
let token;
|
||||
|
||||
// Key for the token in the session storage.
|
||||
const TOKEN_KEY = "token";
|
||||
|
||||
/**
|
||||
* Generate a UUID (Used for session tokens).
|
||||
* Taken from: https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
|
||||
* @returns A UUID.
|
||||
*/
|
||||
const generateUUID = () => {
|
||||
let d = new Date().getTime(),
|
||||
d2 = (performance && performance.now && performance.now() * 1000) || 0;
|
||||
@ -19,6 +27,10 @@ const generateUUID = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the token for the current session.
|
||||
* @returns The token.
|
||||
*/
|
||||
export const getToken = () => {
|
||||
if (token) {
|
||||
return token;
|
||||
@ -32,6 +44,11 @@ export const getToken = () => {
|
||||
return token;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply a delta to the state.
|
||||
* @param state The state to apply the delta to.
|
||||
* @param delta The delta to apply.
|
||||
*/
|
||||
export const applyDelta = (state, delta) => {
|
||||
for (const substate in delta) {
|
||||
let s = state;
|
||||
@ -45,53 +62,86 @@ export const applyDelta = (state, delta) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const applyEvent = async (state, event, endpoint, router) => {
|
||||
/**
|
||||
* Send an event to the server.
|
||||
* @param event The event to send.
|
||||
* @param router The router object.
|
||||
* @param socket The socket object to send the event on.
|
||||
*/
|
||||
export const applyEvent = async (event, router, socket) => {
|
||||
// Handle special events
|
||||
if (event.name == "_redirect") {
|
||||
router.push(event.payload.path);
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.name == "_console") {
|
||||
console.log(event.payload.message);
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.name == "_alert") {
|
||||
alert(event.payload.message);
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the event to the server.
|
||||
event.token = getToken();
|
||||
const update = (await axios.post(endpoint, event)).data;
|
||||
applyDelta(state, update.delta);
|
||||
return update.events;
|
||||
if (socket) {
|
||||
socket.send(JSON.stringify(event));
|
||||
}
|
||||
};
|
||||
|
||||
export const updateState = async (
|
||||
state,
|
||||
result,
|
||||
setResult,
|
||||
endpoint,
|
||||
router
|
||||
) => {
|
||||
/**
|
||||
* Process an event off the event queue.
|
||||
* @param state The state with the event queue.
|
||||
* @param result The current result
|
||||
* @param setResult The function to set the result.
|
||||
* @param router The router object.
|
||||
* @param socket The socket object to send the event on.
|
||||
*/
|
||||
export const updateState = async (state, result, setResult, router, socket) => {
|
||||
// If we are already processing an event, or there are no events to process, return.
|
||||
if (result.processing || state.events.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the socket is not ready, return.
|
||||
if (!socket.readyState) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the next event in the queue.
|
||||
setResult({ ...result, processing: true });
|
||||
const events = await applyEvent(
|
||||
state,
|
||||
state.events.shift(),
|
||||
endpoint,
|
||||
router
|
||||
);
|
||||
setResult({
|
||||
processing: true,
|
||||
state: state,
|
||||
events: events,
|
||||
});
|
||||
await applyEvent(state.events.shift(), router, socket);
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to a websocket and set the handlers.
|
||||
* @param socket The socket object to connect.
|
||||
* @param state The state object to apply the deltas to.
|
||||
* @param setResult The function to set the result.
|
||||
* @param endpoint The endpoint to connect to.
|
||||
*/
|
||||
export const connect = async (socket, state, setResult, endpoint) => {
|
||||
socket.current = new WebSocket(endpoint);
|
||||
socket.current.onmessage = function (update) {
|
||||
update = JSON.parse(update.data);
|
||||
applyDelta(state, update.delta);
|
||||
setResult({
|
||||
processing: false,
|
||||
state: state,
|
||||
events: update.events,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an event object.
|
||||
* @param name The name of the event.
|
||||
* @param payload The payload of the event.
|
||||
* @returns The event object.
|
||||
*/
|
||||
export const E = (name, payload) => {
|
||||
return { name, payload };
|
||||
};
|
||||
|
112
pynecone/app.py
112
pynecone/app.py
@ -1,10 +1,8 @@
|
||||
"""The main Pynecone app."""
|
||||
|
||||
import os
|
||||
import re
|
||||
from typing import Any, Callable, Coroutine, Dict, List, Optional, Tuple, Type, Union
|
||||
|
||||
import fastapi
|
||||
from fastapi import FastAPI, WebSocket
|
||||
from fastapi.middleware import cors
|
||||
|
||||
from pynecone import constants, utils
|
||||
@ -32,7 +30,7 @@ class App(Base):
|
||||
stylesheets: List[str] = []
|
||||
|
||||
# The backend API object.
|
||||
api: fastapi.FastAPI = None # type: ignore
|
||||
api: FastAPI = None # type: ignore
|
||||
|
||||
# The state class to use for the app.
|
||||
state: Type[State] = DefaultState
|
||||
@ -62,7 +60,7 @@ class App(Base):
|
||||
self.state_manager.setup(state=self.state)
|
||||
|
||||
# Set up the API.
|
||||
self.api = fastapi.FastAPI()
|
||||
self.api = FastAPI()
|
||||
self.add_cors()
|
||||
self.add_default_endpoints()
|
||||
|
||||
@ -74,7 +72,7 @@ class App(Base):
|
||||
"""
|
||||
return f"<App state={self.state.__name__}>"
|
||||
|
||||
def __call__(self) -> fastapi.FastAPI:
|
||||
def __call__(self) -> FastAPI:
|
||||
"""Run the backend api instance.
|
||||
|
||||
Returns:
|
||||
@ -85,10 +83,10 @@ class App(Base):
|
||||
def add_default_endpoints(self):
|
||||
"""Add the default endpoints."""
|
||||
# To test the server.
|
||||
self.get(str(constants.Endpoint.PING))(_ping)
|
||||
self.api.get(str(constants.Endpoint.PING))(_ping)
|
||||
|
||||
# To make state changes.
|
||||
self.post(str(constants.Endpoint.EVENT))(_event(app=self))
|
||||
self.api.websocket(str(constants.Endpoint.EVENT))(_event(app=self))
|
||||
|
||||
def add_cors(self):
|
||||
"""Add CORS middleware to the app."""
|
||||
@ -99,32 +97,6 @@ class App(Base):
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
def get(self, path: str, *args, **kwargs) -> Callable:
|
||||
"""Register a get request.
|
||||
|
||||
Args:
|
||||
path: The endpoint path to link to the request.
|
||||
*args: Args to pass to the request.
|
||||
**kwargs: Kwargs to pass to the request.
|
||||
|
||||
Returns:
|
||||
A decorator to handle the request.
|
||||
"""
|
||||
return self.api.get(path, *args, **kwargs)
|
||||
|
||||
def post(self, path: str, *args, **kwargs) -> Callable:
|
||||
"""Register a post request.
|
||||
|
||||
Args:
|
||||
path: The endpoint path to link to the request.
|
||||
*args: Args to pass to the request.
|
||||
**kwargs: Kwargs to pass to the request.
|
||||
|
||||
Returns:
|
||||
A decorator to handle the request.
|
||||
"""
|
||||
return self.api.post(path, *args, **kwargs)
|
||||
|
||||
def preprocess(self, state: State, event: Event) -> Optional[Delta]:
|
||||
"""Preprocess the event.
|
||||
|
||||
@ -324,35 +296,65 @@ async def _ping() -> str:
|
||||
return "pong"
|
||||
|
||||
|
||||
def _event(app: App) -> Reducer:
|
||||
"""Create an event reducer to modify the state.
|
||||
def _event(app: App):
|
||||
"""Websocket endpoint for events.
|
||||
|
||||
Args:
|
||||
app: The app to modify the state of.
|
||||
app: The app to add the endpoint to.
|
||||
|
||||
Returns:
|
||||
A handler that takes in an event and modifies the state.
|
||||
The websocket endpoint.
|
||||
"""
|
||||
|
||||
async def process(event: Event) -> StateUpdate:
|
||||
# Get the state for the session.
|
||||
state = app.get_state(event.token)
|
||||
async def ws(websocket: WebSocket):
|
||||
"""Create websocket endpoint.
|
||||
|
||||
# Preprocess the event.
|
||||
pre = app.preprocess(state, event)
|
||||
if pre is not None:
|
||||
return StateUpdate(delta=pre)
|
||||
Args:
|
||||
websocket: The websocket sending events.
|
||||
"""
|
||||
# Accept the connection.
|
||||
await websocket.accept()
|
||||
|
||||
# Apply the event to the state.
|
||||
update = await state.process(event)
|
||||
app.set_state(event.token, state)
|
||||
# Process events until the connection is closed.
|
||||
while True:
|
||||
# Get the event.
|
||||
event = Event.parse_raw(await websocket.receive_text())
|
||||
|
||||
# Postprocess the event.
|
||||
post = app.postprocess(state, event, update.delta)
|
||||
if post is not None:
|
||||
return StateUpdate(delta=post)
|
||||
# Process the event.
|
||||
update = await process(app, event)
|
||||
|
||||
# Return the delta.
|
||||
return update
|
||||
# Send the update.
|
||||
await websocket.send_text(update.json())
|
||||
|
||||
return process
|
||||
return ws
|
||||
|
||||
|
||||
async def process(app: App, event: Event) -> StateUpdate:
|
||||
"""Process an event.
|
||||
|
||||
Args:
|
||||
app: The app to process the event for.
|
||||
event: The event to process.
|
||||
|
||||
Returns:
|
||||
The state update after processing the event.
|
||||
"""
|
||||
# Get the state for the session.
|
||||
state = app.get_state(event.token)
|
||||
|
||||
# Preprocess the event.
|
||||
pre = app.preprocess(state, event)
|
||||
if pre is not None:
|
||||
return StateUpdate(delta=pre)
|
||||
|
||||
# Apply the event to the state.
|
||||
update = await state.process(event)
|
||||
app.set_state(event.token, state)
|
||||
|
||||
# Postprocess the event.
|
||||
post = app.postprocess(state, event, update.delta)
|
||||
if post is not None:
|
||||
return StateUpdate(delta=post)
|
||||
|
||||
# Return the update.
|
||||
return update
|
||||
|
@ -10,9 +10,9 @@ from pynecone.state import State
|
||||
|
||||
# Imports to be included in every Pynecone app.
|
||||
DEFAULT_IMPORTS: ImportDict = {
|
||||
"react": {"useEffect", "useState"},
|
||||
"react": {"useEffect", "useRef", "useState"},
|
||||
"next/router": {"useRouter"},
|
||||
f"/{constants.STATE_PATH}": {"updateState", "E"},
|
||||
f"/{constants.STATE_PATH}": {"connect", "updateState", "E"},
|
||||
"": {"focus-visible/dist/focus-visible"},
|
||||
}
|
||||
|
||||
|
@ -136,12 +136,16 @@ EVENT_FN = join(
|
||||
ROUTER = constants.ROUTER
|
||||
RESULT = constants.RESULT
|
||||
PROCESSING = constants.PROCESSING
|
||||
SOCKET = constants.SOCKET
|
||||
STATE = constants.STATE
|
||||
EVENTS = constants.EVENTS
|
||||
SET_RESULT = format_state_setter(RESULT)
|
||||
USE_EFFECT = join(
|
||||
[
|
||||
"useEffect(() => {{",
|
||||
f" if (!{SOCKET}.current) {{{{",
|
||||
f" connect({SOCKET}, {{state}}, {SET_RESULT}, {EVENT_ENDPOINT})",
|
||||
" }}",
|
||||
" const update = async () => {{",
|
||||
f" if ({RESULT}.{STATE} != null) {{{{",
|
||||
f" {{set_state}}({{{{",
|
||||
@ -154,7 +158,7 @@ USE_EFFECT = join(
|
||||
f" {PROCESSING}: false,",
|
||||
" }})",
|
||||
" }}",
|
||||
f" await updateState({{state}}, {RESULT}, {SET_RESULT}, {EVENT_ENDPOINT}, {ROUTER})",
|
||||
f" await updateState({{state}}, {RESULT}, {SET_RESULT}, {ROUTER}, {SOCKET}.current)",
|
||||
" }}",
|
||||
" update()",
|
||||
"}})",
|
||||
@ -163,3 +167,6 @@ USE_EFFECT = join(
|
||||
|
||||
# Routing
|
||||
ROUTER = f"const {constants.ROUTER} = useRouter()"
|
||||
|
||||
# Sockets.
|
||||
SOCKET = "const socket = useRef(null)"
|
||||
|
@ -94,9 +94,6 @@ def compile_constants() -> str:
|
||||
)
|
||||
|
||||
|
||||
import plotly.graph_objects as go
|
||||
|
||||
|
||||
def compile_state(state: Type[State]) -> str:
|
||||
"""Compile the state of the app.
|
||||
|
||||
@ -126,7 +123,8 @@ def compile_state(state: Type[State]) -> str:
|
||||
initial_state=json.dumps(initial_result),
|
||||
)
|
||||
router = templates.ROUTER
|
||||
return templates.join([synced_state, result, router])
|
||||
socket = templates.SOCKET
|
||||
return templates.join([synced_state, result, router, socket])
|
||||
|
||||
|
||||
def compile_events(state: Type[State]) -> str:
|
||||
|
@ -74,6 +74,8 @@ APP_VAR = "app"
|
||||
API_VAR = "api"
|
||||
# The name of the router variable.
|
||||
ROUTER = "router"
|
||||
# The name of the socket variable.
|
||||
SOCKET = "socket"
|
||||
# The name of the variable to hold API results.
|
||||
RESULT = "result"
|
||||
# The name of the process variable.
|
||||
@ -146,7 +148,17 @@ class Endpoint(Enum):
|
||||
Returns:
|
||||
The full URL for the endpoint.
|
||||
"""
|
||||
# Import here to avoid circular imports.
|
||||
from pynecone import utils
|
||||
|
||||
# Get the API URL from the config.
|
||||
config = utils.get_config()
|
||||
return "".join([config.api_url, str(self)])
|
||||
url = "".join([config.api_url, str(self)])
|
||||
|
||||
# The event endpoint is a websocket.
|
||||
if self == Endpoint.EVENT:
|
||||
# Replace the protocol with ws.
|
||||
url = url.replace("https://", "ws://").replace("http://", "ws://")
|
||||
|
||||
# Return the url.
|
||||
return url
|
||||
|
@ -34,6 +34,7 @@ uvicorn = "^0.20.0"
|
||||
rich = "^12.6.0"
|
||||
redis = "^4.3.5"
|
||||
httpx = "^0.23.1"
|
||||
websockets = "^10.4"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^7.1.2"
|
||||
|
Loading…
Reference in New Issue
Block a user