mirror of
https://github.com/ekzhang/rustpad.git
synced 2021-06-08 23:07:11 +03:00
Implement multiplexing for editors
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -118,6 +118,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
@@ -843,6 +853,7 @@ name = "rustpad-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dashmap",
|
||||
"dotenv",
|
||||
"futures",
|
||||
"log",
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.40"
|
||||
dashmap = "4.0.2"
|
||||
dotenv = "0.15.0"
|
||||
futures = "0.3.15"
|
||||
log = "0.4.14"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use rustpad::Rustpad;
|
||||
use warp::{filters::BoxedFilter, ws::Ws, Filter, Reply};
|
||||
|
||||
@@ -24,21 +25,34 @@ fn frontend() -> BoxedFilter<(impl Reply,)> {
|
||||
|
||||
/// Construct backend routes, including WebSocket handlers.
|
||||
fn backend() -> BoxedFilter<(impl Reply,)> {
|
||||
let rustpad = Arc::new(Rustpad::new());
|
||||
let rustpad = warp::any().map(move || Arc::clone(&rustpad));
|
||||
let rustpad_map: Arc<DashMap<String, Arc<Rustpad>>> = Default::default();
|
||||
let rustpad_map = warp::any().map(move || Arc::clone(&rustpad_map));
|
||||
|
||||
let socket = warp::path("socket")
|
||||
.and(warp::path::param())
|
||||
.and(warp::path::end())
|
||||
.and(warp::ws())
|
||||
.and(rustpad.clone())
|
||||
.map(|ws: Ws, rustpad: Arc<Rustpad>| {
|
||||
ws.on_upgrade(move |socket| async move { rustpad.on_connection(socket).await })
|
||||
});
|
||||
.and(rustpad_map.clone())
|
||||
.map(
|
||||
|id: String, ws: Ws, rustpad_map: Arc<DashMap<String, Arc<Rustpad>>>| {
|
||||
let rustpad = rustpad_map.entry(id).or_default();
|
||||
let rustpad = Arc::clone(rustpad.value());
|
||||
ws.on_upgrade(move |socket| async move { rustpad.on_connection(socket).await })
|
||||
},
|
||||
);
|
||||
|
||||
let text = warp::path("text")
|
||||
.and(warp::path::param())
|
||||
.and(warp::path::end())
|
||||
.and(rustpad.clone())
|
||||
.map(|rustpad: Arc<Rustpad>| rustpad.text());
|
||||
.and(rustpad_map.clone())
|
||||
.map(
|
||||
|id: String, rustpad_map: Arc<DashMap<String, Arc<Rustpad>>>| {
|
||||
rustpad_map
|
||||
.get(&id)
|
||||
.map(|rustpad| rustpad.text())
|
||||
.unwrap_or_default()
|
||||
},
|
||||
);
|
||||
|
||||
socket.or(text).boxed()
|
||||
}
|
||||
|
||||
@@ -63,11 +63,6 @@ impl From<ServerMsg> for Message {
|
||||
}
|
||||
|
||||
impl Rustpad {
|
||||
/// Construct a new, empty Rustpad object.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Handle a connection from a WebSocket.
|
||||
pub async fn on_connection(&self, socket: WebSocket) {
|
||||
let id = self.count.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
@@ -30,7 +30,7 @@ impl JsonSocket {
|
||||
/// Connect a new test client WebSocket.
|
||||
async fn connect(filter: &BoxedFilter<(impl Reply + 'static,)>) -> Result<JsonSocket> {
|
||||
let client = warp::test::ws()
|
||||
.path("/api/socket")
|
||||
.path("/api/socket/foobar")
|
||||
.handshake(filter.clone())
|
||||
.await?;
|
||||
Ok(JsonSocket(client))
|
||||
@@ -38,7 +38,10 @@ async fn connect(filter: &BoxedFilter<(impl Reply + 'static,)>) -> Result<JsonSo
|
||||
|
||||
/// Check the text route.
|
||||
async fn expect_text(filter: &BoxedFilter<(impl Reply + 'static,)>, text: &str) {
|
||||
let resp = warp::test::request().path("/api/text").reply(filter).await;
|
||||
let resp = warp::test::request()
|
||||
.path("/api/text/foobar")
|
||||
.reply(filter)
|
||||
.await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
assert_eq!(resp.body(), text);
|
||||
}
|
||||
|
||||
11
src/App.tsx
11
src/App.tsx
@@ -25,10 +25,11 @@ import languages from "./languages.json";
|
||||
|
||||
set_panic_hook();
|
||||
|
||||
const WS_URI =
|
||||
const id = window.location.hash.slice(1);
|
||||
const wsUri =
|
||||
(window.location.origin.startsWith("https") ? "wss://" : "ws://") +
|
||||
window.location.host +
|
||||
"/api/socket";
|
||||
`/api/socket/${id}`;
|
||||
|
||||
function App() {
|
||||
const toast = useToast();
|
||||
@@ -42,7 +43,7 @@ function App() {
|
||||
model.setValue("");
|
||||
model.setEOL(0); // LF
|
||||
const rustpad = new Rustpad({
|
||||
uri: WS_URI,
|
||||
uri: wsUri,
|
||||
editor,
|
||||
onConnected: () => setConnected(true),
|
||||
onDisconnected: () => setConnected(false),
|
||||
@@ -52,7 +53,7 @@ function App() {
|
||||
}, [editor]);
|
||||
|
||||
async function handleCopy() {
|
||||
await navigator.clipboard.writeText(`${window.location.origin}/`);
|
||||
await navigator.clipboard.writeText(`${window.location.origin}/#${id}`);
|
||||
toast({
|
||||
title: "Copied!",
|
||||
description: "Link copied to clipboard",
|
||||
@@ -114,7 +115,7 @@ function App() {
|
||||
pr="3.5rem"
|
||||
variant="outline"
|
||||
bgColor="white"
|
||||
value={`${window.location.origin}/`}
|
||||
value={`${window.location.origin}/#${id}`}
|
||||
/>
|
||||
<InputRightElement width="3.5rem">
|
||||
<Button h="1.4rem" size="xs" onClick={handleCopy}>
|
||||
|
||||
@@ -3,6 +3,17 @@ import ReactDOM from "react-dom";
|
||||
import { ChakraProvider } from "@chakra-ui/react";
|
||||
import "./index.css";
|
||||
|
||||
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
const idLen = 6;
|
||||
|
||||
if (!window.location.hash) {
|
||||
let id = "";
|
||||
for (let i = 0; i < idLen; i++) {
|
||||
id += chars[Math.floor(Math.random() * chars.length)];
|
||||
}
|
||||
window.location.hash = id;
|
||||
}
|
||||
|
||||
// An asynchronous entry point is needed to load WebAssembly files.
|
||||
import("./App").then(({ default: App }) => {
|
||||
ReactDOM.render(
|
||||
|
||||
Reference in New Issue
Block a user