diff --git a/Cargo.lock b/Cargo.lock index e4df794..7cc1820 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,9 +248,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", diff --git a/Cargo.toml b/Cargo.toml index bffa5bc..f5242bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ categories = [ "command-line-utilities", "gui", "text-processing" ] [dependencies] async-std = "1.12.0" directories = "5.0.1" -chrono = "0.4.35" +chrono = "0.4.37" dialoguer = "0.11.0" crossterm = "0.27.0" rusqlite = { version = "0.31.0", features = ["bundled"] } diff --git a/src/main.rs b/src/main.rs index d311b84..92c53d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,14 +57,14 @@ fn display_about() -> Result<(), Box> { "{}", paragraph( &skin, - "**Notabena** is a FOSS note-taking CLI tool, written in Rust.\nDonations are always a great way to help us keeping the project alive. It can be done here: https://paypal.me/Notabena (ctrl+click to follow link)." + "**Notabena** is a FOSS note-taking CLI tool, written in Rust." ) ); println!( "version: v{}, licensed under: GPL v3", env!("CARGO_PKG_VERSION") ); - println!("COPYRIGHT (c) 2023-PRESENT NOTABENA ORGANISATION\nPROJECT LEADS @ThatFrogDev, @MrSerge01, GITHUB CONTRIBUTORS\n\n(scroll up if you can't read everything)"); + println!("COPYRIGHT (c) 2023-PRESENT NOTABENA ORGANISATION\nAUTHOR: @ThatFrogDev, GITHUB CONTRIBUTORS\n\n(scroll up if you can't read everything)"); Ok(()) } diff --git a/src/note.rs b/src/note.rs index d53b378..4ace705 100644 --- a/src/note.rs +++ b/src/note.rs @@ -1,6 +1,10 @@ use crate::{ api, multiselect, - prompts::{confirm::confirm, input::input, select::select}, + prompts::{ + confirm::confirm, + input::{multi, single}, + select::select, + }, truncate_note, utilities::{cursor_to_origin::cursor_to_origin, display::display}, }; @@ -20,7 +24,8 @@ impl Note { pub fn create(db_file: &PathBuf) -> Result<(), Box> { let sqlite = Connection::open(db_file)?; - // fetch IDs from database, sort and find the first gap. if it does not exist, use the length of the array + 1 + // fetch IDs from database, sort and find the first gap + //if it does not exist, use the length of the array + 1 let mut stmt = sqlite.prepare("SELECT id FROM saved_notes")?; let ids: Result, _> = stmt.query_map(params![], |row| row.get(0))?.collect(); let mut ids = ids?; @@ -34,12 +39,14 @@ impl Note { cursor_to_origin()?; println!( - "If you're done inputting a field, you can press Enter twice to continue or save, or Alt/Option-Q to return to the main menu.\r" + "If you're done inputting a field, you can press Enter twice to continue or save, or Alt/Option-Q to return to the main menu.\n\ + For the \"Content\" field: to end a paragraph press Space and then Enter. To end your note, press Enter on a second new line.\n\ + You can use Markdown to format your notes.\r" ); let mut name: String; loop { - name = input("Name:", "".to_string())?; + name = single("Name:", "".to_string(), true)?; if name.len() > 64 { cursor_to_origin()?; println!( @@ -53,7 +60,7 @@ impl Note { let inputted_note = Note { id, name, - content: input("Content:", "".to_string())?, + content: multi("Content:", "".to_string())?, created: format!("{}", Local::now().format("%A %e %B, %H:%M")), }; @@ -108,8 +115,8 @@ impl Note { let selected_note = &saved_notes[selection]; let updated_note = Note { id: selected_note.id, - name: input("Name:", selected_note.name.clone())?, - content: input("Content:", selected_note.content.clone())?, + name: single("Name:", selected_note.name.clone(), true)?, + content: multi("Content:", selected_note.content.clone())?, created: selected_note.created.clone(), }; diff --git a/src/prompts/input.rs b/src/prompts/input.rs index 710cf59..d4f13e7 100644 --- a/src/prompts/input.rs +++ b/src/prompts/input.rs @@ -1,25 +1,36 @@ -use crate::return_to_main::{continue_event, return_event}; use crate::{main, return_to_main}; use dialoguer::{theme::ColorfulTheme, Input}; -pub fn input(prompt: &str, initial_text: String) -> Result> { - let result = match initial_text.as_str() { - "" => Input::with_theme(&ColorfulTheme::default()) - .with_prompt(prompt) - .interact_text() - .unwrap(), - _ => Input::with_theme(&ColorfulTheme::default()) - .with_prompt(prompt) - .with_initial_text(initial_text) - .interact_text() - .unwrap(), - }; +pub fn single( + prompt: &str, + initial_text: String, + is_required: bool, +) -> Result> { + let mut result: String; + + loop { + result = match initial_text.as_str() { + "" => Input::with_theme(&ColorfulTheme::default()) + .with_prompt(prompt) + .interact_text() + .unwrap(), + _ => Input::with_theme(&ColorfulTheme::default()) + .with_prompt(prompt) + .with_initial_text(initial_text.clone()) + .interact_text() + .unwrap(), + }; + + if !is_required || !result.trim().is_empty() { + break; + } + } match return_to_main() { Ok(value) => { - if value == return_event() { + if value == "userReturned" { main()?; - } else if value == continue_event() { + } else if value == "userContinued" { return Ok(result); } } @@ -28,3 +39,31 @@ pub fn input(prompt: &str, initial_text: String) -> Result Result> { + let mut result = String::new(); + let mut t_enter = 0; + let mut first_iteration = true; + + loop { + let line = if first_iteration { + first_iteration = false; + single(prompt, initial_text.clone(), false)? + } else { + single("", initial_text.clone(), false)? + }; + + if line.trim().is_empty() { + t_enter += 1; + if t_enter == 2 { + break; + } + } else { + t_enter = 0; + } + result.push_str(&line); + result.push('\n'); + } + + Ok(result) +} diff --git a/src/return_to_main.rs b/src/return_to_main.rs index 29e71a4..e81ee73 100644 --- a/src/return_to_main.rs +++ b/src/return_to_main.rs @@ -4,7 +4,7 @@ use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use std::time::Duration; // very hacky way to make these values public -pub fn return_event() -> KeyEvent { +fn return_event() -> KeyEvent { KeyEvent { code: KeyCode::Char('q'), modifiers: KeyModifiers::ALT, @@ -13,7 +13,7 @@ pub fn return_event() -> KeyEvent { } } -pub fn continue_event() -> KeyEvent { +fn continue_event() -> KeyEvent { KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::NONE, @@ -22,17 +22,17 @@ pub fn continue_event() -> KeyEvent { } } -pub fn return_to_main() -> Result> { +pub fn return_to_main() -> Result<&'static str, Box> { enable_raw_mode()?; loop { if event::poll(Duration::from_millis(1000))? { if let Event::Key(event) = event::read()? { if event == return_event() { - return Ok(return_event()); + return Ok("userReturned"); } if event == continue_event() { - return Ok(continue_event()); + return Ok("userContinued"); } } } diff --git a/src/utilities/truncate_note.rs b/src/utilities/truncate_note.rs index f4417d2..289bed4 100644 --- a/src/utilities/truncate_note.rs +++ b/src/utilities/truncate_note.rs @@ -6,7 +6,12 @@ pub fn truncate_note( db_file: &PathBuf, ) -> Result<(), Box> { for note in &get_notes(db_file)? { - let mut truncated_content: String = note.content.chars().take(10).collect(); + let mut truncated_content: String = note + .content + .chars() + .take(10) + .collect::() + .replace("\n", " "); if truncated_content.chars().count() == 10 { truncated_content += "..."; // this is cursed and awesome at the same time }