mirror of
https://github.com/mistricky/codesnap.nvim.git
synced 2024-12-27 03:46:29 +00:00
[Feat] finished codesnap client
This commit is contained in:
parent
f8773e538e
commit
383be3a533
19 changed files with 1756 additions and 114 deletions
|
@ -1,15 +1,17 @@
|
|||
local logger = require("codesnap.utils.logger")
|
||||
local path_utils = require("codesnap.utils.path")
|
||||
local static = require("codesnap.static")
|
||||
|
||||
local client = {
|
||||
job_id = 0,
|
||||
}
|
||||
|
||||
local cwd = static.cwd .. "/snap-server"
|
||||
|
||||
function client:connect()
|
||||
return vim.fn.jobstart({
|
||||
path_utils.back(path_utils.back(debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])")))
|
||||
.. "/snap-server/target/debug/snap-server",
|
||||
cwd .. "/target/release/snap-server",
|
||||
}, {
|
||||
cwd = cwd,
|
||||
stderr_buffered = true,
|
||||
rpc = true,
|
||||
on_stderr = function(_, err)
|
||||
|
|
|
@ -3,15 +3,14 @@ local static = require("codesnap.static")
|
|||
local client = require("codesnap.client")
|
||||
local visual_utils = require("codesnap.utils.visual")
|
||||
|
||||
local main = {}
|
||||
local main = {
|
||||
cwd = static.cwd,
|
||||
preview_switch = static.preview_switch,
|
||||
}
|
||||
|
||||
function main.setup(config)
|
||||
static.config = table_utils.merge(static.config, config == nil and {} or config)
|
||||
|
||||
print(vim.inspect(static.config))
|
||||
print(table_utils.serialize_json(static.config))
|
||||
print()
|
||||
|
||||
if static.config.auto_load then
|
||||
client:start()
|
||||
end
|
||||
|
@ -23,4 +22,12 @@ function main.preview_code()
|
|||
client:send("preview_code", { content = visual_utils.get_selected_text(), language = vim.bo.filetype })
|
||||
end
|
||||
|
||||
function main.open_preview()
|
||||
client:send("open_preview")
|
||||
end
|
||||
|
||||
function main.stop_client()
|
||||
client:stop()
|
||||
end
|
||||
|
||||
return main
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
local path_utils = require("codesnap.utils.path")
|
||||
|
||||
return {
|
||||
config = {
|
||||
breadcrumbs = true,
|
||||
column_number = true,
|
||||
mac_window_bar = true,
|
||||
opacity = true,
|
||||
watermark = "CodeSnap.nvim",
|
||||
auto_load = true,
|
||||
},
|
||||
cwd = path_utils.back(path_utils.back(debug.getinfo(1, "S").source:sub(2):match("(.*[/\\])"))),
|
||||
preview_switch = true,
|
||||
}
|
||||
|
|
20
plugin/build.lua
Normal file
20
plugin/build.lua
Normal file
|
@ -0,0 +1,20 @@
|
|||
local codesnap = require("codesnap")
|
||||
local snap_client_cwd = codesnap.cwd .. "/snap-client"
|
||||
local snap_server_cwd = codesnap.cwd .. "/snap-server"
|
||||
|
||||
-- Build preview client
|
||||
os.execute(
|
||||
"cd "
|
||||
.. snap_client_cwd
|
||||
.. " && "
|
||||
.. "npm i "
|
||||
.. " && "
|
||||
.. "npm run build"
|
||||
.. " && "
|
||||
.. "mv ./build "
|
||||
.. snap_server_cwd
|
||||
.. "/public"
|
||||
)
|
||||
|
||||
-- Build server
|
||||
os.execute("cd " .. snap_server_cwd .. " && " .. "cargo build --release")
|
|
@ -1,19 +1,19 @@
|
|||
local codesnap = require("codesnap")
|
||||
local static = require("codesnap.static")
|
||||
local client = require("codesnap.client")
|
||||
-- local client = require("codesnap.client")
|
||||
|
||||
-- snap code
|
||||
vim.api.nvim_create_user_command("CodeSnap", function() end, {})
|
||||
-- vim.api.nvim_create_user_command("CodeSnap", function()
|
||||
-- client:send("copy")
|
||||
-- end, {})
|
||||
|
||||
vim.api.nvim_create_user_command("CodeSnapPreviewOn", function() end, {})
|
||||
|
||||
vim.api.nvim_create_user_command("CodeSnapPreviewOff", function() end, {})
|
||||
vim.api.nvim_create_user_command("CodeSnapPreviewOn", function()
|
||||
codesnap.open_preview()
|
||||
end, {})
|
||||
|
||||
vim.api.nvim_create_autocmd({ "CursorMoved" }, {
|
||||
callback = function()
|
||||
local mode = vim.api.nvim_get_mode().mode
|
||||
|
||||
if mode ~= "v" or not static.preview_switch then
|
||||
if mode ~= "v" or not codesnap.preview_switch then
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -24,6 +24,6 @@ vim.api.nvim_create_autocmd({ "CursorMoved" }, {
|
|||
vim.api.nvim_create_autocmd({ "VimLeavePre" }, {
|
||||
pattern = "*",
|
||||
callback = function()
|
||||
client:stop()
|
||||
codesnap.stop_client()
|
||||
end,
|
||||
})
|
||||
|
|
|
@ -1,34 +1,37 @@
|
|||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import useWebSocket, { ReadyState } from "react-use-websocket";
|
||||
import { ControlBar, Editor, Frame, Panel } from "./components";
|
||||
import { useConfig, useEvent } from "./hooks";
|
||||
import { toPng, toJpeg, toBlob, toPixelData, toSvg } from "html-to-image";
|
||||
import { toPng, toBlob } from "html-to-image";
|
||||
import download from "downloadjs";
|
||||
|
||||
const CODE_EMPTY_PLACEHOLDER = `print "Hello, CodeSnap.nvim!"`;
|
||||
|
||||
function App() {
|
||||
const [socketUrl] = useState("ws://127.0.0.1:8080/ws");
|
||||
const [socketUrl] = useState(`ws://${window.location.host}/ws`);
|
||||
const { sendMessage, lastMessage, readyState } = useWebSocket(socketUrl);
|
||||
const event = useEvent(lastMessage);
|
||||
const config = useConfig(event?.config_setup);
|
||||
const frameRef = useRef<HTMLDivElement | null>(null);
|
||||
const [isCopyButtonDisabled, setIsCopyButtonDisabled] = useState(false);
|
||||
|
||||
const handleCopyButtonClick = useCallback(async () => {
|
||||
if (!frameRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsCopyButtonDisabled(true);
|
||||
|
||||
try {
|
||||
const blob = await toBlob(frameRef.current);
|
||||
const clipboardItem = new ClipboardItem({ "image/png": blob! });
|
||||
|
||||
navigator.clipboard.write([clipboardItem]);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
setIsCopyButtonDisabled(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleExportClick = useCallback(async () => {
|
||||
|
@ -41,6 +44,24 @@ function App() {
|
|||
download(dataURL, "codesnap.png");
|
||||
}, []);
|
||||
|
||||
const notifyCopyCommand = useCallback(async () => {
|
||||
if (!frameRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataURL = await toPng(frameRef.current);
|
||||
|
||||
sendMessage(dataURL);
|
||||
}, [sendMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (readyState !== ReadyState.OPEN || !event?.copy) {
|
||||
return;
|
||||
}
|
||||
|
||||
notifyCopyCommand();
|
||||
}, [event, readyState, notifyCopyCommand]);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col items-center bg-deep-gray">
|
||||
<p className="rainbow-text text-4xl font-extrabold mt-20">
|
||||
|
@ -48,11 +69,12 @@ function App() {
|
|||
</p>
|
||||
<Panel>
|
||||
<ControlBar
|
||||
isCopyButtonDisabled={isCopyButtonDisabled}
|
||||
onExportClick={handleExportClick}
|
||||
onCopyClick={handleCopyButtonClick}
|
||||
readyState={readyState}
|
||||
/>
|
||||
<div className="rounded-xl overflow-hidden">
|
||||
<div id="frame" className="rounded-xl overflow-hidden">
|
||||
<Frame ref={frameRef} watermark={config?.watermark}>
|
||||
<Editor
|
||||
language={event?.code?.language}
|
||||
|
|
53
snap-client/src/components/control-bar/connection-status.tsx
Normal file
53
snap-client/src/components/control-bar/connection-status.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { useMemo } from "react";
|
||||
import { ReadyState } from "react-use-websocket";
|
||||
|
||||
interface ConnectionStatusProps {
|
||||
readyState: ReadyState;
|
||||
}
|
||||
|
||||
const CONNECTION_STATUS_MAP = {
|
||||
[ReadyState.CONNECTING]: {
|
||||
text: "Connecting",
|
||||
color: "#fdcb6e",
|
||||
},
|
||||
[ReadyState.OPEN]: {
|
||||
text: "Connected",
|
||||
color: "#00b894",
|
||||
},
|
||||
[ReadyState.CLOSING]: {
|
||||
text: "Closing",
|
||||
color: "#fab1a0",
|
||||
},
|
||||
[ReadyState.CLOSED]: {
|
||||
text: "Closed",
|
||||
color: "#636e72",
|
||||
},
|
||||
[ReadyState.UNINSTANTIATED]: {
|
||||
text: "Uninstantiated",
|
||||
color: "#2d3436",
|
||||
},
|
||||
} as const;
|
||||
|
||||
const UNKNOWN_STATE = { text: "Unknown", color: "#a29bfe" };
|
||||
|
||||
export const ConnectionStatus = ({ readyState }: ConnectionStatusProps) => {
|
||||
const parsedState = useMemo(
|
||||
() => CONNECTION_STATUS_MAP[readyState] ?? UNKNOWN_STATE,
|
||||
[readyState],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-grow flex-row items-center mr-1 ml-2">
|
||||
<div
|
||||
className="flex w-5 h-5 mr-2 justify-center items-center rounded-full"
|
||||
style={{ backgroundColor: `${parsedState.color}50` }}
|
||||
>
|
||||
<div
|
||||
className="w-3 h-3 rounded-full"
|
||||
style={{ backgroundColor: parsedState.color }}
|
||||
></div>
|
||||
</div>
|
||||
{parsedState.text}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,6 +2,7 @@ import { ConnectionStatus } from "./connection-status";
|
|||
import { ReadyState } from "react-use-websocket";
|
||||
|
||||
interface ControlBarProps {
|
||||
isCopyButtonDisabled: boolean;
|
||||
onCopyClick(): void;
|
||||
onExportClick(): void;
|
||||
readyState: ReadyState;
|
||||
|
@ -10,13 +11,11 @@ interface ControlBarProps {
|
|||
export const ControlBar = ({
|
||||
onCopyClick,
|
||||
onExportClick,
|
||||
isCopyButtonDisabled,
|
||||
readyState,
|
||||
}: ControlBarProps) => {
|
||||
return (
|
||||
<div
|
||||
className="bg-neutral rounded-xl mb-2 p-1 flex flex-row items-center"
|
||||
onClick={onCopyClick}
|
||||
>
|
||||
<div className="bg-neutral rounded-xl mb-2 p-1 flex flex-row items-center">
|
||||
<ConnectionStatus readyState={readyState} />
|
||||
<div className="flex flex-row items-center">
|
||||
{/*
|
||||
|
@ -44,7 +43,11 @@ export const ControlBar = ({
|
|||
<path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button className="btn">
|
||||
<button
|
||||
onClick={onCopyClick}
|
||||
id="copy"
|
||||
className={`btn ${isCopyButtonDisabled && "btn-disabled"}`}
|
||||
>
|
||||
Copy
|
||||
<svg
|
||||
className="fill-neutral-content"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { useLocalStorage } from "./use-storage";
|
||||
|
||||
export interface Config {
|
||||
breadcrumbs: boolean;
|
||||
column_number: boolean;
|
||||
mac_window_bar: boolean;
|
||||
opacity: boolean;
|
||||
watermark: string;
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Config } from "./use-config";
|
|||
export enum EventType {
|
||||
CONFIG_SETUP = "config_setup",
|
||||
CODE = "code",
|
||||
COPY = "copy",
|
||||
}
|
||||
|
||||
type CodeMessage = {
|
||||
|
@ -14,6 +15,7 @@ type CodeMessage = {
|
|||
type ParsedConfig = {
|
||||
[EventType.CODE]: CodeMessage;
|
||||
[EventType.CONFIG_SETUP]: Config;
|
||||
[EventType.COPY]: undefined;
|
||||
};
|
||||
|
||||
export const useEvent = (
|
||||
|
|
1519
snap-server/Cargo.lock
generated
1519
snap-server/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -3,13 +3,17 @@ name = "snap-server"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix = "0.13.3"
|
||||
actix-files = "0.6.5"
|
||||
actix-web = "4.5.1"
|
||||
actix-web-actors = "4.3.0"
|
||||
arboard = "3.3.1"
|
||||
headless_chrome = "1.0.9"
|
||||
image = "0.24.8"
|
||||
image-base64 = "0.1.0"
|
||||
neovim-lib = "0.6.1"
|
||||
rand = "0.8.5"
|
||||
serde = {version = "1.0.196", features = ["derive", "serde_derive", "std"]}
|
||||
serde_json = "1.0.113"
|
||||
webbrowser = "0.8.12"
|
||||
|
|
21
snap-server/src/clipboard.rs
Normal file
21
snap-server/src/clipboard.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use arboard::Clipboard;
|
||||
use arboard::ImageData;
|
||||
use image::load_from_memory;
|
||||
|
||||
pub fn copy_base64_image_into_clipboard(base64_image: String) {
|
||||
copy_memory_image_into_clipboard(&image_base64::from_base64(base64_image))
|
||||
}
|
||||
|
||||
pub fn copy_memory_image_into_clipboard(buffer: &Vec<u8>) {
|
||||
let dynamic_image = load_from_memory(buffer).unwrap();
|
||||
|
||||
let image = ImageData {
|
||||
width: dynamic_image.width() as usize,
|
||||
height: dynamic_image.height() as usize,
|
||||
bytes: dynamic_image.as_bytes().into(),
|
||||
};
|
||||
|
||||
let mut clipboard = Clipboard::new().unwrap();
|
||||
|
||||
clipboard.set_image(image).unwrap();
|
||||
}
|
|
@ -3,15 +3,15 @@ pub mod config;
|
|||
pub mod messages;
|
||||
pub mod neovim;
|
||||
|
||||
use actix::{Actor, Addr, AsyncContext, Context};
|
||||
use actix::{Actor, Addr, Context};
|
||||
use arguments::parse_string_first;
|
||||
pub use config::Config;
|
||||
use headless_chrome::{protocol::cdp::Page, Browser, Tab};
|
||||
pub use messages::Message;
|
||||
use neovim::Neovim;
|
||||
use serde_json::{json, Value};
|
||||
use std::{
|
||||
any::Any,
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
|
@ -23,6 +23,7 @@ use crate::{
|
|||
pub struct EventHandler {
|
||||
neovim: Arc<Mutex<Neovim>>,
|
||||
server: Arc<Addr<Server>>,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
impl Actor for EventHandler {
|
||||
|
@ -34,17 +35,47 @@ impl Actor for EventHandler {
|
|||
}
|
||||
|
||||
impl EventHandler {
|
||||
pub fn new(neovim: Arc<Mutex<Neovim>>, server: Arc<Addr<Server>>) -> EventHandler {
|
||||
EventHandler { neovim, server }
|
||||
pub fn new(neovim: Arc<Mutex<Neovim>>, server: Arc<Addr<Server>>, port: u16) -> EventHandler {
|
||||
EventHandler {
|
||||
neovim,
|
||||
server,
|
||||
port,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_browser(&self) -> Result<Arc<Tab>, Box<dyn Error>> {
|
||||
let browser = Browser::default()?;
|
||||
|
||||
let tab = browser.new_tab()?;
|
||||
|
||||
tab.navigate_to(format!("http://localhost:{}", self.port).as_str())?;
|
||||
|
||||
Ok(tab)
|
||||
}
|
||||
|
||||
pub fn start_listen(&mut self) {
|
||||
let receiver = self.neovim.lock().unwrap().create_receiver();
|
||||
|
||||
for (event_name, values) in receiver {
|
||||
self.neovim.lock().unwrap().print(&event_name);
|
||||
// self.neovim.lock().unwrap().print(&event_name);
|
||||
|
||||
match Message::from(event_name.clone()) {
|
||||
Message::OpenPreview => {
|
||||
let _ = webbrowser::open(format!("http://localhost:{}", self.port).as_str());
|
||||
}
|
||||
Message::Copy => {
|
||||
// let _png_data = tab
|
||||
// .wait_for_element("#frame")
|
||||
// .unwrap()
|
||||
// .capture_screenshot(Page::CaptureScreenshotFormatOption::Png)
|
||||
// .unwrap();
|
||||
//
|
||||
// std::fs::write("hello_world.png", _png_data).unwrap();
|
||||
|
||||
// self.server.do_send(ClientMessage {
|
||||
// msg: Event::new("copy", json!("{}")).into(),
|
||||
// })
|
||||
}
|
||||
Message::PreviewCode => self.server.do_send(ClientMessage {
|
||||
msg: Event::new(
|
||||
"code",
|
||||
|
|
|
@ -2,8 +2,6 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
#[derive(Serialize, Debug, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
breadcrumbs: bool,
|
||||
column_number: bool,
|
||||
mac_window_bar: bool,
|
||||
opacity: bool,
|
||||
watermark: Option<String>,
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
pub enum Message {
|
||||
PreviewCode,
|
||||
ConfigSetup,
|
||||
Copy,
|
||||
OpenPreview,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
@ -10,6 +12,8 @@ impl Eq for Message {}
|
|||
impl From<String> for Message {
|
||||
fn from(value: String) -> Self {
|
||||
match value.as_str() {
|
||||
"open_preview" => Message::OpenPreview,
|
||||
"copy" => Message::Copy,
|
||||
"preview_code" => Message::PreviewCode,
|
||||
"config_setup" => Message::ConfigSetup,
|
||||
_ => Message::Unknown,
|
||||
|
|
|
@ -1,18 +1,27 @@
|
|||
mod clipboard;
|
||||
mod event;
|
||||
mod event_handler;
|
||||
mod port;
|
||||
mod server;
|
||||
mod session;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{
|
||||
net::TcpListener,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use actix::{Actor, Addr, Arbiter, StreamHandler};
|
||||
use actix::{Actor, Addr, Arbiter};
|
||||
use actix_files::{Files, NamedFile};
|
||||
use actix_web::{
|
||||
web::{self, Data, Payload},
|
||||
App, Error, HttpRequest, HttpResponse, HttpServer,
|
||||
App, Error, HttpRequest, HttpResponse, HttpServer, Responder,
|
||||
};
|
||||
use actix_web_actors::ws;
|
||||
use event_handler::{neovim::Neovim, Config};
|
||||
use event_handler::{EventHandler, Message};
|
||||
use clipboard::copy_memory_image_into_clipboard;
|
||||
use event_handler::neovim::Neovim;
|
||||
use event_handler::EventHandler;
|
||||
use headless_chrome::{protocol::cdp::Page, Browser, Tab};
|
||||
use port::get_available_port;
|
||||
use server::Server;
|
||||
use session::Session;
|
||||
|
||||
|
@ -24,40 +33,33 @@ async fn index(
|
|||
ws::start(Session::new(server), &req, stream)
|
||||
}
|
||||
|
||||
async fn root() -> impl Responder {
|
||||
NamedFile::open_async("./public/index.html").await.unwrap()
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let neovim = Arc::new(Mutex::new(Neovim::new()));
|
||||
let server = Arc::new(Server::new(neovim.clone()).start());
|
||||
let cloned_server = Arc::clone(&server);
|
||||
let cloned_neovim = neovim.clone();
|
||||
let available_port = get_available_port().unwrap();
|
||||
|
||||
Arbiter::new().spawn(async {
|
||||
EventHandler::new(neovim, cloned_server).start();
|
||||
Arbiter::new().spawn(async move {
|
||||
EventHandler::new(cloned_neovim.clone(), cloned_server.clone(), available_port).start();
|
||||
});
|
||||
|
||||
HttpServer::new(move || {
|
||||
let _ = HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(web::Data::new(server.clone()))
|
||||
.route("/ws", web::get().to(index))
|
||||
.service(web::resource("/").to(root))
|
||||
.service(Files::new("/public", "./public"))
|
||||
.service(Files::new("/static", "./public/static"))
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.bind(("127.0.0.1", available_port))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
.await;
|
||||
|
||||
// fn main() {
|
||||
// let data = r#"
|
||||
// {
|
||||
// "breadcrumbs":true,
|
||||
// "watermark":"CodeSnap.nvim",
|
||||
// "mac_window_bar":true,
|
||||
// "column_number":true,
|
||||
// "auto_load":true,
|
||||
// "background":{
|
||||
// "grandient":true
|
||||
// }
|
||||
// }"#;
|
||||
//
|
||||
// let config: Config = serde_json::from_str(data).unwrap();
|
||||
//
|
||||
// println!("{:?}", config)
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
|
12
snap-server/src/port.rs
Normal file
12
snap-server/src/port.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use std::net::TcpListener;
|
||||
|
||||
pub fn get_available_port() -> Option<u16> {
|
||||
(8000..10000).find(|port| port_is_available(*port))
|
||||
}
|
||||
|
||||
fn port_is_available(port: u16) -> bool {
|
||||
match TcpListener::bind(("127.0.0.1", port)) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use crate::server::{ClientMessage, Connect, Disconnect, Server, ServerMessage};
|
||||
use crate::server::{Connect, Disconnect, Server, ServerMessage};
|
||||
use actix::{
|
||||
dev::ContextFutureSpawner, fut, Actor, ActorContext, ActorFutureExt, Addr, AsyncContext,
|
||||
Handler, Running, StreamHandler, WrapFuture,
|
||||
|
@ -13,6 +13,10 @@ use std::{
|
|||
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||||
const CLIENT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
fn is_valid_base64_image(base64_image: &str) -> bool {
|
||||
base64_image.starts_with("data:image/png;base64")
|
||||
}
|
||||
|
||||
pub struct Session {
|
||||
id: usize,
|
||||
server: Arc<Addr<Server>>,
|
||||
|
@ -30,7 +34,6 @@ impl Session {
|
|||
|
||||
fn heartbeat(&self, ctx: &mut ws::WebsocketContext<Self>) {
|
||||
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
|
||||
// check client heartbeats
|
||||
if Instant::now().duration_since(act.heartbeat) > CLIENT_TIMEOUT {
|
||||
act.server.do_send(Disconnect { id: act.id });
|
||||
|
||||
|
@ -89,9 +92,13 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Session {
|
|||
self.heartbeat = Instant::now();
|
||||
}
|
||||
Ok(ws::Message::Text(text)) => {
|
||||
self.server.do_send(ClientMessage {
|
||||
msg: "aaaaa".to_string(),
|
||||
});
|
||||
// let image = text.to_string();
|
||||
|
||||
// if !is_valid_base64_image(&image) {
|
||||
ctx.text(text)
|
||||
// }
|
||||
|
||||
// copy_base64_image_into_clipboard(image);
|
||||
}
|
||||
Ok(ws::Message::Binary(bin)) => ctx.binary(bin),
|
||||
_ => (),
|
||||
|
|
Loading…
Reference in a new issue