Added main gui components.
This commit is contained in:
commit
3b152d2251
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal 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
5426
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
32
Cargo.toml
Normal file
32
Cargo.toml
Normal 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
21
Dioxus.toml
Normal 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
25
README.md
Normal 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
BIN
assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
20
assets/header.svg
Normal file
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
204
assets/main.css
Normal 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
8
clippy.toml
Normal 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
224
src/main.rs
Normal 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);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user