615 lines
38 KiB
Plaintext
615 lines
38 KiB
Plaintext
import { Button, ComboBox, LineEdit, ListView, ScrollView, Spinner } from "std-widgets.slint";
|
|
export {
|
|
ActivityRow, CommentRow, CommitRow, DiffLine, FileRow, GraphLane, HeatCell, IssueRow,
|
|
LabelRow, MarkdownBlock, PullRow, RepositoryRow, ServerRow,
|
|
} from "types.slint";
|
|
import {
|
|
ActivityRow, CommentRow, CommitRow, DiffLine, FileRow, GraphLane, HeatCell, IssueRow,
|
|
MarkdownBlock, PullRow, RepositoryRow, ServerRow,
|
|
} from "types.slint";
|
|
export { AppTheme, IconTheme } from "components.slint";
|
|
import {
|
|
AppearanceChoice, AppIcon, AppTheme, CommentCard, EdgeBack, EmptyState, FileCard, Header,
|
|
IconStyleChoice, MarkdownContent, PullToRefreshGesture, TabButton,
|
|
} from "components.slint";
|
|
|
|
export component AppWindow inherits Window {
|
|
title: "Gotcha";
|
|
preferred-width: 390px;
|
|
preferred-height: 844px;
|
|
background: AppTheme.page;
|
|
private property <length> toolbar-height: 68px;
|
|
|
|
in-out property <string> tab: "home";
|
|
in-out property <string> page: "servers";
|
|
in-out property <[ServerRow]> servers;
|
|
in-out property <[RepositoryRow]> repositories;
|
|
in-out property <[IssueRow]> issues;
|
|
in-out property <[PullRow]> pulls;
|
|
in-out property <[CommentRow]> comments;
|
|
in-out property <[CommitRow]> commits;
|
|
in-out property <[FileRow]> files;
|
|
in-out property <[ActivityRow]> activities;
|
|
in-out property <[HeatCell]> heat_cells;
|
|
in-out property <int> contribution_count;
|
|
in-out property <bool> has_active_server;
|
|
in-out property <string> home_server;
|
|
in-out property <string> server_title;
|
|
in-out property <string> repository_title;
|
|
in-out property <string> issue_title;
|
|
in-out property <string> issue_meta;
|
|
in-out property <[MarkdownBlock]> issue_body;
|
|
in-out property <string> detail_title;
|
|
in-out property <string> detail_meta;
|
|
in-out property <string> detail_files_ref;
|
|
in-out property <[MarkdownBlock]> detail_body;
|
|
in-out property <[DiffLine]> diff_lines;
|
|
in-out property <int> diff_columns;
|
|
in-out property <string> issue_filter: "open";
|
|
in-out property <string> pull_filter: "open";
|
|
in-out property <[string]> branch_choices;
|
|
in-out property <int> branch_index;
|
|
in-out property <bool> filter_open;
|
|
in-out property <bool> loading;
|
|
in-out property <bool> home_loading;
|
|
in-out property <bool> refreshing;
|
|
in-out property <string> error;
|
|
in-out property <int> home_scroll_request;
|
|
in-out property <int> servers_scroll_request;
|
|
in-out property <int> repositories_scroll_request;
|
|
in-out property <int> issues_scroll_request;
|
|
in-out property <int> issue_scroll_request;
|
|
private property <bool> home_at_top: true;
|
|
private property <bool> repositories_at_top: true;
|
|
private property <bool> issues_at_top: true;
|
|
private property <bool> issue_at_top: true;
|
|
|
|
callback select_tab(string);
|
|
callback refresh_home();
|
|
callback refresh_repositories();
|
|
callback refresh_issues();
|
|
callback refresh_issue();
|
|
callback refresh_pulls();
|
|
callback refresh_pull();
|
|
callback refresh_commits();
|
|
callback show_add_server();
|
|
callback save_server(string, string, string);
|
|
callback select_server(int);
|
|
callback select_repository(string, string);
|
|
callback toggle_favorite(string, string);
|
|
callback select_issue(int);
|
|
callback select_pull(string, string, int);
|
|
callback select_commit(string);
|
|
callback select_branch(string);
|
|
callback select_file(string);
|
|
callback select_pull_file(string);
|
|
callback select_activity(string, string, string, int, string);
|
|
callback choose_filter(string);
|
|
callback choose_icon_style(int);
|
|
callback choose_appearance(int);
|
|
callback back();
|
|
|
|
if tab == "home": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: root.has_active_server ? root.home_server : "Home"; can_back: false; }
|
|
|
|
if !root.has_active_server: EmptyState {
|
|
x: 32px; width: parent.width - 64px; y: 210px; height: 190px;
|
|
title: "No server selected";
|
|
detail: "Open Issues to select a server or add your first one.";
|
|
}
|
|
|
|
if root.has_active_server: Rectangle {
|
|
x: 16px; y: 72px; width: parent.width - 32px; height: 118px;
|
|
background: AppTheme.surface; border-radius: 12px;
|
|
Text { x: 13px; y: 10px; text: "Activity · last 12 months"; font-size: 13px; font-weight: 600; color: AppTheme.text; }
|
|
Text { x: 13px; y: 86px; text: root.contribution_count + " contributions"; font-size: 12px; color: AppTheme.text-secondary; }
|
|
for cell in root.heat_cells: Rectangle {
|
|
x: 13px + cell.week * 6.7px; y: 39px + cell.day * 6.2px;
|
|
width: 5.2px; height: 5.2px; border-radius: 1px;
|
|
background: cell.level == 0 ? (AppTheme.dark ? #3a3a3c : #e5e5e9) : cell.level == 1 ? #b8d9f4 : cell.level == 2 ? #72b5e8 : cell.level == 3 ? #278bd4 : #0969b7;
|
|
}
|
|
}
|
|
|
|
if root.has_active_server && !root.home_loading && root.activities.length == 0: EmptyState {
|
|
x: 32px; width: parent.width - 64px; y: 250px; height: 150px;
|
|
title: "No recent activity";
|
|
detail: "Your recent server actions will appear here.";
|
|
}
|
|
|
|
if root.has_active_server: PullToRefreshGesture {
|
|
x: 16px; y: 202px; width: parent.width - 32px; height: parent.height - 214px;
|
|
at_top: root.home_at_top; refreshing: root.home_loading;
|
|
refresh => { root.refresh_home(); }
|
|
home_list := ListView {
|
|
property <int> scroll_request: root.home_scroll_request;
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
changed scroll_request => { self.viewport-y = 0px; root.home_at_top = true; }
|
|
scrolled => { root.home_at_top = self.viewport-y >= 0px; }
|
|
for activity in root.activities: Rectangle {
|
|
height: 94px;
|
|
background: AppTheme.surface; border-radius: 12px;
|
|
TouchArea {
|
|
enabled: activity.target != "";
|
|
clicked => { root.select_activity(activity.target, activity.owner, activity.repository, activity.number, activity.sha); }
|
|
}
|
|
Rectangle {
|
|
x: 12px; y: 14px; width: 34px; height: 34px; border-radius: 17px;
|
|
background: AppTheme.accent-soft;
|
|
AppIcon { x: 5px; y: 5px; width: 24px; height: 24px; kind: activity.icon; }
|
|
}
|
|
Text { x: 57px; y: 11px; width: parent.width - 69px; height: 24px; text: activity.title; font-size: 15px; font-weight: 600; color: AppTheme.text; overflow: elide; }
|
|
Text { x: 57px; y: 36px; width: parent.width - 69px; height: 35px; text: activity.detail; font-size: 13px; color: AppTheme.text-secondary; wrap: word-wrap; overflow: elide; }
|
|
Text { x: 57px; y: 72px; width: parent.width - 69px; text: activity.meta; font-size: 11px; color: AppTheme.text-tertiary; overflow: elide; }
|
|
}
|
|
}
|
|
}
|
|
|
|
if root.home_loading: Spinner { width: 34px; height: 34px; x: (parent.width - self.width) / 2; y: 222px; }
|
|
}
|
|
|
|
if tab == "settings": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header {
|
|
x: 0; y: 0; can_back: false;
|
|
title: "Settings";
|
|
}
|
|
Rectangle {
|
|
x: 16px; y: 76px; width: parent.width - 32px; height: 184px;
|
|
background: AppTheme.surface; border-radius: 12px;
|
|
Text { x: 15px; y: 13px; text: "Icon style"; font-size: 18px; font-weight: 650; color: AppTheme.text; }
|
|
Text { x: 15px; y: 43px; width: parent.width - 30px; text: "Choose how navigation and activity icons look throughout Gotcha."; font-size: 13px; color: AppTheme.text-secondary; wrap: word-wrap; }
|
|
IconStyleChoice { x: 12px; y: 91px; width: (parent.width - 36px) / 3; height: 78px; value: 0; label: "Outline"; clicked => { root.choose_icon_style(0); } }
|
|
IconStyleChoice { x: 18px + (parent.width - 36px) / 3; y: 91px; width: (parent.width - 36px) / 3; height: 78px; value: 1; label: "Duotone"; clicked => { root.choose_icon_style(1); } }
|
|
IconStyleChoice { x: 24px + 2 * (parent.width - 36px) / 3; y: 91px; width: (parent.width - 36px) / 3; height: 78px; value: 2; label: "Bold"; clicked => { root.choose_icon_style(2); } }
|
|
}
|
|
Rectangle {
|
|
x: 16px; y: 276px; width: parent.width - 32px; height: 136px;
|
|
background: AppTheme.surface; border-radius: 12px;
|
|
Text { x: 15px; y: 13px; text: "Appearance"; font-size: 18px; font-weight: 650; color: AppTheme.text; }
|
|
Text { x: 15px; y: 43px; width: parent.width - 30px; text: "Follow iOS automatically or choose a fixed light or dark appearance."; font-size: 13px; color: AppTheme.text-secondary; wrap: word-wrap; }
|
|
AppearanceChoice { x: 12px; y: 81px; width: (parent.width - 36px) / 3; height: 42px; value: 0; label: "Auto"; clicked => { root.choose_appearance(0); } }
|
|
AppearanceChoice { x: 18px + (parent.width - 36px) / 3; y: 81px; width: (parent.width - 36px) / 3; height: 42px; value: 1; label: "Light"; clicked => { root.choose_appearance(1); } }
|
|
AppearanceChoice { x: 24px + 2 * (parent.width - 36px) / 3; y: 81px; width: (parent.width - 36px) / 3; height: 42px; value: 2; label: "Dark"; clicked => { root.choose_appearance(2); } }
|
|
}
|
|
}
|
|
|
|
if (tab == "issues" || tab == "repositories") && page == "servers": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: "Servers"; can_back: false; }
|
|
if root.servers.length > 0: servers_list := ListView {
|
|
property <int> scroll_request: root.servers_scroll_request;
|
|
x: 16px; y: 76px; width: parent.width - 32px; height: parent.height - 154px;
|
|
mouse-drag-pan-enabled: true;
|
|
changed scroll_request => { self.viewport-y = 0px; }
|
|
for server[index] in root.servers: Rectangle {
|
|
height: 76px;
|
|
background: AppTheme.surface;
|
|
border-color: AppTheme.border;
|
|
border-width: 1px;
|
|
border-radius: 12px;
|
|
TouchArea { clicked => { root.select_server(index); } }
|
|
Text { x: 16px; y: 13px; width: parent.width - 54px; text: server.name; font-size: 17px; font-weight: 600; color: AppTheme.text; overflow: elide; }
|
|
Text { x: 16px; y: 42px; width: parent.width - 54px; text: server.url; font-size: 13px; color: AppTheme.text-secondary; overflow: elide; }
|
|
AppIcon { x: parent.width - 29px; y: 26px; width: 20px; height: 20px; kind: "disclosure"; icon-color: AppTheme.text-tertiary; }
|
|
}
|
|
}
|
|
Button { x: 16px; y: parent.height - 66px; width: parent.width - 32px; height: 48px; text: "Add Server"; clicked => { root.show_add_server(); } }
|
|
}
|
|
|
|
if (tab == "issues" || tab == "repositories") && page == "add": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: "Add Server"; can_back: true; back => { root.back(); } }
|
|
VerticalLayout {
|
|
x: 20px; y: 92px; width: parent.width - 40px; height: 260px; spacing: 10px;
|
|
Text { text: "Name"; font-size: 13px; color: AppTheme.text-secondary; }
|
|
server_name := LineEdit { placeholder-text: "Work"; }
|
|
Text { text: "Server URL"; font-size: 13px; color: AppTheme.text-secondary; }
|
|
server_url := LineEdit { placeholder-text: "https://gitea.example.com"; }
|
|
Text { text: "Access token"; font-size: 13px; color: AppTheme.text-secondary; }
|
|
server_token := LineEdit { placeholder-text: "Token"; input-type: InputType.password; }
|
|
}
|
|
Button {
|
|
x: 20px; y: 380px; width: parent.width - 40px; height: 48px;
|
|
text: root.loading ? "Connecting…" : "Add Server";
|
|
enabled: !root.loading;
|
|
clicked => { root.save_server(server_name.text, server_url.text, server_token.text); }
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if (tab == "issues" || tab == "repositories") && page == "repositories": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: root.server_title; can_back: true; back => { root.back(); } }
|
|
if root.refreshing: Spinner { x: parent.width - 42px; y: 17px; width: 24px; height: 24px; }
|
|
if !root.loading && root.repositories.length == 0: EmptyState {
|
|
x: 34px; width: parent.width - 68px; y: 220px; height: 160px;
|
|
title: "No repositories";
|
|
detail: "This account does not own any repositories on this server.";
|
|
}
|
|
PullToRefreshGesture {
|
|
x: 20px; y: 70px; width: parent.width - 40px; height: parent.height - 82px;
|
|
at_top: root.repositories_at_top; refreshing: root.refreshing;
|
|
refresh => { root.refresh_repositories(); }
|
|
repositories_list := ListView {
|
|
property <int> scroll_request: root.repositories_scroll_request;
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
changed scroll_request => { self.viewport-y = 0px; root.repositories_at_top = true; }
|
|
scrolled => { root.repositories_at_top = self.viewport-y >= 0px; }
|
|
for repo in root.repositories: Rectangle {
|
|
height: 104px;
|
|
background: AppTheme.surface;
|
|
border-color: AppTheme.border;
|
|
border-width: 1px;
|
|
border-radius: 12px;
|
|
TouchArea { clicked => { root.select_repository(repo.owner, repo.name); } }
|
|
Text { x: 15px; y: 11px; width: parent.width - 62px; text: repo.name; font-size: 17px; font-weight: 600; color: AppTheme.text; overflow: elide; }
|
|
Text { x: 15px; y: 39px; width: parent.width - 30px; text: repo.description; font-size: 14px; color: AppTheme.text-secondary; overflow: elide; }
|
|
Text { x: 15px; y: 71px; width: parent.width - 30px; text: repo.meta; font-size: 12px; color: AppTheme.text-tertiary; overflow: elide; }
|
|
TouchArea {
|
|
x: parent.width - 52px; y: 2px; width: 48px; height: 48px;
|
|
clicked => { root.toggle_favorite(repo.owner, repo.name); }
|
|
AppIcon { x: 10px; y: 10px; width: 28px; height: 28px; kind: repo.favorite ? "favorite-filled" : "favorite"; icon-color: repo.favorite ? #f2a900 : AppTheme.text-tertiary; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if tab == "issues" && page == "issues": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header {
|
|
x: 0; y: 0; title: root.repository_title; can_back: true; can_filter: true;
|
|
back => { root.back(); }
|
|
filter => { root.filter_open = true; }
|
|
}
|
|
if root.refreshing: Spinner { x: parent.width - 42px; y: 17px; width: 24px; height: 24px; }
|
|
if !root.loading && root.issues.length == 0: EmptyState {
|
|
x: 34px; width: parent.width - 68px; y: 220px; height: 160px;
|
|
title: "No " + root.issue_filter + " issues";
|
|
detail: "No issues match the selected status.";
|
|
}
|
|
PullToRefreshGesture {
|
|
x: 20px; y: 70px; width: parent.width - 40px; height: parent.height - 82px;
|
|
at_top: root.issues_at_top; refreshing: root.refreshing;
|
|
refresh => { root.refresh_issues(); }
|
|
issues_list := ListView {
|
|
property <int> scroll_request: root.issues_scroll_request;
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
changed scroll_request => { self.viewport-y = 0px; root.issues_at_top = true; }
|
|
scrolled => { root.issues_at_top = self.viewport-y >= 0px; }
|
|
for issue in root.issues: Rectangle {
|
|
height: issue.labels.length > 0 ? 138px : 112px;
|
|
background: AppTheme.surface;
|
|
border-color: AppTheme.border;
|
|
border-width: 1px;
|
|
border-radius: 12px;
|
|
TouchArea { clicked => { root.select_issue(issue.number); } }
|
|
Text { x: 15px; y: 11px; width: parent.width - 44px; text: issue.title; font-size: 16px; font-weight: 600; color: AppTheme.text; overflow: elide; }
|
|
Text { x: 15px; y: 39px; width: parent.width - 30px; height: 38px; text: issue.summary; font-size: 13px; color: AppTheme.text-secondary; wrap: word-wrap; overflow: elide; }
|
|
if issue.labels.length > 0: HorizontalLayout {
|
|
x: 15px; y: 82px; width: parent.width - 30px; height: 22px; spacing: 5px;
|
|
for label in issue.labels: Rectangle {
|
|
width: min(104px, max(48px, (parent.width - (issue.labels.length - 1) * 5px) / issue.labels.length));
|
|
height: 22px; background: label.background; border-radius: 11px;
|
|
Text { x: 7px; width: parent.width - 14px; text: label.name; color: label.foreground; font-size: 11px; font-weight: 600; horizontal-alignment: center; vertical-alignment: center; overflow: elide; }
|
|
}
|
|
}
|
|
Text { x: 15px; y: issue.labels.length > 0 ? 112px : 86px; width: parent.width - 30px; text: issue.meta; font-size: 12px; color: AppTheme.text-tertiary; overflow: elide; }
|
|
}
|
|
}
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if tab == "issues" && page == "issue": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: "Issue"; can_back: true; back => { root.back(); } }
|
|
if root.refreshing: Spinner { x: parent.width - 42px; y: 17px; width: 24px; height: 24px; }
|
|
PullToRefreshGesture {
|
|
x: 18px; y: 78px; width: parent.width - 36px; height: parent.height - 96px;
|
|
at_top: root.issue_at_top; refreshing: root.refreshing;
|
|
refresh => { root.refresh_issue(); }
|
|
issue_scroll := ScrollView {
|
|
property <int> scroll_request: root.issue_scroll_request;
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
changed scroll_request => { self.viewport-y = 0px; root.issue_at_top = true; }
|
|
scrolled => { root.issue_at_top = self.viewport-y >= 0px; }
|
|
VerticalLayout {
|
|
width: parent.width;
|
|
spacing: 12px;
|
|
alignment: start;
|
|
Text { text: root.issue_title; font-size: 23px; font-weight: 650; color: AppTheme.text; wrap: word-wrap; }
|
|
Text { text: root.issue_meta; font-size: 13px; color: AppTheme.text-secondary; wrap: word-wrap; }
|
|
Rectangle { height: 1px; background: AppTheme.separator; }
|
|
MarkdownContent { width: parent.width; blocks: root.issue_body; }
|
|
if root.comments.length > 0: Text { text: "Comments"; font-size: 17px; font-weight: 600; color: AppTheme.text; }
|
|
for comment in root.comments: CommentCard { width: parent.width; comment: comment; }
|
|
}
|
|
}
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if tab == "pulls" && !root.has_active_server: Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: "Home"; can_back: false; }
|
|
EmptyState {
|
|
x: 32px; width: parent.width - 64px; y: 210px; height: 190px;
|
|
title: "No server selected";
|
|
detail: "Open Issues or Repositories to select a server or add your first one.";
|
|
}
|
|
}
|
|
|
|
if tab == "pulls" && root.has_active_server && page == "pulls": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header {
|
|
x: 0; y: 0; title: "Pull Requests"; can_back: false; can_filter: true;
|
|
filter => { root.filter_open = true; }
|
|
}
|
|
if root.refreshing: Spinner { x: parent.width - 92px; y: 17px; width: 24px; height: 24px; }
|
|
if !root.loading && root.pulls.length == 0: EmptyState {
|
|
x: 34px; width: parent.width - 68px; y: 220px; height: 160px;
|
|
title: "No " + root.pull_filter + " pull requests";
|
|
detail: "No pull requests match the selected status.";
|
|
}
|
|
PullToRefreshGesture {
|
|
x: 16px; y: 70px; width: parent.width - 32px; height: parent.height - 82px;
|
|
at_top: root.issues_at_top; refreshing: root.refreshing;
|
|
refresh => { root.refresh_pulls(); }
|
|
ListView {
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
scrolled => { root.issues_at_top = self.viewport-y >= 0px; }
|
|
for pull in root.pulls: Rectangle {
|
|
height: 112px;
|
|
background: AppTheme.surface; border-color: AppTheme.border; border-width: 1px; border-radius: 12px;
|
|
TouchArea { clicked => { root.select_pull(pull.owner, pull.repository, pull.number); } }
|
|
Text { x: 14px; y: 10px; width: parent.width - 28px; text: pull.repository + " #" + pull.number; font-size: 12px; color: AppTheme.text-secondary; overflow: elide; }
|
|
Text { x: 14px; y: 31px; width: parent.width - 28px; text: pull.title; font-size: 16px; font-weight: 600; color: AppTheme.text; overflow: elide; }
|
|
Text { x: 14px; y: 57px; width: parent.width - 28px; text: pull.summary; font-size: 13px; color: AppTheme.text-secondary; overflow: elide; }
|
|
Text { x: 14px; y: 85px; width: parent.width - 28px; text: pull.meta; font-size: 12px; color: AppTheme.text-tertiary; overflow: elide; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if tab == "pulls" && page == "pull": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: "Pull Request"; can_back: true; back => { root.back(); } }
|
|
if root.refreshing: Spinner { x: parent.width - 42px; y: 17px; width: 24px; height: 24px; }
|
|
PullToRefreshGesture {
|
|
x: 18px; y: 78px; width: parent.width - 36px; height: parent.height - 96px;
|
|
at_top: root.issue_at_top; refreshing: root.refreshing;
|
|
refresh => { root.refresh_pull(); }
|
|
ScrollView {
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
scrolled => { root.issue_at_top = self.viewport-y >= 0px; }
|
|
VerticalLayout {
|
|
width: parent.width; spacing: 12px; alignment: start;
|
|
Text { text: root.detail_title; font-size: 23px; font-weight: 650; color: AppTheme.text; wrap: word-wrap; }
|
|
Text { text: root.detail_meta; font-size: 13px; color: AppTheme.text-secondary; wrap: word-wrap; }
|
|
Rectangle { height: 1px; background: AppTheme.separator; }
|
|
MarkdownContent { width: parent.width; blocks: root.detail_body; }
|
|
if root.files.length > 0: Text { text: root.detail_files_ref; font-size: 17px; font-weight: 600; color: AppTheme.text; }
|
|
for file in root.files: FileCard { width: parent.width; file: file; clicked => { root.select_pull_file(file.path); } }
|
|
if root.comments.length > 0: Text { text: "Comments"; font-size: 17px; font-weight: 600; color: AppTheme.text; }
|
|
for comment in root.comments: CommentCard { width: parent.width; comment: comment; }
|
|
}
|
|
}
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if tab == "repositories" && page == "commits": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: root.repository_title; can_back: true; back => { root.back(); } }
|
|
ComboBox {
|
|
x: 16px; y: 66px; width: parent.width - 32px; height: 42px;
|
|
model: root.branch_choices; current-index: root.branch_index;
|
|
selected(value) => { root.select_branch(value); }
|
|
}
|
|
if root.refreshing: Spinner { x: parent.width - 42px; y: 17px; width: 24px; height: 24px; }
|
|
if !root.loading && root.commits.length == 0: EmptyState {
|
|
x: 34px; width: parent.width - 68px; y: 220px; height: 160px;
|
|
title: "No commits";
|
|
detail: "This repository has no commit history.";
|
|
}
|
|
PullToRefreshGesture {
|
|
x: 16px; y: 116px; width: parent.width - 32px; height: parent.height - 128px;
|
|
at_top: root.repositories_at_top; refreshing: root.refreshing;
|
|
refresh => { root.refresh_commits(); }
|
|
ListView {
|
|
width: 100%; height: 100%; mouse-drag-pan-enabled: true;
|
|
scrolled => { root.repositories_at_top = self.viewport-y >= 0px; }
|
|
for commit in root.commits: commit_row := Rectangle {
|
|
property <length> graph-width: commit.lanes.length > 0 ? min(72px, commit.lanes.length * 10px + 8px) : 0px;
|
|
property <length> lane-spacing: (self.graph-width - 8px) / max(1, commit.lanes.length);
|
|
height: 86px; background: transparent;
|
|
Rectangle { width: 100%; height: 100%; background: AppTheme.surface; border-color: AppTheme.border; border-width: 1px; border-radius: 12px; }
|
|
TouchArea { clicked => { root.select_commit(commit.sha); } }
|
|
for lane[index] in commit.lanes: Rectangle {
|
|
x: 4px + index * commit_row.lane-spacing;
|
|
width: 3px; height: 100%; background: transparent;
|
|
if lane.top: Rectangle {
|
|
y: -1px; width: 3px;
|
|
height: lane.connect_left && !lane.bottom ? 37px : 42px;
|
|
background: lane.color;
|
|
}
|
|
if lane.bottom: Rectangle {
|
|
y: lane.connect_left && !lane.top ? 45px : 40px;
|
|
width: 3px;
|
|
height: parent.height - (lane.connect_left && !lane.top ? 44px : 39px);
|
|
background: lane.color;
|
|
}
|
|
if lane.connect_left && lane.top == lane.bottom: Rectangle {
|
|
x: -commit_row.lane-spacing; y: 39px;
|
|
width: commit_row.lane-spacing + 3px; height: 3px;
|
|
background: lane.color;
|
|
}
|
|
if lane.connect_left && lane.bottom && !lane.top: Path {
|
|
x: 1.5px - commit_row.lane-spacing; y: 35px;
|
|
width: commit_row.lane-spacing; height: 11px;
|
|
viewbox-width: 10; viewbox-height: 11;
|
|
commands: "M 0 5.5 L 5 5.5 Q 10 5.5 10 10.5";
|
|
fill: transparent; stroke: lane.color; stroke-width: 3px; stroke-line-cap: round;
|
|
}
|
|
if lane.connect_left && lane.top && !lane.bottom: Path {
|
|
x: 1.5px - commit_row.lane-spacing; y: 35px;
|
|
width: commit_row.lane-spacing; height: 11px;
|
|
viewbox-width: 10; viewbox-height: 11;
|
|
commands: "M 10 0.5 Q 10 5.5 5 5.5 L 0 5.5";
|
|
fill: transparent; stroke: lane.color; stroke-width: 3px; stroke-line-cap: round;
|
|
}
|
|
if lane.node: Rectangle {
|
|
x: -3px; y: 36px; width: 9px; height: 9px; border-radius: 5px;
|
|
background: lane.color; border-color: AppTheme.surface; border-width: 2px;
|
|
}
|
|
}
|
|
Text { x: parent.graph-width + 14px; y: 8px; width: parent.width - self.x - 14px; text: commit.title; font-size: 15px; font-weight: 600; color: AppTheme.text; overflow: elide; }
|
|
Text { x: parent.graph-width + 14px; y: 33px; width: parent.width - self.x - 14px; text: commit.refs != "" ? commit.refs : commit.meta; font-size: 12px; color: commit.refs != "" ? AppTheme.accent : AppTheme.text-secondary; overflow: elide; }
|
|
Text { x: parent.graph-width + 14px; y: 53px; width: parent.width - self.x - 14px; text: commit.refs != "" ? commit.meta + " · " + commit.sha : commit.sha; font-size: 11px; color: AppTheme.text-tertiary; overflow: elide; }
|
|
}
|
|
}
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if tab == "repositories" && page == "files": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: "Changed Files"; can_back: true; back => { root.back(); } }
|
|
if !root.loading && root.files.length == 0: EmptyState {
|
|
x: 34px; width: parent.width - 68px; y: 220px; height: 160px;
|
|
title: "No changed files";
|
|
detail: "This commit does not contain file changes.";
|
|
}
|
|
ListView {
|
|
x: 16px; y: 70px; width: parent.width - 32px; height: parent.height - 82px; mouse-drag-pan-enabled: true;
|
|
for file in root.files: FileCard { file: file; clicked => { root.select_file(file.path); } }
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
if (tab == "repositories" || tab == "pulls") && page == "diff": Rectangle {
|
|
x: root.safe-area-insets.left; y: root.safe-area-insets.top;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.height - root.safe-area-insets.top - root.safe-area-insets.bottom - root.toolbar-height;
|
|
background: AppTheme.page;
|
|
Header { x: 0; y: 0; title: root.detail_title; can_back: true; back => { root.back(); } }
|
|
ScrollView {
|
|
x: 12px; y: 70px; width: parent.width - 24px; height: parent.height - 82px; mouse-drag-pan-enabled: true;
|
|
viewport-width: max(self.visible-width, root.diff_columns * 7px + 100px);
|
|
viewport-height: diff_rows.preferred-height;
|
|
diff_rows := VerticalLayout {
|
|
width: parent.viewport-width;
|
|
for line in root.diff_lines: Rectangle {
|
|
height: 20px;
|
|
background: line.kind == "addition" ? (AppTheme.dark ? #173d25 : #ddf5e3) : line.kind == "removal" ? (AppTheme.dark ? #4a2020 : #fde2e1) : line.kind == "hunk" ? (AppTheme.dark ? #17324d : #dcecff) : line.kind == "header" ? AppTheme.surface-muted : AppTheme.surface;
|
|
Text { x: 0; width: 38px; text: line.old_number; font-family: "monospace"; font-size: 11px; color: AppTheme.text-tertiary; horizontal-alignment: right; vertical-alignment: center; }
|
|
Text { x: 42px; width: 38px; text: line.new_number; font-family: "monospace"; font-size: 11px; color: AppTheme.text-tertiary; horizontal-alignment: right; vertical-alignment: center; }
|
|
Rectangle { x: 84px; width: 1px; height: 100%; background: AppTheme.separator; }
|
|
Text {
|
|
x: 90px; width: parent.width - 94px; text: line.text; font-family: "monospace"; font-size: 11px;
|
|
color: line.kind == "addition" ? (AppTheme.dark ? #7fe787 : #176b2c) : line.kind == "removal" ? (AppTheme.dark ? #ff7b72 : #a12720) : line.kind == "hunk" ? (AppTheme.dark ? #79c0ff : #175b9e) : AppTheme.text;
|
|
wrap: no-wrap; overflow: clip; vertical-alignment: center;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
EdgeBack { x: 0; y: 0; back => { root.back(); } }
|
|
}
|
|
|
|
Rectangle {
|
|
x: root.safe-area-insets.left;
|
|
y: root.height - root.safe-area-insets.bottom - root.toolbar-height;
|
|
width: root.width - root.safe-area-insets.left - root.safe-area-insets.right;
|
|
height: root.toolbar-height;
|
|
background: AppTheme.toolbar;
|
|
TabButton {
|
|
x: 0; width: parent.width / 5; height: parent.height; kind: "home"; label: "Home"; active: root.tab == "home";
|
|
clicked => {
|
|
if root.tab == "home" { root.home_scroll_request += 1; }
|
|
root.select_tab("home");
|
|
}
|
|
}
|
|
TabButton {
|
|
x: parent.width / 5; width: parent.width / 5; height: parent.height; kind: "issues"; label: "Issues"; active: root.tab == "issues";
|
|
clicked => {
|
|
if root.tab == "issues" {
|
|
if root.page == "servers" { root.servers_scroll_request += 1; }
|
|
if root.page == "repositories" { root.repositories_scroll_request += 1; }
|
|
if root.page == "issues" { root.issues_scroll_request += 1; }
|
|
if root.page == "issue" { root.issue_scroll_request += 1; }
|
|
}
|
|
root.select_tab("issues");
|
|
}
|
|
}
|
|
TabButton { x: parent.width * 2 / 5; width: parent.width / 5; height: parent.height; kind: "repositories"; label: "Repos"; active: root.tab == "repositories"; clicked => { root.select_tab("repositories"); } }
|
|
TabButton { x: parent.width * 3 / 5; width: parent.width / 5; height: parent.height; kind: "pulls"; label: "Pulls"; active: root.tab == "pulls"; clicked => { root.select_tab("pulls"); } }
|
|
TabButton { x: parent.width * 4 / 5; width: parent.width / 5; height: parent.height; kind: "settings"; label: "Settings"; active: root.tab == "settings"; clicked => { root.select_tab("settings"); } }
|
|
}
|
|
|
|
if root.loading && (root.tab == "issues" || root.tab == "repositories" || root.tab == "pulls"): Rectangle {
|
|
width: 100%; height: 100%;
|
|
background: AppTheme.loading-overlay;
|
|
TouchArea { }
|
|
Spinner { width: 38px; height: 38px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; }
|
|
}
|
|
if root.filter_open: Rectangle {
|
|
width: 100%; height: 100%; background: #00000066;
|
|
TouchArea { clicked => { root.filter_open = false; } }
|
|
Rectangle {
|
|
x: 28px; y: (parent.height - 190px) / 2; width: parent.width - 56px; height: 190px;
|
|
background: AppTheme.surface; border-radius: 14px;
|
|
Text { x: 18px; y: 16px; width: parent.width - 36px; text: "Status"; font-size: 19px; font-weight: 650; color: AppTheme.text; }
|
|
VerticalLayout {
|
|
x: 18px; y: 54px; width: parent.width - 36px; height: 118px; spacing: 8px;
|
|
Button { text: "Open"; clicked => { root.choose_filter("open"); root.filter_open = false; } }
|
|
Button { text: "Closed"; clicked => { root.choose_filter("closed"); root.filter_open = false; } }
|
|
}
|
|
}
|
|
}
|
|
if root.error != "": Rectangle {
|
|
x: 14px; y: parent.height - root.safe-area-insets.bottom - 132px; width: parent.width - 28px; height: 66px;
|
|
background: #d93d35; border-radius: 12px;
|
|
Text { x: 14px; width: parent.width - 28px; text: root.error; color: white; font-size: 13px; wrap: word-wrap; vertical-alignment: center; }
|
|
TouchArea { clicked => { root.error = ""; } }
|
|
}
|
|
}
|