Initial commit.

main
Werner Kroneman 2023-12-07 17:17:52 +01:00
commit 0d80360b58
6 changed files with 4492 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

4195
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

15
Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "chatclient"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = "1.34.0"
log = "0.4.20"
env_logger = "0.10.1"
dioxus = "0.4.0"
dioxus-desktop = "0.4.0"
futures-util = "0.3.29"
xmpp = { git = "https://gitlab.com/xmpp-rs/xmpp-rs.git", rev="05a0b6cd8f870ff2fe9b773d7eefb8205b45f4b9"}

27
flake.lock Normal file
View File

@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1701718080,
"narHash": "sha256-6ovz0pG76dE0P170pmmZex1wWcQoeiomUZGggfH9XPs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "2c7f3c0fb7c08a0814627611d9d7d45ab6d75335",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

52
flake.nix Normal file
View File

@ -0,0 +1,52 @@
{
description = "A devshell environment for an XMPP client thingy.";
nixConfig.bash-prompt = "[rust/chatthingy] ";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
rust-toolchain = pkgs.symlinkJoin {
name = "rust-toolchain";
paths = [pkgs.rustc pkgs.cargo pkgs.rustPlatform.rustcSrc];
};
in
{
devShells.${system}.default =
pkgs.mkShell rec {
packages = with pkgs; [
rust-toolchain
rustfmt
pkg-config
alsaLib
udev
xorg.libX11
xorg.libXcursor
xorg.libXi
xorg.libXrandr
vulkan-loader
libxkbcommon
wayland
fontconfig
openssl
glibc
gtk3
webkitgtk_4_1
dioxus-cli
];
WEBKIT_DISABLE_COMPOSITING_MODE=1;
# LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath packages;
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
};
}

202
src/main.rs Normal file
View File

@ -0,0 +1,202 @@
#![allow(non_snake_case)]
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use futures_util::stream::StreamExt;
use dioxus::html::textarea;
// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;
use dioxus_elements::{h1, li, ul};
use log::info;
use tokio::select;
use xmpp::{BareJid, ClientBuilder, ClientType, RoomNick};
use xmpp::parsers::message::MessageType;
fn main() {
env_logger::init();
// launch the dioxus app in a webview
dioxus_desktop::launch(App);
}
#[derive(Props)]
struct RoomListProps<'a> {
rooms: Vec<BareJid>,
on_room_picked: EventHandler<'a, BareJid>,
}
/// A Dioxus component that renders a list of rooms
fn RoomList<'a>(cx: Scope<'a, RoomListProps>) -> Element<'a> {
render! {
h1 { "Rooms" }
ul {
for room in cx.props.rooms.iter() {
rsx! { li {
// onclick: move |_| cx.props.room_selected.call(room.to_owned()),
onclick: |evt| cx.props.on_room_picked.call(room.to_owned()),
"{room}"
} }
}
}
}
}
#[derive(Props)]
struct RoomViewProps<'a> {
room: BareJid,
messages: Vec<String>,
on_message_sent: EventHandler<'a, String>,
}
// impl PartialEq for RoomViewProps {
// fn eq(&self, other: &Self) -> bool {
// self.room == other.room && self.messages == other.messages
// }
// }
fn RoomView<'a>(cx: Scope<'a, RoomViewProps>) -> Element<'a> {
let message = use_state(cx, || "".to_owned());
render! {
div {
h1 { "Messages" }
ul {
for message in cx.props.messages.iter() {
rsx! { li {
"{message}"
} }
}
}
textarea {
oninput: |evt| {
message.set(evt.value.clone());
}
}
button {
onclick: move |_| {
cx.props.on_message_sent.call(message.current().to_string());
},
"Send"
}
}
}
}
// define a component that renders a div with the text "Hello, world!"
fn App(cx: Scope) -> Element {
let rooms = use_state(cx, || vec![]);
let messages = use_state(cx, || HashMap::new());
let current_room = use_state(cx, || None::<BareJid>);
#[derive(Debug)]
struct SendMessage {
recipient: BareJid,
message: String,
}
let cr = use_coroutine(cx, |mut commands: UnboundedReceiver<SendMessage>| {
let mut rooms = rooms.to_owned();
let mut messages = messages.to_owned();
let current_room = current_room.to_owned();
async move {
let jid = BareJid::from_str("bot@mizah.xyz").unwrap();
let password = "G0cEiYp98G8nBzOdkKMF";
let mut agent = ClientBuilder::new(jid, &password)
.set_client(ClientType::Pc, "dergchat")
.set_default_nick("dergchat")
.build();
// tokio::spawn({
// async move {
// while let Some(command) = commands.next().await {
// println!("Sending command: {:?}", command);
// agent.borrow_mut().send_message(command.recipient.into(), MessageType::Chat, "en/us", &command.message).await;
// }
// }
// });
loop {
select! {
events = agent.wait_for_events() => {
if let Some(events) = events {
for event in events {
match event {
xmpp::Event::JoinRoom(jid, conference) => {
println!("Joining room: {}", &jid);
rooms.make_mut().push(jid.clone());
agent.join_room(jid, None, None, "en/us", "online").await;
},
xmpp::Event::ChatMessage(id, sender, body) => {
println!("Message from {}: {}", &sender, &body.0);
messages.make_mut().entry(sender).or_insert(vec![]).push(body.0);
},
xmpp::Event::RoomMessage(id, room_jid, sender_nick, body) => {
println!("Message in {} from {}: {}", &room_jid, &sender_nick, &body.0);
messages.make_mut().entry(room_jid).or_insert(vec![]).push(body.0);
}
xmpp::Event::RoomPrivateMessage(id, room_jid, sender_nick, body) => {
println!("Private message in {} from {}: {}", &room_jid, &sender_nick, &body.0);
messages.make_mut().entry(room_jid).or_insert(vec![]).push(body.0);
},
_ => {
log::debug!("Received unsupported event {:?}", event);
}
}
}
} else {
info!("Disconnected");
}
},
command = commands.next() => {
if let Some(command) = command {
println!("Sending command: {:?}", command);
agent.send_message(command.recipient.into(), MessageType::Groupchat, "en-us", &command.message).await;
} else {
info!("Command channel closed");
break;
}
},
}
}
}
});
render! {
RoomList {
rooms: rooms.to_vec(),
on_room_picked: move |x:BareJid| {
println!("Room selected: {:?}", x);
current_room.set(Some(x));
},
}
if let Some(room) = current_room.get() {
let messages = messages.get().get(&room).expect("Selected non-existant room").to_vec();
rsx! {
RoomView {
room: room.clone(),
messages: messages,
on_message_sent: move |x:String| {
println!("Message sent: {:?}", x);
cr.send(SendMessage {
recipient: room.clone(),
message: x,
});
},
}
}
}
}
}