Files
crossdesk/apps/desktop/src/gui/ui/message_panel.slint
T

222 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ScrollView } from "std-widgets.slint";
import { CompactButton, DialogSurface, IconButton, FontAwesomeIcons } from "common.slint";
export struct AnnouncementItem {
id: string,
revision: string,
title: string,
date: string,
read: bool,
}
export global MessageStrings {
in-out property <string> messages: "Messages";
in-out property <string> unread: "Unread";
in-out property <string> read: "Read";
in-out property <string> empty: "No announcements";
in-out property <string> select-message: "Select an announcement to read";
in-out property <string> loading: "Loading…";
in-out property <string> read-save-error: "Unable to save reading status locally. Open the announcement again to retry.";
in-out property <string> close: "Close";
in-out property <string> delete-message: "Delete";
in-out property <string> delete-title: "Delete announcement";
in-out property <string> delete-confirmation: "Delete this announcement?";
in-out property <string> delete-error: "Unable to delete this announcement. Please try again.";
in-out property <string> cancel: "Cancel";
}
export component MessagePanel inherits DialogSurface {
in property <[AnnouncementItem]> items;
in property <int> unread-count;
in property <int> total;
in property <bool> loading;
in property <bool> failed;
in property <bool> read-save-failed;
in property <bool> delete-failed;
in property <bool> loaded;
in property <bool> connected;
in property <string> selected-id;
in property <string> selected-title;
in property <styled-text> selected-body;
in property <string> selected-date;
in property <bool> selected-read;
callback open-message(string, string);
callback open-link(string);
callback delete-message(string, string);
callback load-more;
callback close;
private property <string> notice-text: root.delete-failed ? MessageStrings.delete-error
: root.read-save-failed ? MessageStrings.read-save-error : "";
private property <length> content-top: root.notice-text == "" ? 42px : 78px;
private property <string> pending-delete-id;
private property <string> pending-delete-revision;
function request-delete(id: string, revision: string) {
root.pending-delete-id = id;
root.pending-delete-revision = revision;
confirmation-keys.focus();
}
confirmation-keys := FocusScope {
width: 0px; height: 0px;
key-pressed(event) => {
if event.text == Key.Escape {
if root.pending-delete-id != "" { root.pending-delete-id = ""; }
else { root.close(); }
return accept;
}
return reject;
}
}
background: #f7f8fa;
TouchArea { }
Rectangle {
x: 1px; y: 1px; width: parent.width - 2px; height: 30px;
background: #e7ebf0; border-radius: 7px;
Rectangle { y: parent.height - 7px; height: 7px; background: #e7ebf0; }
message-heading := Text {
x: 12px; width: parent.width - 100px; height: parent.height;
text: MessageStrings.messages;
font-size: 14px; font-weight: 400; color: #111827;
vertical-alignment: center; overflow: elide;
}
if root.unread-count > 0: Rectangle {
x: message-heading.x + message-heading.preferred-width + 8px;
y: 5px; height: 20px;
width: max(20px, unread-count-label.preferred-width + 12px);
border-radius: 10px; background: #dbeafe;
accessible-role: text;
accessible-label: MessageStrings.unread + " " + root.unread-count;
unread-count-label := Text {
text: "" + root.unread-count; color: #1d4ed8;
font-size: 12px; font-weight: 600;
horizontal-alignment: center; vertical-alignment: center;
accessible-role: none;
}
}
if root.loading: Text {
x: parent.width - 150px; width: 110px; height: parent.height;
text: MessageStrings.loading; color: #4b5563; font-size: 12px;
horizontal-alignment: right; vertical-alignment: center;
}
Rectangle {
x: parent.width - 31px; y: 3px; width: 25px; height: 24px;
border-radius: 3px;
background: close-touch.has-hover ? #d8dde5 : transparent;
accessible-role: button;
accessible-label: MessageStrings.close;
accessible-action-default => { root.close(); }
Text { text: "×"; font-size: 20px; color: #4a5568; horizontal-alignment: center; vertical-alignment: center; }
close-touch := TouchArea { mouse-cursor: pointer; clicked => { root.close(); } }
}
}
if root.notice-text != "": Rectangle {
x: 12px; y: 40px; width: parent.width - 24px; height: 28px;
border-radius: 4px;
background: #fef2f2;
Text {
x: 8px; width: parent.width - 16px; height: parent.height;
text: root.notice-text; font-size: 12px; overflow: elide;
color: #b42318;
vertical-alignment: center;
}
}
Rectangle {
x: 12px; y: root.content-top; width: 180px; height: parent.height - self.y - 12px;
background: white; border-width: 1px; border-color: #e2e5e9;
announcements-scroll := ScrollView {
width: parent.width; height: parent.height;
VerticalLayout {
width: 160px; spacing: 3px; padding: 5px;
for item in root.items: Rectangle {
height: 56px; width: 150px;
background: root.selected-id == item.id ? #eaf2ff : entry-touch.has-hover ? #f1f5fa : white;
border-radius: 4px;
Text { x: 8px; y: 5px; width: parent.width - 28px; height: 22px; text: item.title; overflow: elide; font-size: 14px; font-weight: item.read ? 400 : 600; color: item.read ? #374151 : #111827; vertical-alignment: center; }
if !item.read: Rectangle {
x: parent.width - 18px; y: 13px; width: 6px; height: 6px;
border-radius: 3px; background: #2563eb;
accessible-role: none;
}
Text { x: 8px; y: 32px; width: parent.width - 42px; text: item.date; color: #4b5563; font-size: 12px; overflow: elide; }
accessible-role: button;
accessible-label: item.title + " " + (item.read ? MessageStrings.read : MessageStrings.unread);
accessible-action-default => { root.open-message(item.id, item.revision); }
entry-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-message(item.id, item.revision); } }
IconButton {
x: parent.width - 25px; y: 29px; width: 20px; height: 20px;
icon: FontAwesomeIcons.xmark; icon-size: 12px;
icon-color: #667085; danger: true;
accessible-role: button;
accessible-label: MessageStrings.delete-message + " " + item.title;
accessible-action-default => { root.request-delete(item.id, item.revision); }
clicked => { root.request-delete(item.id, item.revision); }
}
}
}
}
if root.items.length == 0 && root.total == 0 && root.loaded && root.connected && !root.loading && !root.failed: Text { x: 8px; width: parent.width - 16px; text: MessageStrings.empty; wrap: word-wrap; color: #4b5563; font-size: 14px; horizontal-alignment: center; vertical-alignment: center; }
}
Rectangle {
x: 202px; y: root.content-top; width: parent.width - 214px; height: parent.height - self.y - 12px;
background: white; border-width: 1px; border-color: #e2e5e9;
if root.selected-id == "" && root.items.length > 0: Text { x: 14px; width: parent.width - 28px; text: MessageStrings.select-message; color: #4b5563; font-size: 14px; wrap: word-wrap; horizontal-alignment: center; vertical-alignment: center; }
if root.selected-id != "": ScrollView {
width: parent.width; height: parent.height;
VerticalLayout {
width: root.width - 234px; padding: 12px; spacing: 10px; alignment: start;
Text { vertical-stretch: 0; text: root.selected-title; wrap: word-wrap; font-size: 16px; font-weight: 600; color: #202124; }
Text { vertical-stretch: 0; text: root.selected-date; accessible-label: root.selected-date + " · " + (root.selected-read ? MessageStrings.read : MessageStrings.unread); color: #4b5563; font-size: 12px; wrap: word-wrap; }
StyledText {
vertical-stretch: 0;
text: root.selected-body;
default-color: #202124; default-font-size: 14px;
link-color: #2563eb;
link-clicked(url) => { root.open-link(url); }
}
}
}
}
Timer {
interval: 100ms;
running: root.connected && !root.loading && !root.failed
&& root.pending-delete-id == "" && root.items.length > 0
&& root.items.length < root.total && announcements-scroll.viewport-height > 0px
&& announcements-scroll.viewport-height + announcements-scroll.viewport-y <= announcements-scroll.height + 80px;
triggered => { root.load-more(); }
}
if root.pending-delete-id != "": Rectangle {
background: #00000042; border-radius: 8px; z: 30;
TouchArea { clicked => { confirmation-keys.focus(); } }
DialogSurface {
x: (parent.width - self.width) / 2;
y: (parent.height - self.height) / 2;
width: 230px; height: confirmation-content.preferred-height;
TouchArea { clicked => { confirmation-keys.focus(); } }
confirmation-content := VerticalLayout {
padding: 16px;
padding-top: 26px;
padding-bottom: 26px;
spacing: 20px;
Text { text: MessageStrings.delete-confirmation; font-size: 14px; color: #202124; wrap: word-wrap; horizontal-alignment: center; }
HorizontalLayout {
alignment: center; spacing: 8px;
cancel-delete-button := CompactButton {
width: 72px; text-size: 13px; text: MessageStrings.cancel;
accessible-role: button; accessible-label: MessageStrings.cancel;
accessible-action-default => { cancel-delete-button.clicked(); }
clicked => { root.pending-delete-id = ""; }
}
confirm-delete-button := CompactButton {
width: 72px; text-size: 13px; text: MessageStrings.delete-message; primary: true;
accessible-role: button; accessible-label: MessageStrings.delete-title;
accessible-action-default => { confirm-delete-button.clicked(); }
clicked => {
root.delete-message(root.pending-delete-id, root.pending-delete-revision);
root.pending-delete-id = "";
}
}
}
}
}
}
}