Almost done, only image left

This commit is contained in:
Piyush मिश्रः 2021-02-20 13:54:52 +05:30
parent e03a51dc23
commit 6a26ff18aa
7 changed files with 580 additions and 288 deletions

View File

@ -4,15 +4,14 @@ use std::{collections::HashMap, vec};
use actix::prelude::*;
use actix_broker::BrokerSubscribe;
use vecmap::VecMap;
use ms::Resp;
use crate::{errors, ws_sansad, messages as ms};
use crate::{ws_sansad, messages as ms};
#[allow(dead_code)]
pub struct ChatPinnd {
grih: HashMap<String, Grih>, // kunjika, Grih
vyaktigat_waitlist: Vec<VyaktiWatchlist>,
non_connected_vyakti: VecMap<String, Vyakti>, // kunjika, vayakti
}
pub struct Grih {
@ -24,7 +23,7 @@ pub struct Loog {
addr: Addr<ws_sansad::WsSansad>,
kunjika: String,
name: String,
_tags: Vec<String>
tags: Option<Vec<String>>
}
#[derive(Debug, Clone)]
@ -34,6 +33,8 @@ pub struct Vyakti {
}
pub struct VyaktiWatchlist {
kunjika: String,
name: String,
tags: Vec<String>,
addr: Addr<ws_sansad::WsSansad>
}
@ -43,87 +44,65 @@ impl Actor for ChatPinnd {
fn started(&mut self, ctx: &mut Self::Context) {
// for actix broker
self.subscribe_system_async::<ms::SendText>(ctx);
self.subscribe_system_async::<ms::SendStatus>(ctx);
self.subscribe_system_async::<ms::LeaveUser>(ctx);
}
}
// Set information of user
impl Handler<ms::SetInfoVyakti> for ChatPinnd {
type Result = Option<String>;
fn handle(&mut self, msg: ms::SetInfoVyakti, _: &mut Self::Context) -> Self::Result {
// check if vayakti info is not modified and do key exist
if !msg.modify {
if self.non_connected_vyakti.key_exist(&msg.kunjika) {
return Some("Kunjika Exists".to_owned());
}
if let Some(_) = self.grih.iter().position(|a| {
match a.1.loog.iter().position(|b| {
b.kunjika == msg.kunjika
}) {
Some(_) => true,
None => false
}
}) {
return Some("Kunjika Exists".to_owned());
}
}
// change value
self.non_connected_vyakti.insert(msg.kunjika, Vyakti {
name: msg.name,
tags: msg.tags
});
None
}
}
/// Join grih
impl Handler<ms::JoinGrih> for ChatPinnd {
type Result = Result<(), errors::GrihFullError>;
type Result = Resp;
fn handle(&mut self, msg: ms::JoinGrih, _: &mut Self::Context) -> Self::Result {
match self.grih.get_mut(&msg.grih_kunjika) { // check if group exist
// check if user exist
if let Some(_) = self.vyaktigat_waitlist.iter().position(|vk| vk.kunjika == msg.kunjika) {
println!("got in watchlist");
return Resp::Err("Kunjika already exist".to_owned());
}
if let Some(_) = self.grih.iter().position(|(_,g)| {
match g.loog.iter().position(|a| {println!("Got in grih {:?} {:?}", a.kunjika, msg.kunjika); a.kunjika == msg.kunjika}) {
Some(_) => true,
None => false
}
}) {
return Resp::Err("Kunjika already exist".to_owned());
}
// check if grih exist and add user
match self.grih.get_mut(&msg.grih_kunjika) {
Some(grih) =>{ // exist
// check if group have no space left
// check if grih have no space left
if let Some(n) = grih.length {
if grih.loog.len() >= n {
return Err(errors::GrihFullError);
return Resp::Err("Grih have no space".to_owned());
}
}
let vayakti = self.non_connected_vyakti.get(&msg.kunjika).unwrap();
let name = vayakti.name.to_owned();
let tags = vayakti.tags.to_owned();
let name_tmp = name.clone();
let kunjika_tmp = msg.kunjika.clone();
grih.loog.iter().for_each(move |a: &Loog| {
grih.loog.iter().for_each(|a: &Loog| {
a.addr.do_send(ms::WsConnected {
name: name_tmp.clone(),
kunjika: kunjika_tmp.clone()
name: msg.name.to_owned(),
kunjika: msg.kunjika.to_owned()
})
});
self.non_connected_vyakti.remove(&msg.kunjika).unwrap_or(());
grih.loog.push(Loog::new(msg.addr, msg.kunjika,name,tags));
grih.loog.push(Loog::new(msg.addr, msg.kunjika,msg.name, None));
}, None => { // don't exist
// add group and notify
let vayakti = self.non_connected_vyakti.get(&msg.kunjika).unwrap();
// add grih and notify
msg.addr.do_send(ms::WsConnected {
name: vayakti.name.clone(),
kunjika: msg.kunjika.clone()
name: msg.name.to_owned(),
kunjika: msg.kunjika.to_owned()
});
self.grih.insert(msg.grih_kunjika, Grih {
length: msg.length,
loog: vec![Loog::new(msg.addr,msg.kunjika.clone(),vayakti.name.clone(),vayakti.tags.clone())]
loog: vec![Loog::new(msg.addr,msg.kunjika,msg.name, None)]
});
self.non_connected_vyakti.remove(&msg.kunjika).unwrap_or(());
}
}
Ok(())
Resp::Ok
}
}
@ -132,50 +111,165 @@ impl Handler<ms::JoinGrih> for ChatPinnd {
/// Check if watchlist is empty, if yes add the kunjika andaddr to watchlist
/// if watchlist have people get 0th person an connect it
impl Handler<ms::JoinRandom> for ChatPinnd {
type Result = Option<()>;
type Result = Resp;
fn handle(&mut self, msg: ms::JoinRandom, _: &mut Self::Context) -> Self::Result {
// check if user exist
if let Some(_) = self.vyaktigat_waitlist.iter().position(|vk| vk.kunjika == msg.kunjika) {
return Resp::Err("Kunjika already exist".to_owned());
}
if let Some(_) = self.grih.iter().position(|(_,g)| {
match g.loog.iter().position(|a| a.kunjika == msg.kunjika) {
Some(_) => true,
None => false
}
}) {
return Resp::Err("Kunjika already exist".to_owned());
}
// Check if watch list is empty
if self.vyaktigat_waitlist.len() == 0 {
self.vyaktigat_waitlist.push(VyaktiWatchlist {
kunjika: msg.kunjika,
addr: msg.addr
addr: msg.addr,
name: msg.name,
tags: msg.tags
});
return None;
return Resp::None;
}
// connect 0th person
let vayakti_watchlist = self.vyaktigat_waitlist.remove(0);
let vayakti1_name: String;
let vayakti2_name: String;
let group_kunjika: String;
{
let vayakti1 = self.non_connected_vyakti.get(&msg.kunjika).unwrap();
let vayakti2 = self.non_connected_vyakti.get(&vayakti_watchlist.kunjika).unwrap();
vayakti1_name = vayakti1.name.clone();
vayakti2_name = vayakti2.name.clone();
group_kunjika = format!("gupt_{}>{}",msg.kunjika.clone(), vayakti_watchlist.kunjika);
self.grih.insert(group_kunjika.clone(), Grih {
length: Some(2),
loog: vec![Loog::new(msg.addr.clone(), msg.kunjika.clone(), vayakti1.name.clone(), vayakti1.tags.clone()),
Loog::new(vayakti_watchlist.addr.clone(), vayakti_watchlist.kunjika.clone(), vayakti2.name.clone(), vayakti2.tags.clone())]
});
}
// connect person with tag or to zero
let pos = match self.vyaktigat_waitlist.iter().position(|vk| {
match vk.tags.iter().position(|t| msg.tags.contains(t)) {
Some(_) => true,
None => false
}
}) {
Some(i) => i,
None => {
self.vyaktigat_waitlist.push(VyaktiWatchlist {
kunjika: msg.kunjika,
addr: msg.addr,
name: msg.name,
tags: msg.tags
});
return Resp::None;
}
};
self.non_connected_vyakti.remove(&msg.kunjika).unwrap_or(());
self.non_connected_vyakti.remove(&vayakti_watchlist.kunjika).unwrap_or(());
let vayakti_watchlist = self.vyaktigat_waitlist.remove(pos);
let group_kunjika = format!("gupt_{}>{}",msg.kunjika.to_owned(), vayakti_watchlist.kunjika);
self.grih.insert(group_kunjika.to_owned(), Grih {
length: Some(2),
loog: vec![Loog::new(msg.addr.clone(), msg.kunjika.to_owned(), msg.name.to_owned(), Some(msg.tags.clone())),
Loog::new(vayakti_watchlist.addr.clone(), vayakti_watchlist.kunjika.to_owned(), vayakti_watchlist.name.to_owned(), Some(vayakti_watchlist.tags.clone()))]
});
// notify about connection
msg.addr.do_send(ms::WsConnectedRandom {
ajnyat_name: vayakti2_name,
grih_kunjika: group_kunjika.clone()
name: vayakti_watchlist.name,
kunjika: vayakti_watchlist.kunjika,
grih_kunjika: group_kunjika.to_owned()
});
vayakti_watchlist.addr.do_send(ms::WsConnectedRandom {
ajnyat_name: vayakti1_name,
name: msg.name,
kunjika: msg.kunjika.to_owned(),
grih_kunjika: group_kunjika
});
Some(())
Resp::Ok
}
}
/// Next Random user
impl Handler<ms::JoinRandomNext> for ChatPinnd {
type Result = Resp;
fn handle(&mut self, msg: ms::JoinRandomNext, _: &mut Self::Context) -> Self::Result {
let grih = self.grih.get_mut(&msg.grih_kunjika).unwrap();
let loog_i = grih.loog.iter().position(|a| a.kunjika == msg.kunjika).unwrap();
let addr;
let name;
let tags;
{
let loog = grih.loog.get(0).unwrap();
if let None = loog.tags {
return Resp::Err("You are not a randome vyakti!".to_owned());
}
addr = loog.addr.clone();
name = loog.name.to_owned();
tags = loog.tags.clone().unwrap();
}
// Check if watch list is empty
if self.vyaktigat_waitlist.len() == 0 {
self.vyaktigat_waitlist.push(VyaktiWatchlist {
kunjika: msg.kunjika,
addr,
name,
tags
});
return Resp::None;
}
// connect person with tag or to zero
let tags = tags.clone();
let pos = match self.vyaktigat_waitlist.iter().position(|vk| {
match vk.tags.iter().position(|t| tags.contains(t)) {
Some(_) => true,
None => false
}
}) {
Some(i) => i,
None => {
self.vyaktigat_waitlist.push(VyaktiWatchlist {
kunjika: msg.kunjika,
addr,
name,
tags
});
return Resp::None;
}
};
let vayakti_watchlist = self.vyaktigat_waitlist.remove(pos);
let group_kunjika = format!("gupt_{}>{}",msg.kunjika.to_owned(), vayakti_watchlist.kunjika);
grih.loog.remove(loog_i);
grih.loog.iter().for_each(|a| {
a.addr.do_send(ms::WsDisconnected {
kunjika: msg.kunjika.to_owned(),
name: name.to_owned()
})
});
let log_count = grih.loog.len();
drop(grih);
if log_count == 0 {
self.grih.remove(&msg.grih_kunjika);
}
self.grih.insert(group_kunjika.to_owned(), Grih {
length: Some(2),
loog: vec![Loog::new(addr.clone(), msg.kunjika.to_owned(), name.to_owned(), Some(tags.clone())),
Loog::new(vayakti_watchlist.addr.clone(), vayakti_watchlist.kunjika.to_owned(), vayakti_watchlist.name.to_owned(), Some(vayakti_watchlist.tags.clone()))]
});
// notify about connection
addr.do_send(ms::WsConnectedRandom {
name: vayakti_watchlist.name,
kunjika: vayakti_watchlist.kunjika,
grih_kunjika: group_kunjika.to_owned()
});
vayakti_watchlist.addr.do_send(ms::WsConnectedRandom {
name,
kunjika: msg.kunjika.to_owned(),
grih_kunjika: group_kunjika
});
Resp::Ok
}
}
@ -187,9 +281,9 @@ impl Handler<ms::SendText> for ChatPinnd {
if let Some(grih) = self.grih.get(&msg.grih_kunjika) {
grih.loog.iter().for_each(|c| {
c.addr.do_send(ms::WsText {
sender_kunjika: msg.kunjika.clone(),
text: msg.text.clone(),
reply: msg.reply.clone()
sender_kunjika: msg.kunjika.to_owned(),
text: msg.text.to_owned(),
reply: msg.reply.to_owned()
});
});
}
@ -204,8 +298,8 @@ impl Handler<ms::SendStatus> for ChatPinnd {
if let Some(grih) = self.grih.get(&msg.grih_kunjika) {
grih.loog.iter().for_each(|c| {
c.addr.do_send(ms::WsStatus {
sender_kunjika: msg.kunjika.clone(),
status: msg.status.clone(),
sender_kunjika: msg.kunjika.to_owned(),
status: msg.status.to_owned(),
});
});
}
@ -220,7 +314,7 @@ impl Handler<ms::List> for ChatPinnd {
if let Some(grih) = self.grih.get(&msg.grih_kunjika) {
let mut list = Vec::new();
for x in grih.loog.iter() {
list.push((x.kunjika.clone(),x.name.clone()));
list.push((x.kunjika.to_owned(),x.name.to_owned()));
}
serde_json::json!(list).to_string()
} else {
@ -236,23 +330,23 @@ impl Handler<ms::LeaveUser> for ChatPinnd {
fn handle(&mut self, msg: ms::LeaveUser, _: &mut Self::Context) -> Self::Result {
if let Some(grih_kunjika) = &msg.grih_kunjika {
if let Some(grih) = self.grih.get_mut(grih_kunjika) {
if let Some(i) = grih.loog.iter().position(|x| x.addr == msg.addr) {
grih.loog.remove(i);
}
let name = if let Some(i) = grih.loog.iter().position(|x| x.addr == msg.addr) {
grih.loog.remove(i).name
} else { "".to_owned() };
if grih.loog.len() == 0 {
self.grih.remove(grih_kunjika);
} else {
grih.loog.iter().for_each(|a| {
a.addr.do_send(ms::WsDisconnected {
kunjika: msg.kunjika.clone()
kunjika: msg.kunjika.to_owned(),
name: name.to_owned()
})
});
}
}
}
self.non_connected_vyakti.remove(&msg.kunjika).unwrap_or(());
if let Some(i) = self.vyaktigat_waitlist.iter().position(|a| a.kunjika == msg.kunjika) {
self.vyaktigat_waitlist.remove(i);
}
@ -263,8 +357,7 @@ impl Default for ChatPinnd {
fn default() -> Self {
ChatPinnd {
grih: HashMap::new(),
vyaktigat_waitlist: Vec::new(),
non_connected_vyakti: VecMap::new()
vyaktigat_waitlist: Vec::new()
}
}
}
@ -273,12 +366,13 @@ impl Loog {
fn new(addr: Addr<ws_sansad::WsSansad>,
kunjika: String,
name: String,
tags: Vec<String>) -> Self {
tags: Option<Vec<String>>) -> Self {
Loog {
addr,
kunjika,
name,
_tags:tags
tags
}
}
}

View File

@ -1,4 +1,5 @@
use std::fmt;
use std::error::Error;
#[derive(Debug, Clone)]
pub struct AlreadyExistError;
@ -8,3 +9,7 @@ impl fmt::Display for AlreadyExistError {
write!(f, "User kunjika already exist!")
}
}
impl Error for AlreadyExistError {
}

View File

@ -1,35 +1,37 @@
//! Messages to be sent between Actors
use actix::prelude::*;
use dev::{MessageResponse, ResponseChannel};
use crate::ws_sansad::WsSansad;
use crate::errors;
//################################################## For ChatPinnd ##################################################
/// Request to change information of vayakti to list of vayakti im ChatPind
#[derive(Clone, Message)]
#[rtype(result = "Option<String>")] // None if no error
pub struct SetInfoVyakti {
pub kunjika: String,
pub name: String,
pub tags: Vec<String>,
pub modify: bool
}
/// Request to Grih with its kunjika
#[derive(Clone, Message)]
#[rtype(result = "Result<(), errors::GrihFullError>")]
#[rtype(result = "Resp")]
pub struct JoinGrih {
pub grih_kunjika: String,
pub length: Option<usize>,
pub addr: Addr<WsSansad>,
pub kunjika: String
pub kunjika: String,
pub name: String,
}
/// Request to connect Random vayakti
#[derive(Clone, Message)]
#[rtype(result = "Option<()>")]
#[rtype(result = "Resp")]
pub struct JoinRandom {
pub addr: Addr<WsSansad>,
pub kunjika: String,
pub name: String,
pub tags: Vec<String>,
}
/// Request to connect Random vayakti
#[derive(Clone, Message)]
#[rtype(result = "Resp")]
pub struct JoinRandomNext {
pub grih_kunjika: String,
pub kunjika: String
}
@ -93,7 +95,6 @@ pub struct WsList {
pub json: String
}
// Notify Someone connected
#[derive(Clone, Message)]
#[rtype(result = "()")]
@ -106,7 +107,8 @@ pub struct WsConnected {
#[derive(Clone, Message)]
#[rtype(result = "()")]
pub struct WsDisconnected {
pub kunjika: String
pub kunjika: String,
pub name: String
}
// Give response message
@ -121,7 +123,25 @@ pub struct WsResponse {
#[derive(Clone, Message)]
#[rtype(result = "()")]
pub struct WsConnectedRandom {
pub ajnyat_name: String,
pub name: String,
pub kunjika: String,
pub grih_kunjika: String
}
//################################################## Helper ##################################################
pub enum Resp {
Ok,
Err(String),
None
}
impl<A, M> MessageResponse<A, M> for Resp
where
A: Actor,
M: Message<Result = Resp>,
{
fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) {
if let Some(tx) = tx {
tx.send(self);
}
}
}

View File

@ -2,15 +2,22 @@
use actix::prelude::*;
use actix_broker::{Broker, SystemBroker};
use actix_web_actors::ws;
use ms::Resp;
use serde_json::{json, Value};
use std::time::{Duration, Instant};
use crate::{chat_pinnd::ChatPinnd, messages as ms, validator::{Validation as vl, validate}};
use crate::errors;
/// How often heartbeat pings are sent
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
/// How long before lack of client response causes a timeout
const CLIENT_TIMEOUT: Duration = Duration::from_secs(10);
pub struct WsSansad {
kunjika: Option<String>,
kunjika: String,
isthiti: Isthiti,
addr: Option<Addr<Self>>,
hb: Instant
}
#[derive(Debug)]
@ -25,6 +32,7 @@ impl Actor for WsSansad {
fn started(&mut self, ctx: &mut Self::Context) {
self.addr = Some(ctx.address().clone()); // own addr
self.hb(ctx);
}
fn stopping(&mut self, _: &mut Self::Context) -> Running {
@ -37,14 +45,18 @@ impl Actor for WsSansad {
impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WsSansad {
fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context) {
match msg {
Ok(ws::Message::Ping(msg)) => ctx.ping(&msg),
Ok(ws::Message::Text(msg)) => {
Ok(ws::Message::Ping(msg)) => {
ctx.ping(&msg);
self.hb = Instant::now();
}, Ok(ws::Message::Pong(_)) => {
self.hb = Instant::now();
}, Ok(ws::Message::Text(msg)) => {
futures::executor::block_on(self.parse_text_handle(msg));
},
Ok(ws::Message::Close(msg)) => {
}, Ok(ws::Message::Close(msg)) => {
ctx.close(msg);
ctx.stop();
}
_ => ctx.close(None)
_ => ctx.stop()
}
}
}
@ -69,7 +81,7 @@ impl Handler<ms::WsStatus> for WsSansad {
type Result = ();
fn handle(&mut self, msg: ms::WsStatus, ctx: &mut Self::Context) -> Self::Result {
let json = json!({
"cmd": "text",
"cmd": "status",
"status": msg.status,
"kunjika": msg.sender_kunjika // Sender's kunjuka
});
@ -121,7 +133,8 @@ impl Handler<ms::WsDisconnected> for WsSansad {
fn handle(&mut self, msg: ms::WsDisconnected, ctx: &mut Self::Context) -> Self::Result {
let json = json!({
"cmd": "disconnected",
"name": msg.kunjika
"name": msg.name,
"kunjika": msg.kunjika
});
ctx.text(json.to_string());
}
@ -133,8 +146,9 @@ impl Handler<ms::WsConnectedRandom> for WsSansad {
fn handle(&mut self, msg: ms::WsConnectedRandom, ctx: &mut Self::Context) -> Self::Result {
self.isthiti = Isthiti::Grih(msg.grih_kunjika);
let json = json!({
"cmd": "connected",
"ajnyat": msg.ajnyat_name
"cmd": "random",
"name": msg.name,
"kunjika": msg.kunjika
});
ctx.text(json.to_string());
}
@ -143,20 +157,41 @@ impl Handler<ms::WsConnectedRandom> for WsSansad {
impl WsSansad {
pub fn new() -> Self {
WsSansad {
kunjika: None,
kunjika: String::new(),
isthiti: Isthiti::None,
addr: None,
hb: Instant::now()
}
}
/// helper method that sends ping to client every second.
///
/// also this method checks heartbeats from client
fn hb(&self, ctx: &mut <Self as Actor>::Context) {
ctx.run_interval(HEARTBEAT_INTERVAL, |act, ctx| {
// check client heartbeats
if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT {
// heartbeat timed out
println!("Websocket Client heartbeat failed, disconnecting!");
// stop actor
ctx.stop();
// don't try to send a ping
return;
}
ctx.ping(b"");
});
}
/// parse the request text from client
async fn parse_text_handle(&mut self, msg: String) {
println!("{:?}", msg);
if let Ok(val) = serde_json::from_str::<Value>(&msg) {
match val.get("cmd").unwrap().as_str().unwrap() {
"seinfo" => { self.set_info(val).await },
"join" => { self.join_grih(val).await },
"rand" => { self.join_random().await },
"rand" => { self.join_random(val).await },
"randnext" => { self.join_random_next().await },
"text" => { self.send_text(val).await },
"status" => { self.send_status(val).await },
"list" => { self.list().await },
@ -181,10 +216,17 @@ impl WsSansad {
message: text.to_owned()
});
}
/// Request for joining to random person
async fn join_random(&mut self, val: Value) {
// Check is already joined
match self.isthiti {
Isthiti::None => (),
Isthiti::VraktigatWaitlist => {
self.send_ok_response("watchlist");
return;
}, Isthiti::Grih(_) => return
}
/// send info of user and modify if needed
async fn set_info(&mut self, val: Value) {
// parse parameters
let kunjika = match val.get("kunjika") {
Some(val ) => val.as_str().unwrap().to_owned(),
None => {
@ -222,32 +264,69 @@ impl WsSansad {
return;
}
// check if eing modified
let modify = self.kunjika == Some(kunjika.clone());
//request
let result: Option<String> = ChatPinnd::from_registry().send(ms::SetInfoVyakti {
kunjika: kunjika.clone(),
// request
let result: Resp = ChatPinnd::from_registry().send(ms::JoinRandom{
addr: self.addr.clone().unwrap(),
kunjika: kunjika.to_owned(),
name,
tags,
modify
tags
}).await.unwrap();
if let Some(msg) = result {
self.send_err_response(&msg);
return;
match result {
Resp::Err(err) => self.send_err_response(&err),
Resp::Ok => self.kunjika = kunjika,
Resp::None => {
self.addr.clone().unwrap().do_send(ms::WsResponse{
result: "watch".to_owned() ,
message: "Watchlist".to_owned()
});
self.isthiti = Isthiti::VraktigatWaitlist;
self.kunjika = kunjika
}
}
self.kunjika = Some(kunjika);
self.send_ok_response("info changed");
}
/// Request for joining to random person
async fn join_random(&mut self) {
// check if vayakti exist
if let None = self.kunjika {
self.send_err_response("No vayakti kunjika set");
return;
async fn join_random_next(&mut self) {
// Check is already joined
let grih_kunjika = match &self.isthiti {
Isthiti::VraktigatWaitlist => {
self.send_ok_response("watchlist");
return;
},
Isthiti::Grih(grih_kunjika) => grih_kunjika,
Isthiti::None => {
self.send_ok_response("Not allowed");
return;
}
};
// request
let result: Resp = ChatPinnd::from_registry().send(ms::JoinRandomNext{
kunjika: self.kunjika.to_owned(),
grih_kunjika: grih_kunjika.to_owned(),
}).await.unwrap();
match result {
Resp::Err(err) => self.send_err_response(&err),
Resp::None => {
self.addr.clone().unwrap().do_send(ms::WsResponse{
result: "watch".to_owned() ,
message: "Watchlist".to_owned()
});
self.isthiti = Isthiti::VraktigatWaitlist;
self.kunjika = self.kunjika.to_owned()
}
_ => ()
}
}
/// Request to join to grih
async fn join_grih(&mut self, val: Value) {
// Check is already joined
match self.isthiti {
Isthiti::None => (),
_ => return
}
// is vayakti in watch list
@ -256,50 +335,27 @@ impl WsSansad {
return;
}
// request
let result: Option<()> = ChatPinnd::from_registry().send(ms::JoinRandom{
addr: self.addr.clone().unwrap(),
kunjika: self.kunjika.clone().unwrap()
}).await.unwrap();
if let None = result {
self.send_ok_response("watchlist");
self.isthiti = Isthiti::VraktigatWaitlist;
}
}
/// Request to join to grih
async fn join_grih(&mut self, val: Value) {
//check user exist
if let None = self.kunjika {
self.send_err_response("No vayakti kunjika set");
return;
}
// Check is already joined
match self.isthiti {
Isthiti::None => (),
_ => {
return;
}
}
// parse parameter
let grih_kunjika = match val.get("grih_kunjika") {
Some(val) => val,
let kunjika = match val.get("kunjika") {
Some(val ) => val.as_str().unwrap().to_owned(),
None => {
self.send_err_response("Invalid request");
return;
}
}.as_str().unwrap().to_owned();
println!("about to validate");
// Validate
if let Some(val ) = validate(vec![vl::NonEmpty, vl::NoGupt, vl::NoSpace], &grih_kunjika, "Grih Kunjika") {
println!("{}", val);
self.send_err_response(&val);
return;
}
};
let name = match val.get("name") {
Some(val ) => val.as_str().unwrap().to_owned(),
None => {
self.send_err_response("Invalid request");
return;
}
};
let grih_kunjika = match val.get("grih_kunjika") {
Some(val ) => val.as_str().unwrap().to_owned(),
None => {
self.send_err_response("Invalid request");
return;
}
};
let length: Option<usize> = match val.get("length") {
Some(val) => match val.as_i64(){
Some(val) => Some(val as usize),
@ -308,30 +364,47 @@ impl WsSansad {
None => None
};
println!("{:?} {:?} {:?}", grih_kunjika, self.kunjika, length);
// Validate
if let Some(val ) = validate(vec![vl::NonEmpty, vl::NoSpace, vl::NoHashtag], &kunjika, "Kunjika") {
self.send_err_response(&val);
return;
}
if let Some(val ) = validate(vec![vl::NonEmpty], &name, "Name") {
self.send_err_response(&val);
return;
}
if let Some(val ) = validate(vec![vl::NonEmpty, vl::NoGupt, vl::NoSpace], &grih_kunjika, "Grih Kunjika") {
self.send_err_response(&val);
return;
}
// request
let result: Result<(), errors::GrihFullError> = ChatPinnd::from_registry().send(ms::JoinGrih {
grih_kunjika: grih_kunjika.clone(),
let result: Resp = ChatPinnd::from_registry().send(ms::JoinGrih {
grih_kunjika: grih_kunjika.to_owned(),
length,
addr: self.addr.clone().unwrap(),
kunjika: self.kunjika.clone().unwrap()
kunjika: kunjika.to_owned(),
name
}).await.unwrap();
match result {
Ok(_) => {
Resp::Err(err) => self.send_err_response(&err),
Resp::Ok => {
self.isthiti = Isthiti::Grih(grih_kunjika);
self.kunjika = kunjika;
self.send_ok_response("joined")
},
Err(e) => self.send_err_response(&format!("{}", e))
}
_ => ()
}
}
/// Request to join to grih
async fn list(&mut self) {
// check if vayakti exist
if let None = self.kunjika {
self.send_err_response("No vayakti kunjika set");
if let Isthiti::None = self.isthiti {
self.send_err_response("Not in any Grih");
return;
}
@ -339,7 +412,7 @@ impl WsSansad {
match &self.isthiti {
Isthiti::Grih(kunjika) => {
let json: String = ChatPinnd::from_registry().send(ms::List {
grih_kunjika: kunjika.clone()
grih_kunjika: kunjika.to_owned()
}).await.unwrap();
self.addr.clone().unwrap().do_send(ms::WsList {
@ -356,8 +429,8 @@ impl WsSansad {
/// send text to vayakti in grih
async fn send_text(&mut self, val: Value) {
// check if vayakti exist
if let None = self.kunjika {
self.send_err_response("No vayakti kunjika set");
if let Isthiti::None = self.isthiti {
self.send_err_response("Not in any Grih");
return;
}
@ -385,15 +458,15 @@ impl WsSansad {
};
let grih_kunjika = match &self.isthiti {
Isthiti::Grih(g) => {
g.clone()
Isthiti::Grih(grih_kunjika) => {
grih_kunjika.to_owned()
}, _ => {
return;
}
};
Broker::<SystemBroker>::issue_async(ms::SendText {
grih_kunjika,
kunjika: self.kunjika.clone().unwrap(),
kunjika: self.kunjika.to_owned(),
text,
reply
});
@ -402,8 +475,8 @@ impl WsSansad {
/// send status to vayakti in grih
async fn send_status(&mut self, val: Value) {
// check if vayakti exist
if let None = self.kunjika {
self.send_err_response("No vayakti kunjika set");
if let Isthiti::None = self.isthiti {
self.send_err_response("Not in any Grih");
return;
}
@ -425,34 +498,32 @@ impl WsSansad {
}
}.as_str().unwrap().to_owned();
let grih_kunjika = match &self.isthiti {
Isthiti::Grih(g) => {
g.clone()
Isthiti::Grih(grih_kunjika) => {
grih_kunjika.to_owned()
}, _ => {
return;
}
};
Broker::<SystemBroker>::issue_async(ms::SendStatus {
grih_kunjika,
kunjika: self.kunjika.clone().unwrap(),
kunjika: self.kunjika.to_owned(),
status
});
}
// notify leaving
/// notify leaving
async fn leave_grih(&mut self) {
let grih_kunjika = match &self.isthiti {
Isthiti::Grih(val) => Some(val.to_owned()),
_ => None
};
if let Some(ku) = &self.kunjika {
Broker::<SystemBroker>::issue_async(ms::LeaveUser {
grih_kunjika,
kunjika:
ku.to_owned(),
addr: self.addr.clone().unwrap()
});
}
Broker::<SystemBroker>::issue_async(ms::LeaveUser {
grih_kunjika,
kunjika: self.kunjika.to_owned(),
addr: self.addr.clone().unwrap()
});
self.isthiti = Isthiti::None;
self.send_ok_response("left");

View File

@ -3,6 +3,7 @@ pre {
font-size: 0.9em;
margin-top: 0;
margin-bottom: 0;
white-space: pre-wrap;
}
textarea {

View File

@ -30,7 +30,7 @@
<div name="error_area" class="siimple-content siimple--py-1 siimple--text-small siimple--color-primary siimple-content--fluid"></div>
<div class="siimple-form-detail">Connect Grih(home) with kunjika.</div>
<div class="siimple-form-field">
<div class="siimple-form-field-label">Grih kunjika(id)</div>
<div class="siimple-form-field-label">Grih kunjika(home id)</div>
<input name="grih_kunjika" type="text" class="siimple-input siimple-input--fluid" placeholder="Grih kunjika">
<div class="siimple-form-field-helper">New group is created if no such group exists</div>
</div>
@ -66,7 +66,7 @@
<div class="siimple-form-field">
<div class="siimple-form-field-label">Tags</div>
<input name="tags" type="text" class="siimple-input siimple-input--fluid" placeholder="Tags">
<div class="siimple-form-field-helper">Seperated by space</div>
<div class="siimple-form-field-helper">Seperated by space. Leave empty to connect anyone. Its recommended to try without tags if you don't get anyone</div>
</div>
<div class="siimple-form-field siimple--text-right" style="width: 100%;">
<div><span name="error_msg" class="siimple-tag siimple-tag--error siimple--my-2 hidden"></span></div>
@ -85,14 +85,14 @@
</div>
<a class="siimple-navbar-title"><img src="img/label-l.svg" alt="Lupt Chat" height="34px"></a>
<div class="siimple--float-right">
<a class="siimple-navbar-item">Next &rightarrow;</a>
<a id="next_btn" onclick="connect_next()" class="siimple-navbar-item">Next &rightarrow;</a>
</div>
</div>
<div id="message_area_scroll" style="height: calc(100% - 2*44px - 2*5px); padding: 5px; overflow-y: scroll;" class="siimple--bg-light">
<div id="message_area" class="siimple--my-1">
</div>
<div id="message_area" class="siimple--my-1">
<div id="status_area" class="siimple--my-1">
</div>
</div>
@ -134,7 +134,36 @@
</div>
<div>
<a onclick="cleanMessage()" class="siimple-btn siimple-btn--blue">Clear</a>
<a class="siimple-btn siimple-btn--blue">List Users</a>
<a onclick="vayaktiList()" class="siimple-btn siimple-btn--blue">List Vyakti(Users)</a>
</div>
</div>
<!-- Vyakti List -->
<div id="vayakti_model" class="siimple-modal siimple-modal--large hidden" style="z-index: 9999;" id="modal">
<div class="siimple-modal-content">
<div class="siimple-modal-header">
<div class="siimple-modal-header-title">List Vyakti(Users)</div>
<div onclick="$('#vayakti_model').addClass('hidden')" class="siimple-modal-header-close" id="modal-close"></div>
</div>
<div class="siimple-modal-body siimple--px-0">
<div class="siimple-table siimple--px-3">
<div class="siimple-table-header">
<div class="siimple-table-row">
<div class="siimple-table-cell">Name</div>
<div class="siimple-table-cell">Kunjika</div>
</div>
</div>
<div id="vayakti_list" class="siimple-table-body">
<div class="siimple-table-row">
<div class="siimple-table-cell">a</div>
<div class="siimple-table-cell">a</div>
</div>
</div>
</div>
</div>
<div class="siimple-modal-footer siimple--py-2">
<a onclick="$('#vayakti_model').addClass('hidden'); $('#vayakti_list').empty()" class="siimple-btn siimple-btn--blue">Close</a>
</div>
</div>
</div>

View File

@ -20,18 +20,34 @@ $(document).ready(() => {
});
$('.message-me, .message-other').click(function() {
activateMessage(this);
selectMessage(this);
});
$('#selected_clip > .siimple-close').click(function() {
deactivateMessages();
unselectMessages();
});
var send_typing = false;
var timeout = null;
$('#send_box').keypress(function(e) {
if(e.originalEvent.charCode == 13 && !e.shiftKey) {
send();
e.preventDefault();
clearTimeout(timeout);
send_typing = false;
sendTypingEnd()
return
}
if (!send_typing) {
sendTyping();
send_typing = true;
return;
}
clearTimeout(timeout);
timeout = setTimeout(function() {
send_typing = false;
sendTypingEnd();
},3000);
});
$('#send_box').bind('input propertychange keyup', function() {
@ -47,6 +63,20 @@ $(document).ready(() => {
});
});
function sendTyping() {
socket.send(JSON.stringify({
cmd: 'status',
status: "typing"
}));
}
function sendTypingEnd() {
socket.send(JSON.stringify({
cmd: 'status',
status: "typing_end"
}));
}
function calcHeight(value) {
let numberOfLineBreaks = (value.match(/\n/g) || []).length;
// min-height + lines x line-height + padding + border
@ -61,7 +91,9 @@ var myinfo = {
kunjika: "",
name: ""
};
var users = {};
var vayaktiList = {};
var typing = [];
var no_name_message = false;
// Connection opened
socket.addEventListener('open', function (event) {
@ -71,13 +103,13 @@ socket.addEventListener('open', function (event) {
// Listen for messages
socket.addEventListener('message', function (event) {
var j = JSON.parse(event.data);
console.log(j);
switch(j.cmd) {
case 'resp':
if(j.result == 'Err') {
if($('#chat_panel').hasClass('hidden')) {
$('[name="error_msg"]').text(j.message);
$('[name="error_msg"]').removeClass('hidden');
$('#progressbar').addClass('hidden');
callbacks = [];
} else {
pushStatus(j.message);
@ -89,24 +121,49 @@ socket.addEventListener('message', function (event) {
}
}
break;
case 'random':
callbacks[0]();
callbacks = [];
no_name_message = true;
$('#next_btn').removeClass('hidden');
pushStatus('Say hi to '+j.name);
vayaktiList[j.kunjika] = j.name;
break;
case 'status':
if(j.status == "typing") {
typing.push(j.kunjika);
pushTypingStatus();
} else if(j.status == "typing_end") {
const index = typing.indexOf(j.kunjika);
if (index > -1) typing.splice(index, 1);
pushTypingStatus();
}
break;
case 'text':
pushMessage(j.kunjika, j.text, j.reply);
break;
case 'connected':
users[j.kunjika] = j.name;
vayaktiList[j.kunjika] = j.name;
pushStatus('Vyakti '+j.name+' connected as '+j.kunjika);
break;
case 'disconnected':
delete users[j.kunjika];
delete vayaktiList[j.kunjika];
pushStatus('Vyakti '+j.name+' disconnected as '+j.kunjika);
break;
case 'list':
JSON.parse(j.vayakti).forEach(function(usr) {
vayaktiList[usr[0]] = usr[1];
});
break;
}
});
var joining = false;
function connect(frm) {
if(joining) return;
joining = true;
var frm = $(frm);
$('#progressbar').addClass('hidden');
$('#progressbar').removeClass('hidden');
var data = {};
frm.serializeArray().forEach(el => {
data[el.name] = el.value;
@ -115,8 +172,27 @@ function connect(frm) {
data['length'] = parseInt(data['length']);
}
callbacks.push(() => {
socket.send(JSON.stringify(Object.assign({cmd: frm.attr('cmd')}, data)));
cleanMessage();
$('#progressbar').addClass('hidden');
$('#send_box').text('');
$('#connect_panel').addClass('hidden');
$('[name="error_msg"]').addClass('hidden');
$('#chat_panel').removeClass('hidden');
$('#send_box').focus();
$('#next_btn').addClass('hidden');
myinfo.kunjika = data.kunjika;
myinfo.name = data.name;
no_name_message = false;
joining = false;
socket.send(JSON.stringify({cmd: 'list'}));
});
socket.send(JSON.stringify(Object.assign({cmd: frm.attr('cmd')}, data)));
}
function connect_next() {
if(joining) return;
joining = true;
$('#progressbar').removeClass('hidden');
callbacks.push(() => {
cleanMessage();
$('#progressbar').addClass('hidden');
@ -125,10 +201,11 @@ function connect(frm) {
$('[name="error_msg"]').addClass('hidden');
$('#chat_panel').removeClass('hidden');
$('#send_box').focus();
myinfo.kunjika = data.kunjika;
myinfo.name = data.name;
$('#next_btn').addClass('hidden');
joining = false;
socket.send(JSON.stringify({cmd: 'list'}));
});
socket.send(JSON.stringify(Object.assign({cmd: 'seinfo'}, data)));
socket.send(JSON.stringify({ cmd: 'randnext' }));
}
function leave() {
@ -138,19 +215,43 @@ function leave() {
$('#selected_clip').addClass('hidden');
$('#action_clip').addClass('hidden');
$('#connect_panel').removeClass('hidden');
myinfo.kunjika = "";
myinfo.name = "";
myinfo.kunjika = '';
myinfo.name = '';
});
socket.send(JSON.stringify({
cmd: "leave"
cmd: 'leave'
}));
}
function pushTypingStatus() {
var elm = $('#status_area > #typing');
if(elm.length > 0) elm.remove();
if(typing.length == 0) return;
var text = '';
typing.forEach((val) => {
text += val + ','
})
text = text.substr(0, text.length-1);
text += ' is typing...'
$('#status_area').append($('<div>', { id: 'typing',
class:'siimple-label siimple--mx-2 siimple--my-0' }).append(text));
var scroll = $("#message_area_scroll");
scroll.scrollTop(scroll[0].scrollHeight);
}
function pushMessage(sender, text, reply = null) {
var isMe = myinfo.kunjika == sender;
var area = $('#message_area');
var elm = $('<div>', {class: 'message '+(isMe?'message-me':'message-other')});
elm.append($('<div>', {class: 'message-by'}).append(users[sender]));
if(!no_name_message) {
if(sender == myinfo.kunjika)
elm.append($('<div>', {class: 'message-by'}).append('me'))
else
elm.append($('<div>', {class: 'message-by'}).append(vayaktiList[sender]+'('+sender+')'))
} else {
elm.addClass('siimple--py-1');
}
if(reply != null && reply.length > 0) {
elm.append(
$('<div>', {class: 'message message-reply'})
@ -159,35 +260,33 @@ function pushMessage(sender, text, reply = null) {
}
elm.append($('<pre>').append(text));
elm.click(function() {
activateMessage(this);
selectMessage(this);
});
area.append(elm);
//to bottom
var scroll = $("#message_area_scroll");
scroll.scrollTop(scroll[0].scrollHeight);
}
// in message area
function pushStatus(text) {
var area = $('#message_area');
var elm = $('<div>', {class: 'status'});
elm.append($('<span>', {class: 'siimple-tag siimple-tag--dark'}).append(text));
area.append(elm);
//to bottom
var scroll = $("#message_area_scroll");
scroll.scrollTop(scroll[0].scrollHeight);
}
function deactivateMessages() {
function unselectMessages() {
$('.active').each(function() {
$(this).removeClass('active');
});
$('#selected_clip').addClass('hidden');
}
function activateMessage(t) {
function selectMessage(t) {
var t = $(t);
t.toggleClass('active');
@ -214,7 +313,7 @@ function prepareReply() {
el.removeClass('hidden');
el.attr('msg', text);
$('#reply_clip > span').text(text.substr(0, 15)+ '...');
deactivateMessages();
unselectMessages();
}
function send() {
@ -237,49 +336,22 @@ function copyMessagesToClipboard() {
$temp.val(selectedMessageToText()).select();
document.execCommand("copy");
$temp.remove();
deactivateMessages();
unselectMessages();
}
function cleanMessage() {
$('#message_area').empty();
$('#action_clip').addClass('hidden');
}
// function wsend(p) {
// socket.send(p);
// }
// function join(r, l) {
// socket.send(JSON.stringify({
// cmd: "join",
// grih_kunjika: r,
// length: l
// }));
// }
// function leave() {
// socket.send(JSON.stringify({
// cmd: "leave"
// }));
// }
// function send(t) {
// socket.send(JSON.stringify({
// cmd: "text",
// text: t
// }));
// }
// function info(k, n, t) {
// socket.send(JSON.stringify({
// cmd: "seinfo",
// kunjika: k,
// name: n,
// tags: t
// }));
// }
// function joinrand() {
// socket.send(JSON.stringify({
// cmd: "rand"
// }));
// }
function vayaktiList() {
var v = $('#vayakti_list');
v.empty();
Object.keys(vayaktiList).forEach((key) => {
v.append($('<div>', {class: 'siimple-table-row'})
.append($('<div>', {class: 'siimple-table-cell'}).append(key))
.append($('<div>', {class: 'siimple-table-cell'}).append(vayaktiList[key])));
});
$('#vayakti_model').removeClass('hidden');
$('#action_clip').addClass('hidden');
}