feat: implement client backend
This includes temporary AI-generated frontend scaffolding, none of this will be used in production. It was just used during development for the backend.
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
/// TEMPORARY AI GENERATED FRONTEND \\\
|
||||
/// NOT TO BE USED IN PRODUCTION \\\
|
||||
|
||||
import { ListView } from "std-widgets.slint";
|
||||
import { Theme } from "../theme.slint";
|
||||
import { Icons } from "../icons.slint";
|
||||
import { ChannelItem, ChannelKind } from "../types.slint";
|
||||
import { Icon } from "icon.slint";
|
||||
import { Avatar } from "avatar.slint";
|
||||
import { Badge } from "badge.slint";
|
||||
|
||||
component ChannelRow inherits Rectangle {
|
||||
in property <ChannelItem> c;
|
||||
in property <bool> selected;
|
||||
callback clicked();
|
||||
|
||||
height: c.kind == ChannelKind.category ? 34px : (c.is-dm ? 44px : 34px);
|
||||
opacity: c.joined || c.kind == ChannelKind.category ? 1.0 : 0.5;
|
||||
|
||||
// --- category heading ("TEXT CHANNELS") ---
|
||||
if c.kind == ChannelKind.category: Text {
|
||||
x: 16px;
|
||||
y: 0px;
|
||||
width: parent.width - 32px;
|
||||
height: parent.height - 4px;
|
||||
text: c.name;
|
||||
color: Theme.text-muted;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
vertical-alignment: bottom;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
// --- text / voice channel ---
|
||||
if c.kind != ChannelKind.category && !c.is-dm: Rectangle {
|
||||
x: 8px;
|
||||
y: 1px;
|
||||
width: parent.width - 16px;
|
||||
height: parent.height - 2px;
|
||||
border-radius: 4px;
|
||||
background: root.selected ? Theme.selected-bg : (touch.has-hover ? Theme.hover-bg : transparent);
|
||||
|
||||
Icon {
|
||||
source: c.kind == ChannelKind.voice ? Icons.speaker : Icons.hash;
|
||||
tint: root.selected || c.unread ? Theme.text-header : Theme.text-muted;
|
||||
icon-size: 18px;
|
||||
x: 8px;
|
||||
y: (parent.height - self.height) / 2;
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 34px;
|
||||
width: parent.width - 34px - (c.mentions > 0 ? 40px : 8px);
|
||||
height: parent.height;
|
||||
text: c.name;
|
||||
color: root.selected || c.unread || touch.has-hover ? Theme.text-header : Theme.text-muted;
|
||||
font-weight: c.unread ? 700 : 500;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
Badge {
|
||||
count: c.mentions;
|
||||
x: parent.width - self.width - 8px;
|
||||
y: (parent.height - self.height) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// --- direct message ---
|
||||
if c.is-dm && c.kind != ChannelKind.category: Rectangle {
|
||||
x: 8px;
|
||||
y: 1px;
|
||||
width: parent.width - 16px;
|
||||
height: parent.height - 2px;
|
||||
border-radius: 4px;
|
||||
background: root.selected ? Theme.selected-bg : (touch.has-hover ? Theme.hover-bg : transparent);
|
||||
|
||||
Avatar {
|
||||
initial: c.initial;
|
||||
tint: c.color;
|
||||
diameter: 32px;
|
||||
x: 8px;
|
||||
y: (parent.height - self.height) / 2;
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 50px;
|
||||
width: parent.width - 50px - (c.mentions > 0 ? 40px : 8px);
|
||||
height: parent.height;
|
||||
text: c.name;
|
||||
color: root.selected || c.unread || touch.has-hover ? Theme.text-header : Theme.text-muted;
|
||||
font-weight: c.unread ? 700 : 500;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
Badge {
|
||||
count: c.mentions;
|
||||
x: parent.width - self.width - 8px;
|
||||
y: (parent.height - self.height) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
touch := TouchArea {
|
||||
enabled: c.kind != ChannelKind.category;
|
||||
mouse-cursor: pointer;
|
||||
clicked => {
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export component ChannelList inherits Rectangle {
|
||||
in property <string> title;
|
||||
in property <bool> is-home;
|
||||
in property <[ChannelItem]> channels;
|
||||
in property <string> selected-id;
|
||||
callback select(string);
|
||||
callback open-server-menu();
|
||||
|
||||
width: Theme.sidebar-width;
|
||||
background: Theme.sidebar-bg;
|
||||
|
||||
VerticalLayout {
|
||||
// Header: server name (or "Direct Messages")
|
||||
Rectangle {
|
||||
height: Theme.header-height;
|
||||
background: header-touch.has-hover && !root.is-home ? Theme.hover-bg : transparent;
|
||||
|
||||
Text {
|
||||
x: 16px;
|
||||
width: parent.width - 52px;
|
||||
height: parent.height;
|
||||
text: root.title;
|
||||
color: Theme.text-header;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
|
||||
if !root.is-home: Icon {
|
||||
source: Icons.chevron-down;
|
||||
tint: Theme.text-header;
|
||||
icon-size: 16px;
|
||||
x: parent.width - 16px - self.width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
}
|
||||
|
||||
// bottom hairline
|
||||
Rectangle {
|
||||
y: parent.height - 1px;
|
||||
height: 1px;
|
||||
background: Theme.rail-bg;
|
||||
}
|
||||
|
||||
header-touch := TouchArea {
|
||||
enabled: !root.is-home;
|
||||
clicked => {
|
||||
root.open-server-menu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView {
|
||||
for c in root.channels: ChannelRow {
|
||||
c: c;
|
||||
selected: c.id == root.selected-id;
|
||||
clicked => {
|
||||
root.select(c.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user