Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework Links to be more stable #191

Merged
merged 17 commits into from
Jul 25, 2023
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
40 changes: 38 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wiki-tui"
version = "0.8.0"
version = "0.8.1-pre"
authors = ["builditluc <37375448+Builditluc@users.noreply.github.com>"]
edition = "2018"
repository = "https://github.com/Builditluc/wiki-tui"
Expand Down Expand Up @@ -43,7 +43,9 @@ backtrace = "0.3"
toml = "0.5.9"
structopt = "0.3.25"
chrono = "0.4"
url = "2.4.0"
urlencoding = "2.1.2"
snafu = "0.7"

[dependencies.cursive]
version = "0.20"
Expand Down
11 changes: 8 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::{cell::RefCell, path::PathBuf, rc::Rc, str::FromStr};
#[cfg(not(test))]
use structopt::StructOpt;
use toml::from_str;
use url::Url;
use uuid::Uuid;

const CONFIG_FILE: &str = "config.toml";
Expand Down Expand Up @@ -175,13 +176,17 @@ pub struct ApiConfig {
}

impl ApiConfig {
pub fn url(&self) -> String {
format!(
pub fn url(&self) -> Url {
let url_str = format!(
"{}{}{}",
self.pre_language,
self.language.code(),
self.post_language
)
);

Url::parse(&url_str)
.with_context(|| format!("failed parsing the url: '{}'", url_str))
.expect("invalid api endpoint url")
}
}

Expand Down
16 changes: 8 additions & 8 deletions src/ui/article/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl LinesWrapper {
// go through every elment
for element in self.elements.iter() {
// does this element go onto a new line?
if element.kind() == ElementType::Newline {
if element.kind == ElementType::Newline {
// "add" the element onto a new line
self.current_line = Line::new();
self.current_width = element.width();
Expand Down Expand Up @@ -130,20 +130,20 @@ impl LinesWrapper {
pub fn wrap_lines(mut self) -> Self {
debug!("wrapping the lines");

let mut last_type: ElementType = ElementType::Text;
let mut last_type: &ElementType = &ElementType::Text;

// go through every element
for element in self.elements.clone().iter() {
// is this a link?
let is_link = element.kind() == ElementType::Link;
let is_link = matches!(element.kind, ElementType::Link(..));

// does this element go onto a new line?
if element.kind() == ElementType::Newline {
if element.kind == ElementType::Newline {
// fill the current line and make the next one blank
self.fill_line();
self.newline();

last_type = element.kind();
last_type = &element.kind;
continue;
}

Expand All @@ -166,7 +166,7 @@ impl LinesWrapper {
// of a line, add a leading whitespace
if (!element.content().starts_with([',', '.', ';', ':'])
&& !self.current_line.is_empty())
|| last_type == ElementType::ListMarker
|| last_type == &ElementType::ListMarker
{
self.push_whitespace();
}
Expand All @@ -180,7 +180,7 @@ impl LinesWrapper {
}
// then add it to the merged element
merged_element.push_str(span);
last_type = element.kind();
last_type = &element.kind;
continue;
}

Expand Down Expand Up @@ -219,7 +219,7 @@ impl LinesWrapper {
}
// then add it to the merged element
merged_element.push_str(span);
last_type = element.kind();
last_type = &element.kind;
continue;
}
}
Expand Down
92 changes: 66 additions & 26 deletions src/ui/article/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use crate::config::{self, Config};
use crate::ui::panel::WithPanel;
use crate::ui::search::bar_popup::open_search_bar;
use crate::ui::toc::display_toc;
use crate::ui::utils::{display_dialog, display_error};
use crate::wiki::article::{Article, Property};
use crate::ui::utils::{display_dialog, display_error, display_message};
use crate::wiki::article::link_data::InternalData;
use crate::wiki::article::{Article, Link, Property};
use crate::wiki::search::Namespace;
use crate::{config::CONFIG, ui::views::RootLayout};

use anyhow::{Context, Result};
Expand All @@ -16,12 +18,15 @@ mod lines;
mod view;
pub type ArticleView = view::ArticleView;

const ARTICLE_PROPERTIES: [Property; 2] = [Property::Text, Property::Sections];
const SUPPORTED_NAMESPACES: [Namespace; 1] = [Namespace::Main];

/// Fetches an article from a given id and displays it. It's the on_submit callback for
/// the search results view
pub fn on_article_submit(siv: &mut Cursive, pageid: usize) {
let article = match Article::builder()
.pageid(pageid)
.url(Config::from_siv(siv).borrow().api_config.url())
.from_url(Config::from_siv(siv).borrow().api_config.url())
.properties(vec![Property::Text, Property::Sections])
.fetch()
{
Expand All @@ -38,32 +43,70 @@ pub fn on_article_submit(siv: &mut Cursive, pageid: usize) {
}
}

/// Displays a confirmation dialog on whether to open the link. It's the on_submit callback for the
/// article view
pub fn on_link_submit(siv: &mut Cursive, target: String) {
// convert the target into a human-friendly format
let target = target.strip_prefix("/wiki/").unwrap_or(&target).to_string();
let target_human = target.replace('_', " ");

info!("requesting confirmation to open the link '{}'", target);
/// Checks that the link is supported (supported Namespace, supported link type) and opens it
pub fn open_link(siv: &mut Cursive, link: Link) {
macro_rules! link_dialog {
($cb: expr, $title: expr) => {{
display_dialog(
siv,
"Information",
&format!("Do you want to open the link '{}'?", $title),
$cb,
)
}};
}

let title = String::new();
let body = format!("Do you want to open the article '{}'?", target_human);
let message = match link {
Link::Internal(data) => {
return display_dialog(
siv,
"Information",
&format!("Do you want to open the link '{}'?", data.title),
move |siv| open_internal_link(siv, data.clone()),
)
}
Link::Anchor(_) => "Anchor links are not supported",
Link::RedLink(data) => {
warn!("tried opening a red link '{}'", data.url);
return display_message(
siv,
"Information",
&format!(
"The page '{}' doesn't exist and therefore cannot be opened",
data.title
),
);
}
Link::External(data) => {
warn!("tried opening an external link '{}'", data.url);
return display_message(siv, "Information", &format!("This link doesn't point to another article. \nInstead, it leads to the following external webpage and therefore, cannot be opened: \n\n> {}", data.url.as_str()));
}
Link::ExternalToInternal(_) => "External to Internal links are not supported",
};

display_dialog(siv, &title, &body, move |siv| {
info!("opening the link '{}'", target);
// open the link
open_link(siv, target.clone())
});
warn!("{}", message);
display_message(siv, "Warning", message)
}

/// Helper function for fetching and displaying an article from a given link
fn open_link(siv: &mut Cursive, target: String) {
fn open_internal_link(siv: &mut Cursive, data: InternalData) {
if !SUPPORTED_NAMESPACES.contains(&data.namespace) {
display_message(
siv,
"Information",
&format!(
"The link leads to an article in the '{}' namespace which is supported",
data.namespace
),
);
return;
}

// fetch the article
let article = match Article::builder()
.page(target)
.url(Config::from_siv(siv).borrow().api_config.url())
.properties(vec![Property::Text, Property::Sections])
.page(data.page)
.endpoint(data.endpoint)
.properties(ARTICLE_PROPERTIES.to_vec())
.fetch()
.context("failed fetching the article")
{
Expand All @@ -76,18 +119,15 @@ fn open_link(siv: &mut Cursive, target: String) {
return;
}
};
debug!("fetched the article");

// display the article
if let Err(error) = display_article(siv, article) {
if let Err(error) = display_article(siv, article).context("failed displaying the article") {
warn!("{:?}", error);

// display an error message
// TODO: use builtin helper function for error message here
display_error(siv, error);
return;
}
debug!("displayed the article");
}

/// Helper function for displaying an article on the screen. This includes creating an article view
Expand Down
27 changes: 21 additions & 6 deletions src/ui/article/view.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::{
config::CONFIG,
ui::{
article::{content::ArticleContent, on_link_submit},
article::{content::ArticleContent, open_link},
scroll,
utils::display_message,
utils::{display_dialog, display_message},
},
wiki::article::{Article, ElementType},
wiki::article::{Article, ElementType, Link},
};

use cursive::{
Expand Down Expand Up @@ -91,7 +91,21 @@ impl ArticleView {

/// Check if the link can be opened and opens it if it can
fn check_and_open_link(&self) -> EventResult {
if let Some(element) = self
let link = match self
.content
.element_by_id(self.content.current_link_element_id())
.map(|element| element.kind)
{
Some(ElementType::Link(ref link)) => link.to_owned(),
_ => {
warn!("selected element not a link");
return EventResult::Ignored;
}
};

return EventResult::Consumed(Some(Callback::from_fn(move |s| open_link(s, link.clone()))));

/*if let Some(element) = self
.content
.element_by_id(self.content.current_link_element_id())
{
Expand Down Expand Up @@ -150,6 +164,7 @@ impl ArticleView {
})));
}
EventResult::Ignored
*/
}
}

Expand Down Expand Up @@ -238,8 +253,8 @@ impl View for ArticleView {
} => {
if let Some(element) = s.content.element_by_pos(position.saturating_sub(offset))
{
return match element.kind() {
ElementType::Link if CONFIG.features.links => {
return match element.kind {
ElementType::Link(_) if CONFIG.features.links => {
s.content.select_link_by_id(element.id());
s.check_and_open_link()
}
Expand Down
Loading