mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-19 02:13:03 +08:00
222 lines
11 KiB
Plaintext
222 lines
11 KiB
Plaintext
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 = "";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|