Compare commits
2 Commits
54162d0072
...
e5deaa51d9
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | e5deaa51d9 | |
Savanni D'Gerinel | 45275be11b |
|
@ -8,7 +8,7 @@ use std::{
|
|||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::types::Message;
|
||||
use crate::types::{AppError, Message, PlayArea};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WebsocketClient {
|
||||
|
@ -19,6 +19,8 @@ struct WebsocketClient {
|
|||
pub struct AppState {
|
||||
pub image_base: PathBuf,
|
||||
|
||||
pub playfield_background: String,
|
||||
|
||||
pub clients: HashMap<String, WebsocketClient>,
|
||||
}
|
||||
|
||||
|
@ -29,6 +31,7 @@ impl Core {
|
|||
pub fn new() -> Self {
|
||||
Self(Arc::new(RwLock::new(AppState {
|
||||
image_base: PathBuf::from("/home/savanni/Pictures"),
|
||||
playfield_background: "moon.jpg".to_owned(),
|
||||
clients: HashMap::new(),
|
||||
})))
|
||||
}
|
||||
|
@ -95,6 +98,15 @@ impl Core {
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn set_playfield_background(&self, path: String) -> Result<(), AppError> {
|
||||
{
|
||||
let mut state = self.0.write().unwrap();
|
||||
state.playfield_background = path.clone();
|
||||
}
|
||||
self.publish(Message::PlayArea(PlayArea{ background_image: path }));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn publish(&self, message: Message) {
|
||||
let state = self.0.read().unwrap();
|
||||
|
||||
|
|
|
@ -98,13 +98,14 @@ pub async fn handle_connect_websocket(
|
|||
ws.on_upgrade(move |socket| {
|
||||
let core = core.clone();
|
||||
async move {
|
||||
let (mut ws_sender, mut ws_recv) = socket.split();
|
||||
let mut receiver = core.connect_client(client_id);
|
||||
let (mut ws_sender, _) = socket.split();
|
||||
let mut receiver = core.connect_client(client_id.clone());
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
let background_image = core.0.read().unwrap().playfield_background.clone();
|
||||
let _ = ws_sender
|
||||
.send(Message::text(
|
||||
serde_json::to_string(&crate::types::Message::Count(0)).unwrap(),
|
||||
serde_json::to_string(&crate::types::Message::PlayArea(PlayArea{ background_image })).unwrap(),
|
||||
))
|
||||
.await;
|
||||
while let Some(msg) = receiver.recv().await {
|
||||
|
@ -113,6 +114,7 @@ pub async fn handle_connect_websocket(
|
|||
.send(Message::text(serde_json::to_string(&msg).unwrap()))
|
||||
.await;
|
||||
}
|
||||
println!("process ended for id {}", client_id);
|
||||
});
|
||||
}
|
||||
})
|
||||
|
|
|
@ -134,14 +134,13 @@ pub async fn main() {
|
|||
move |ws, client_id| handle_connect_websocket(core.clone(), ws, client_id)
|
||||
});
|
||||
|
||||
let route_publish = warp::path!("api" / "v1" / "message")
|
||||
.and(warp::post())
|
||||
let route_set_playfield_bg = warp::path!("api" / "v1" / "playfield" / "bg")
|
||||
.and(warp::put())
|
||||
.and(warp::body::json())
|
||||
.map({
|
||||
let core = core.clone();
|
||||
move |body| {
|
||||
println!("route_publish: {:?}", body);
|
||||
core.publish(body);
|
||||
core.set_playfield_background(body);
|
||||
warp::reply()
|
||||
}
|
||||
});
|
||||
|
@ -152,7 +151,7 @@ pub async fn main() {
|
|||
.or(route_playing_field)
|
||||
.or(route_image)
|
||||
.or(route_available_images)
|
||||
.or(route_publish)
|
||||
.or(route_set_playfield_bg)
|
||||
.recover(handle_rejection);
|
||||
|
||||
let server = warp::serve(filter);
|
||||
|
|
|
@ -12,9 +12,9 @@ pub enum AppError {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Message {
|
||||
Count(u32),
|
||||
// PlayArea(PlayArea),
|
||||
PlayArea(PlayArea),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import { WebsocketPlayingFieldComponent } from './components/PlayingField/PlayingField';
|
||||
import { PlayingFieldComponent} from './components/PlayingField/PlayingField';
|
||||
import { Client } from './client';
|
||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
||||
import { GmPlayingFieldComponent } from './components/GmPlayingField/GmPlayingField';
|
||||
import { WebsocketProvider } from './components/WebsocketProvider';
|
||||
|
||||
interface AppProps {
|
||||
client: Client;
|
||||
|
@ -26,7 +26,7 @@ const App = ({ client }: AppProps) => {
|
|||
},
|
||||
{
|
||||
path: "/",
|
||||
element: websocketUrl ? <WebsocketPlayingFieldComponent websocketUrl={websocketUrl} /> : <div> </div>
|
||||
element: websocketUrl ? <WebsocketProvider websocketUrl={websocketUrl}> <PlayingFieldComponent client={client} /> </WebsocketProvider> : <div> </div>
|
||||
}
|
||||
]);
|
||||
return (
|
||||
|
|
|
@ -1,69 +1,22 @@
|
|||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { Client, PlayingField } from '../../client';
|
||||
import { ThumbnailComponent } from '../Thumbnail/Thumbnail';
|
||||
import React, { useContext } from 'react';
|
||||
import './PlayingField.css';
|
||||
import useWebSocket, { ReadyState } from 'react-use-websocket';
|
||||
import { WebsocketContext } from '../WebsocketProvider';
|
||||
import { Client } from '../../client';
|
||||
|
||||
/*
|
||||
export const PlayingFieldComponent = ({ client }: PlayingFieldProps) => {
|
||||
const [socketUrl, setSocketUrl] = useState<string | undefined>(undefined);
|
||||
const [field, setField] = useState<PlayingField | undefined>(undefined);
|
||||
|
||||
const [images, setImages] = useState<string[]>([]);
|
||||
useEffect(() => {
|
||||
client.availableImages().then((images) => setImages(images));
|
||||
}, [client]);
|
||||
|
||||
const backgroundUrl = field && client.imageUrl(field.backgroundImage);
|
||||
return (<div className="playing-field">
|
||||
<div>
|
||||
{images.map((imageName) => <ThumbnailComponent client={client} imageId={imageName} />)}
|
||||
</div>
|
||||
<div className="playing-field__background"> {backgroundUrl && <img src={backgroundUrl.toString()} alt="playing field" />} </div>
|
||||
<div> Right Panel </div>
|
||||
</div>)
|
||||
}
|
||||
*/
|
||||
|
||||
interface WebsocketPlayingFieldProps {
|
||||
websocketUrl: string;
|
||||
interface PlayingFieldComponentProps {
|
||||
client: Client;
|
||||
}
|
||||
|
||||
export const WebsocketPlayingFieldComponent = ({ websocketUrl }: WebsocketPlayingFieldProps) => {
|
||||
const { lastMessage, readyState } = useWebSocket(websocketUrl);
|
||||
|
||||
useEffect(() => {
|
||||
if (lastMessage !== null) {
|
||||
console.log("last message: ", lastMessage);
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
const connectionStatus = {
|
||||
[ReadyState.CONNECTING]: 'Connecting',
|
||||
[ReadyState.OPEN]: 'Open',
|
||||
[ReadyState.CLOSING]: 'Closing',
|
||||
[ReadyState.CLOSED]: 'Closed',
|
||||
[ReadyState.UNINSTANTIATED]: 'Uninstantiated',
|
||||
}[readyState];
|
||||
|
||||
return <PlayingFieldComponent backgroundUrl={undefined} connectionStatus={connectionStatus} />;
|
||||
}
|
||||
|
||||
interface PlayingFieldProps {
|
||||
backgroundUrl: string | undefined;
|
||||
connectionStatus: string;
|
||||
}
|
||||
|
||||
export const PlayingFieldComponent = ({ backgroundUrl, connectionStatus }: PlayingFieldProps) => {
|
||||
if (backgroundUrl) {
|
||||
export const PlayingFieldComponent = ({ client }: PlayingFieldComponentProps) => {
|
||||
const { backgroundImage } = useContext(WebsocketContext);
|
||||
console.log("backgroundImage", backgroundImage);
|
||||
if (backgroundImage) {
|
||||
return (<div className="playing-field">
|
||||
<div className="playing-field__background"> {backgroundUrl && <img src={backgroundUrl.toString()} alt="playing field" />} </div>
|
||||
<div> {connectionStatus} </div>
|
||||
<div className="playing-field__background"> {backgroundImage && <img src={client.imageUrl(backgroundImage).toString()} alt="playing field" />} </div>
|
||||
</div>)
|
||||
} else {
|
||||
return (<div className="playing-field">
|
||||
<div> </div>
|
||||
<div> {connectionStatus} </div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import React, { createContext, PropsWithChildren, useEffect, useReducer } from "react";
|
||||
import useWebSocket from "react-use-websocket";
|
||||
|
||||
type Message = |
|
||||
{
|
||||
type: "PlayArea";
|
||||
background_image: string;
|
||||
}
|
||||
|
||||
type TabletopState = {
|
||||
backgroundImage: string | undefined;
|
||||
}
|
||||
|
||||
const initialState = () => ({ backgroundImage: undefined });
|
||||
|
||||
export const WebsocketContext = createContext<TabletopState>(initialState());
|
||||
|
||||
interface WebsocketProviderProps {
|
||||
websocketUrl: string;
|
||||
}
|
||||
|
||||
export const WebsocketProvider = ({ websocketUrl, children }: PropsWithChildren<WebsocketProviderProps>) => {
|
||||
const { lastMessage } = useWebSocket(websocketUrl);
|
||||
|
||||
const [state, dispatch] = useReducer(handleMessage, initialState());
|
||||
|
||||
useEffect(() => {
|
||||
if (lastMessage !== null) {
|
||||
const message: Message = JSON.parse(lastMessage.data);
|
||||
dispatch(message);
|
||||
}
|
||||
}, [lastMessage]);
|
||||
|
||||
return (<WebsocketContext.Provider value={state}>
|
||||
{children}
|
||||
</WebsocketContext.Provider>);
|
||||
}
|
||||
|
||||
const handleMessage = (state: TabletopState, message: Message): TabletopState => {
|
||||
switch (message.type) {
|
||||
case "PlayArea": {
|
||||
return {
|
||||
...state,
|
||||
backgroundImage: message.background_image,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue