Added main gui components.

This commit is contained in:
ghost 2025-08-19 05:42:05 +02:00
commit 3b152d2251
10 changed files with 5967 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Generated by Cargo
# will have compiled files and executables
/target
.DS_Store
# These are backup files generated by rustfmt
**/*.rs.bk

5426
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

32
Cargo.toml Normal file
View File

@ -0,0 +1,32 @@
[package]
name = "niom-webrtc2"
version = "0.1.0"
authors = ["ghost <ma-koenig@gmx.net>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus = { version = "0.6.0", features = [] }
console_error_panic_hook = "0.1.7"
tracing = "0.1"
dioxus-logger = "0.6.2"
log = "0.4.27"
[features]
default = ["web"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]
[profile]
[profile.wasm-dev]
inherits = "dev"
opt-level = 1
[profile.server-dev]
inherits = "dev"
[profile.android-dev]
inherits = "dev"

21
Dioxus.toml Normal file
View File

@ -0,0 +1,21 @@
[application]
[web.app]
# HTML title tag content
title = "niom-webrtc2"
# include `assets` in web platform
[web.resource]
# Additional CSS style files
style = []
# Additional JavaScript files
script = []
[web.resource.dev]
# Javascript code file
# serve: [dev-server] only
script = []

25
README.md Normal file
View File

@ -0,0 +1,25 @@
# Development
Your new bare-bones project includes minimal organization with a single `main.rs` file and a few assets.
```
project/
├─ assets/ # Any assets that are used by the app should be placed here
├─ src/
│ ├─ main.rs # main.rs is the entry point to your application and currently contains all components for the app
├─ Cargo.toml # The Cargo.toml file defines the dependencies and feature flags for your project
```
### Serving Your App
Run the following command in the root of your project to start developing with the default platform:
```bash
dx serve
```
To run for a different platform, use the `--platform platform` flag. E.g.
```bash
dx serve --platform desktop
```

BIN
assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

20
assets/header.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

204
assets/main.css Normal file
View File

@ -0,0 +1,204 @@
/* Basis-Styling */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.app-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
/* Header */
header {
text-align: center;
margin-bottom: 40px;
padding: 20px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
header h1 {
color: #2563eb;
margin-bottom: 10px;
}
header p {
color: #666;
}
/* Main Content */
.main-content {
display: grid;
gap: 20px;
grid-template-columns: 1fr;
}
/* Connection Panel */
.connection-panel {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.connection-panel h2 {
margin-bottom: 20px;
color: #1f2937;
}
.input-group {
margin-bottom: 16px;
}
.input-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #374151;
}
.input-group input {
width: 100%;
padding: 10px 12px;
border: 2px solid #e5e7eb;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.2s;
}
.input-group input:focus {
outline: none;
border-color: #2563eb;
}
/* Buttons */
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.connect-btn {
background-color: #2563eb;
color: white;
width: 100%;
}
.connect-btn:hover {
background-color: #1d4ed8;
}
/* Call Controls */
.call-controls {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.call-controls h2 {
margin-bottom: 20px;
color: #1f2937;
}
.control-buttons {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.control-buttons button {
flex: 1;
min-width: 120px;
}
.primary {
background-color: #10b981;
color: white;
}
.primary:hover {
background-color: #059669;
}
.danger {
background-color: #ef4444;
color: white;
}
.danger:hover {
background-color: #dc2626;
}
.mute-btn {
background-color: #6b7280;
color: white;
}
.mute-btn:hover {
background-color: #4b5563;
}
/* Status Display */
.status-display {
background: white;
padding: 24px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.status-display h2 {
margin-bottom: 20px;
color: #1f2937;
}
.status-item {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
padding: 8px 0;
border-bottom: 1px solid #f3f4f6;
}
.status-label {
font-weight: 500;
color: #374151;
}
.status-value {
font-weight: 500;
}
.status-value.disconnected {
color: #ef4444;
}
.status-value.connected {
color: #10b981;
}
/* Responsive Design */
@media (min-width: 768px) {
.main-content {
grid-template-columns: 1fr 1fr;
}
.status-display {
grid-column: 1 / -1;
}
}

8
clippy.toml Normal file
View File

@ -0,0 +1,8 @@
await-holding-invalid-types = [
"generational_box::GenerationalRef",
{ path = "generational_box::GenerationalRef", reason = "Reads should not be held over an await point. This will cause any writes to fail while the await is pending since the read borrow is still active." },
"generational_box::GenerationalRefMut",
{ path = "generational_box::GenerationalRefMut", reason = "Write should not be held over an await point. This will cause any reads or writes to fail while the await is pending since the write borrow is still active." },
"dioxus_signals::Write",
{ path = "dioxus_signals::Write", reason = "Write should not be held over an await point. This will cause any reads or writes to fail while the await is pending since the write borrow is still active." },
]

224
src/main.rs Normal file
View File

@ -0,0 +1,224 @@
#![allow(non_snake_case)]
use dioxus::prelude::*;
const FAVICON: Asset = asset!("/assets/favicon.ico");
const MAIN_CSS: Asset = asset!("/assets/main.css");
const HEADER_SVG: Asset = asset!("/assets/header.svg");
fn main() {
// Logger initialisieren für besseres Debugging
// dioxus_logger::init(log::Level::Info).expect("failed to init logger");
// console_error_panic_hook::set_once();
dioxus::launch(App);
}
#[component]
fn App() -> Element {
rsx! {
document::Link { rel: "icon", href: FAVICON }
document::Link { rel: "stylesheet", href: MAIN_CSS }
Content {}
}
}
#[component]
pub fn Content() ->Element {
let mut connected = use_signal(|| false); // Status: Verbindung aufgebaut?
let mut audio_enabled = use_signal(|| true); // Status: Mikro aktiviert?
// State for Peer IDs
let mut local_peer_id = use_signal(|| generate_peer_id());
let mut remote_peer_id = use_signal(|| String::new());
rsx! {
div {
class: "app-container",
// Header
header {
h1 { "Voice Chat MVP" }
p { "WebRTC-basierter Sprachchat mit Ende-zu-Ende-Verschlüsselung" }
}
main {
class: "main-content",
// Connection Panel
ConnectionPanel {
connected,
on_connect: move |_| connected.set(true)
}
// Call Controls
CallControls {
audio_enabled,
on_toggle_audio: move |_| audio_enabled.set(!audio_enabled())
}
// Status Display
StatusDisplay {
connected,
audio_enabled
}
}
}
}
}
// Komponente für Verbindungseinstellungen
#[component]
fn ConnectionPanel(connected: Signal<bool>, on_connect: EventHandler<MouseEvent>) -> Element {
rsx! {
div {
class: "connection-panel",
h2 { "Verbindung" }
div {
class: "input-group",
label { "for": "peer-id", "Peer ID:" }
input {
id: "peer-id",
r#type: "text",
placeholder: "Wird autmatisch generiert"
}
}
div {
class: "input-group",
label { "for": "remote-peer", "Remote Peer-ID:" }
input {
id: "remote-peer",
r#type: "text",
placeholder: "ID des anderen Teilnehmers eingeben"
}
}
button {
class: "connect-btn",
onclick: on_connect,
disabled: connected(),
if connected() {
"Verbunden"
}
else {
"Mit Signaling Server verbinden"
}
}
}
}
}
// Komponente für Anruf-Steuerungen
#[component]
fn CallControls(audio_enabled: Signal<bool>, on_toggle_audio: EventHandler<MouseEvent>) -> Element {
rsx! {
div {
class: "call-controls",
h2 { "Anruf-Steuerung" }
div {
class: "control-buttons",
button {
class: "call-btn primary",
"Anruf starten"
}
button {
class: "mute-btn",
onclick: on_toggle_audio,
if audio_enabled() {
"Mute"
}
else {
"Unmute"
}
}
button {
class: "end-btn danger",
"Anruf beenden"
}
}
}
}
}
// Komponente für Status-Anzeige
#[component]
fn StatusDisplay(connected: Signal<bool>, audio_enabled: Signal<bool>) -> Element {
rsx! {
div {
class: "status-display",
h2 { "Status" }
div {
class: "status-item",
span {
class: "status-label",
"Signaling Server:"
}
span {
class:
if connected() {
"status-value connected"
}
else {
"status-value disconnected"
},
if connected() {
"Verbunden"
}
else {
"Getrennt"
}
}
}
div {
class: "status-item",
span {
class: "status-label",
"WebRTC Verbindung:"
}
span {
class: "status-value disconnected",
"Nicht verbunden"
}
}
div {
class: "status-item",
span {
class: "status-label",
"Audio Status:"
}
span {
class:
if audio_enabled() {
"status-value"
}
else {
"status-value disconnected"
},
if audio_enabled() {
"Aktiviert"
}
else {
"Stumm"
}
}
}
}
}
}
fn generate_peer_id() -> String {
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(1);
let id = COUNTER.fetch_add(1, Ordering::Relaxed);
return format!("peer-{:06}", id);
}