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:
+251
-9
@@ -1,14 +1,256 @@
|
||||
/// TEMPORARY AI GENERATED FRONTEND \\\
|
||||
/// NOT TO BE USED IN PRODUCTION \\\
|
||||
|
||||
import { Theme } from "theme.slint";
|
||||
import { Screen, ServerItem, ChannelItem, MessageItem, MemberItem } from "types.slint";
|
||||
import { LoginScreen } from "login.slint";
|
||||
import { ServerRail } from "components/server-rail.slint";
|
||||
import { ChannelList } from "components/channel-list.slint";
|
||||
import { UserPanel } from "components/user-panel.slint";
|
||||
import { ChatHeader } from "components/chat-header.slint";
|
||||
import { MessageList } from "components/message-list.slint";
|
||||
import { Composer } from "components/composer.slint";
|
||||
import { MemberList } from "components/member-list.slint";
|
||||
|
||||
export { Screen, ServerItem, ChannelItem, MessageItem, MemberItem, ChannelKind, MessageKind, ReactionItem } from "types.slint";
|
||||
|
||||
export component MainWindow inherits Window {
|
||||
preferred-width: 854px;
|
||||
preferred-height: 480px;
|
||||
title: "Tofu";
|
||||
min-width: 900px;
|
||||
min-height: 520px;
|
||||
preferred-width: 1280px;
|
||||
preferred-height: 800px;
|
||||
background: Theme.chat-bg;
|
||||
default-font-size: 15px;
|
||||
|
||||
min-width: 640px;
|
||||
min-height: 360px;
|
||||
// ---- State pushed from Rust -------------------------------------------
|
||||
in property <Screen> screen: Screen.loading;
|
||||
in property <string> login-error;
|
||||
in property <bool> login-busy;
|
||||
in-out property <string> login-homeserver;
|
||||
in-out property <string> login-username;
|
||||
|
||||
Text {
|
||||
text: "Tofu Client";
|
||||
font-size: 48px;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
in property <string> me-name;
|
||||
in property <string> me-id;
|
||||
in property <string> me-initial;
|
||||
in property <color> me-color: Theme.accent;
|
||||
|
||||
in property <[ServerItem]> servers;
|
||||
in property <bool> home-unread;
|
||||
in property <int> home-mentions;
|
||||
in property <[ChannelItem]> channels;
|
||||
in property <[MessageItem]> messages;
|
||||
in property <[MemberItem]> members;
|
||||
|
||||
in property <string> sidebar-title: "Direct Messages";
|
||||
in property <string> room-name;
|
||||
in property <string> room-topic;
|
||||
in property <bool> room-is-dm;
|
||||
in property <bool> room-encrypted;
|
||||
in property <bool> room-open: false;
|
||||
in property <int> room-nonce;
|
||||
in property <bool> loading-older;
|
||||
in property <string> typing-text;
|
||||
in property <string> toast;
|
||||
|
||||
// ---- State owned by the UI --------------------------------------------
|
||||
in-out property <string> selected-server-id; // "" = Home
|
||||
in-out property <string> selected-room-id;
|
||||
in-out property <bool> show-members: true;
|
||||
// Composer mode
|
||||
in-out property <string> reply-to-id;
|
||||
in-out property <string> reply-to-name;
|
||||
in-out property <string> editing-id;
|
||||
in-out property <string> composer-text;
|
||||
in-out property <int> composer-text-nonce;
|
||||
in-out property <int> composer-focus-nonce;
|
||||
|
||||
// ---- Intents sent to Rust ---------------------------------------------
|
||||
callback login(string, string, string);
|
||||
callback logout();
|
||||
callback select-server(string);
|
||||
callback select-room(string);
|
||||
callback send-message(string, string); // reply-to-id (may be ""), text
|
||||
callback edit-message(string, string); // event id, new text
|
||||
callback delete-message(string);
|
||||
callback toggle-reaction(string, string);
|
||||
callback load-older();
|
||||
callback typing();
|
||||
callback not-implemented(string); // feature name, for stubs
|
||||
|
||||
if root.screen == Screen.loading: Rectangle {
|
||||
background: Theme.rail-bg;
|
||||
Text {
|
||||
text: "Tofu";
|
||||
color: Theme.text-muted;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
|
||||
if root.screen == Screen.login: LoginScreen {
|
||||
error: root.login-error;
|
||||
busy: root.login-busy;
|
||||
homeserver <=> root.login-homeserver;
|
||||
username <=> root.login-username;
|
||||
submit(hs, user, pass) => {
|
||||
root.login(hs, user, pass);
|
||||
}
|
||||
}
|
||||
|
||||
if root.screen == Screen.main: HorizontalLayout {
|
||||
// Left column: rail + channel list on top, "you" panel along the bottom.
|
||||
VerticalLayout {
|
||||
width: Theme.rail-width + Theme.sidebar-width;
|
||||
HorizontalLayout {
|
||||
ServerRail {
|
||||
servers: root.servers;
|
||||
selected-id: root.selected-server-id;
|
||||
home-unread: root.home-unread;
|
||||
home-mentions: root.home-mentions;
|
||||
select(id) => {
|
||||
root.selected-server-id = id;
|
||||
root.select-server(id);
|
||||
}
|
||||
add-server => {
|
||||
root.not-implemented("Create / join a space");
|
||||
}
|
||||
}
|
||||
|
||||
ChannelList {
|
||||
title: root.sidebar-title;
|
||||
is-home: root.selected-server-id == "";
|
||||
channels: root.channels;
|
||||
selected-id: root.selected-room-id;
|
||||
select(id) => {
|
||||
root.selected-room-id = id;
|
||||
root.select-room(id);
|
||||
}
|
||||
open-server-menu => {
|
||||
root.not-implemented("Space settings");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UserPanel {
|
||||
name: root.me-name;
|
||||
user-id: root.me-id;
|
||||
initial: root.me-initial;
|
||||
tint: root.me-color;
|
||||
logout => {
|
||||
root.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Right: chat
|
||||
VerticalLayout {
|
||||
ChatHeader {
|
||||
room-name: root.room-name;
|
||||
topic: root.room-topic;
|
||||
is-dm: root.room-is-dm;
|
||||
encrypted: root.room-encrypted;
|
||||
show-members <=> root.show-members;
|
||||
open-threads => {
|
||||
root.not-implemented("Threads");
|
||||
}
|
||||
open-pins => {
|
||||
root.not-implemented("Pinned messages");
|
||||
}
|
||||
open-search => {
|
||||
root.not-implemented("Search");
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
VerticalLayout {
|
||||
MessageList {
|
||||
messages: root.messages;
|
||||
room-name: root.room-name;
|
||||
loading-older: root.loading-older;
|
||||
room-nonce: root.room-nonce;
|
||||
reply(id, sender) => {
|
||||
root.editing-id = "";
|
||||
root.reply-to-id = id;
|
||||
root.reply-to-name = sender;
|
||||
root.composer-focus-nonce += 1;
|
||||
}
|
||||
edit(id, body) => {
|
||||
root.reply-to-id = "";
|
||||
root.editing-id = id;
|
||||
root.composer-text = body;
|
||||
root.composer-text-nonce += 1;
|
||||
}
|
||||
delete(id) => {
|
||||
root.delete-message(id);
|
||||
}
|
||||
react(id, key) => {
|
||||
root.toggle-reaction(id, key);
|
||||
}
|
||||
load-older => {
|
||||
root.load-older();
|
||||
}
|
||||
}
|
||||
|
||||
if root.room-open: Composer {
|
||||
text-to-set: root.composer-text;
|
||||
text-nonce: root.composer-text-nonce;
|
||||
focus-nonce: root.composer-focus-nonce;
|
||||
placeholder: root.room-is-dm ? "Message @\{root.room-name}" : "Message #\{root.room-name}";
|
||||
banner: root.editing-id != "" ? "Editing message" : (root.reply-to-id != "" ? "Replying to \{root.reply-to-name}" : "");
|
||||
editing: root.editing-id != "";
|
||||
typing-text: root.typing-text;
|
||||
submit(text) => {
|
||||
if (root.editing-id != "") {
|
||||
root.edit-message(root.editing-id, text);
|
||||
} else {
|
||||
root.send-message(root.reply-to-id, text);
|
||||
}
|
||||
root.editing-id = "";
|
||||
root.reply-to-id = "";
|
||||
}
|
||||
cancel-banner => {
|
||||
root.editing-id = "";
|
||||
root.reply-to-id = "";
|
||||
root.composer-text = "";
|
||||
root.composer-text-nonce += 1;
|
||||
}
|
||||
typing => {
|
||||
root.typing();
|
||||
}
|
||||
attach => {
|
||||
root.not-implemented("File uploads");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if root.show-members: MemberList {
|
||||
members: root.members;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transient banner for errors and "coming soon" notices.
|
||||
if root.toast != "": Rectangle {
|
||||
width: min(480px, parent.width - 32px);
|
||||
height: 40px;
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: parent.height - self.height - 96px;
|
||||
border-radius: 6px;
|
||||
background: Theme.popup-bg;
|
||||
border-width: 1px;
|
||||
border-color: Theme.divider;
|
||||
Text {
|
||||
x: 12px;
|
||||
width: parent.width - 24px;
|
||||
text: root.toast;
|
||||
color: Theme.text-normal;
|
||||
font-size: 14px;
|
||||
vertical-alignment: center;
|
||||
overflow: elide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user