Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions apps/zbugs-rune/e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ out=$(mktemp -d)
issue=$(sql "select id from issue i where open and not exists
(select 1 from comment c where c.\"issueID\" = i.id)
order by length(description), \"shortID\" limit 1")
# The label menu lists "New Label…", a separator, then the issue's project's
# labels by name, bytewise (item 2 is the first).
# The label picker lists the issue's project's labels by name, bytewise.
first_label=$(sql "select l.name from label l join issue i on i.\"projectID\" = l.\"projectID\"
where i.id = '$issue' order by l.name collate \"C\" limit 1")
sql "delete from \"issueLabel\" where \"issueID\" = '$issue'"
Expand All @@ -48,16 +47,16 @@ cat > "$out/script.json" <<EOF
{"t": 15.3, "type": "type", "text": "rocket"},
{"t": 15.8, "type": "tap", "key": "emoji-🚀"},
{"t": 16.5, "type": "tap", "key": "labels-trigger"},
{"t": 17.0, "type": "tap", "key": "labels-item2"},
{"t": 17.0, "type": "tap", "key": "label-$first_label"},
{"t": 17.5, "type": "key", "name": "Escape"},
{"t": 18.0, "type": "tap", "key": "toggle-open"},
{"t": 19.0, "type": "tap", "key": "edit-comment"},
{"t": 19.3, "type": "type", "text": " edited"},
{"t": 20.5, "type": "tap", "key": "save-comment-edit"},
{"t": 21.5, "type": "tap", "key": "labels-trigger"},
{"t": 22.0, "type": "tap", "key": "labels-item0"},
{"t": 22.5, "type": "type", "text": "$new_label"},
{"t": 24.0, "type": "tap", "key": "save"},
{"t": 22.0, "type": "type", "text": "$new_label"},
{"t": 23.5, "type": "key", "name": "Enter"},
{"t": 24.0, "type": "key", "name": "Escape"},
{"t": 25.0, "type": "tap", "key": "visibility-trigger"},
{"t": 25.5, "type": "tap", "key": "option1"},
{"t": 26.5, "type": "tap", "key": "notifications-trigger"},
Expand Down
65 changes: 39 additions & 26 deletions apps/zbugs-rune/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod token;
mod view;

use rune::{AppSpec, TextEdit, Tx};
use rune_kit::ComboboxState;
use settings::Settings;
use sync::{Session, Setup, SyncThread};
use zbugs_model::args::{
Expand Down Expand Up @@ -84,14 +85,11 @@ pub struct Filter {
/// A dropdown on the issue page.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Picker {
Assignee,
Labels,
Visibility,
Notifications,
/// The sidebar's.
Project,
CreatorFilter,
AssigneeFilter,
LabelFilter,
/// Reactions to the issue (its ID) or to one of its comments.
Emoji(String),
Expand All @@ -110,7 +108,6 @@ pub enum Confirm {
pub enum Sheet {
NewIssue,
EditIssue,
NewLabel,
Login,
Servers,
}
Expand Down Expand Up @@ -172,7 +169,13 @@ pub struct App {
pub title_edit: TextEdit,
pub description_edit: TextEdit,
pub token_edit: TextEdit,
pub label_edit: TextEdit,
/// The label picker's filter, which also names a label to create.
pub label_search: TextEdit,
/// The searchable people pickers: the list's creator and assignee
/// filters, and the issue's assignee.
pub creator_combo: ComboboxState,
pub assignee_filter_combo: ComboboxState,
pub assignee_combo: ComboboxState,
pub cache_edit: TextEdit,
pub api_edit: TextEdit,
pub sheet_error: Option<String>,
Expand Down Expand Up @@ -284,7 +287,10 @@ impl App {
title_edit: TextEdit::new(""),
description_edit: TextEdit::new(""),
token_edit: TextEdit::new(""),
label_edit: TextEdit::new(""),
label_search: TextEdit::new(""),
creator_combo: ComboboxState::default(),
assignee_filter_combo: ComboboxState::default(),
assignee_combo: ComboboxState::default(),
cache_edit: TextEdit::new(""),
api_edit: TextEdit::new(""),
sheet_error: None,
Expand Down Expand Up @@ -727,15 +733,17 @@ impl App {
}
}

/// Creates a label in the issue's project and adds it to the issue.
pub fn save_label(&mut self) {
let name = self.label_edit.text.trim().to_owned();
if name.is_empty() {
self.sheet_error = Some("A label needs a name.".into());
return;
}
if self.labels.iter().any(|l| l.name == name) {
self.sheet_error = Some(format!("There's already a label named {name}."));
/// Creates a label named as the label picker's filter, in the issue's
/// project, and adds it to the issue, unless a label has that name
/// (whatever its case), as the web picker does.
pub fn create_label(&mut self) {
let name = self.label_search.text.trim().to_owned();
if name.is_empty()
|| self
.labels
.iter()
.any(|l| l.name.to_lowercase() == name.to_lowercase())
{
return;
}
let Some(issue) = &self.detail else { return };
Expand All @@ -745,7 +753,8 @@ impl App {
label_name: name,
project_id: None,
}));
self.sheet = None;
self.label_search = TextEdit::new("");
self.label_search.focused = true;
}

pub fn toggle_open(&mut self) {
Expand Down Expand Up @@ -845,9 +854,16 @@ impl App {
}

pub fn toggle_picker(&mut self, picker: Picker) {
if matches!(picker, Picker::Emoji(_)) {
self.emoji_search = TextEdit::new("");
self.emoji_search.focused = true;
match picker {
Picker::Emoji(_) => {
self.emoji_search = TextEdit::new("");
self.emoji_search.focused = true;
}
Picker::Labels => {
self.label_search = TextEdit::new("");
self.label_search.focused = true;
}
_ => {}
}
self.picker = (self.picker.as_ref() != Some(&picker)).then_some(picker);
}
Expand Down Expand Up @@ -961,11 +977,6 @@ impl App {
self.title_edit = TextEdit::new(issue.title.clone());
self.description_edit = TextEdit::new(issue.description.clone());
}
Sheet::NewLabel => {
self.picker = None;
self.label_edit = TextEdit::new("");
self.label_edit.focused = true;
}
Sheet::Login => self.token_edit = TextEdit::new(""),
Sheet::Servers => {
self.cache_edit = TextEdit::new(self.settings.cache_url.clone());
Expand Down Expand Up @@ -1104,8 +1115,9 @@ fn apply_issues(s: &mut App, rows: Vec<Issue>, result_type: ResultType, tx: &mut
}
}

/// Keys no control took: j and k step through the list, and / starts a
/// search, as on the web.
/// Keys no control took: j and k step through the list, / starts a search,
/// as on the web, and Escape closes an open picker (Rune's popovers leave
/// Escape to the app).
fn on_key(s: &mut App, event: &rune::KeyEvent, tx: &mut Tx) -> bool {
if s.sheet.is_some()
|| s.confirm.is_some()
Expand All @@ -1116,6 +1128,7 @@ fn on_key(s: &mut App, event: &rune::KeyEvent, tx: &mut Tx) -> bool {
return false;
}
match &event.key {
rune::Key::Escape if s.picker.is_some() => s.picker = None,
rune::Key::Text(t) if t == "j" => s.step(1, tx),
rune::Key::Text(t) if t == "k" => s.step(-1, tx),
rune::Key::Text(t) if t == "/" => s.search.focused = true,
Expand Down
Loading
Loading