Attach and label media in /list views
This commit is contained in:
parent
18bc0bd57f
commit
cfc218447c
1 changed files with 209 additions and 44 deletions
253
src/main.rs
253
src/main.rs
|
|
@ -711,7 +711,8 @@ async fn handle_norm_message(
|
||||||
match apply_user_op(state, &op).await? {
|
match apply_user_op(state, &op).await? {
|
||||||
UserOpOutcome::Applied(ApplyOutcome::Applied) => {
|
UserOpOutcome::Applied(ApplyOutcome::Applied) => {
|
||||||
session.entries[target_index] = normalized_entry;
|
session.entries[target_index] = normalized_entry;
|
||||||
let (text, kb) = render_list_view(&session.id, &session, &peeked_snapshot);
|
let (text, kb) =
|
||||||
|
render_list_view(&session.id, &session, &peeked_snapshot, &state.config);
|
||||||
if let Some(message_id) = session.message_id {
|
if let Some(message_id) = session.message_id {
|
||||||
bot.edit_message_text(chat_id, message_id, text)
|
bot.edit_message_text(chat_id, message_id, text)
|
||||||
.reply_markup(kb)
|
.reply_markup(kb)
|
||||||
|
|
@ -720,6 +721,11 @@ async fn handle_norm_message(
|
||||||
let sent = bot.send_message(chat_id, text).reply_markup(kb).await?;
|
let sent = bot.send_message(chat_id, text).reply_markup(kb).await?;
|
||||||
session.message_id = Some(sent.id);
|
session.message_id = Some(sent.id);
|
||||||
}
|
}
|
||||||
|
if let Err(err) =
|
||||||
|
send_embedded_media_for_view(bot, chat_id, state, &session, &peeked_snapshot).await
|
||||||
|
{
|
||||||
|
error!("send embedded media failed: {:#}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UserOpOutcome::Applied(ApplyOutcome::NotFound)
|
UserOpOutcome::Applied(ApplyOutcome::NotFound)
|
||||||
| UserOpOutcome::Applied(ApplyOutcome::Duplicate) => {
|
| UserOpOutcome::Applied(ApplyOutcome::Duplicate) => {
|
||||||
|
|
@ -805,7 +811,8 @@ async fn handle_instant_delete_message(
|
||||||
}
|
}
|
||||||
let _ = add_undo(state, UndoKind::Delete, op.entry.clone()).await?;
|
let _ = add_undo(state, UndoKind::Delete, op.entry.clone()).await?;
|
||||||
normalize_peek_view(&mut session, &peeked_snapshot);
|
normalize_peek_view(&mut session, &peeked_snapshot);
|
||||||
let (text, kb) = render_list_view(&session.id, &session, &peeked_snapshot);
|
let (text, kb) =
|
||||||
|
render_list_view(&session.id, &session, &peeked_snapshot, &state.config);
|
||||||
if let Some(message_id) = session.message_id {
|
if let Some(message_id) = session.message_id {
|
||||||
bot.edit_message_text(chat_id, message_id, text)
|
bot.edit_message_text(chat_id, message_id, text)
|
||||||
.reply_markup(kb)
|
.reply_markup(kb)
|
||||||
|
|
@ -814,6 +821,11 @@ async fn handle_instant_delete_message(
|
||||||
let sent = bot.send_message(chat_id, text).reply_markup(kb).await?;
|
let sent = bot.send_message(chat_id, text).reply_markup(kb).await?;
|
||||||
session.message_id = Some(sent.id);
|
session.message_id = Some(sent.id);
|
||||||
}
|
}
|
||||||
|
if let Err(err) =
|
||||||
|
send_embedded_media_for_view(bot, chat_id, state, &session, &peeked_snapshot).await
|
||||||
|
{
|
||||||
|
error!("send embedded media failed: {:#}", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UserOpOutcome::Applied(ApplyOutcome::NotFound)
|
UserOpOutcome::Applied(ApplyOutcome::NotFound)
|
||||||
| UserOpOutcome::Applied(ApplyOutcome::Duplicate) => {
|
| UserOpOutcome::Applied(ApplyOutcome::Duplicate) => {
|
||||||
|
|
@ -940,7 +952,7 @@ async fn handle_search_command(
|
||||||
};
|
};
|
||||||
|
|
||||||
let peeked_snapshot = state.peeked.lock().await.clone();
|
let peeked_snapshot = state.peeked.lock().await.clone();
|
||||||
let (text, kb) = render_list_view(&session_id, &session, &peeked_snapshot);
|
let (text, kb) = render_list_view(&session_id, &session, &peeked_snapshot, &state.config);
|
||||||
let sent = bot
|
let sent = bot
|
||||||
.send_message(msg.chat.id, text)
|
.send_message(msg.chat.id, text)
|
||||||
.reply_markup(kb)
|
.reply_markup(kb)
|
||||||
|
|
@ -1828,7 +1840,7 @@ async fn handle_finish_title_response(
|
||||||
}
|
}
|
||||||
|
|
||||||
let peeked_snapshot = state.peeked.lock().await.clone();
|
let peeked_snapshot = state.peeked.lock().await.clone();
|
||||||
let (text, kb) = render_list_view(&session.id, &session, &peeked_snapshot);
|
let (text, kb) = render_list_view(&session.id, &session, &peeked_snapshot, &state.config);
|
||||||
if let Some(list_message_id) = session.message_id {
|
if let Some(list_message_id) = session.message_id {
|
||||||
bot.edit_message_text(chat_id, list_message_id, text)
|
bot.edit_message_text(chat_id, list_message_id, text)
|
||||||
.reply_markup(kb)
|
.reply_markup(kb)
|
||||||
|
|
@ -1837,6 +1849,10 @@ async fn handle_finish_title_response(
|
||||||
let sent = bot.send_message(chat_id, text).reply_markup(kb).await?;
|
let sent = bot.send_message(chat_id, text).reply_markup(kb).await?;
|
||||||
session.message_id = Some(sent.id);
|
session.message_id = Some(sent.id);
|
||||||
}
|
}
|
||||||
|
if let Err(err) = send_embedded_media_for_view(bot, chat_id, state, &session, &peeked_snapshot).await
|
||||||
|
{
|
||||||
|
error!("send embedded media failed: {:#}", err);
|
||||||
|
}
|
||||||
state
|
state
|
||||||
.sessions
|
.sessions
|
||||||
.lock()
|
.lock()
|
||||||
|
|
@ -2215,7 +2231,7 @@ async fn handle_list_callback(
|
||||||
}
|
}
|
||||||
|
|
||||||
session.message_id = Some(message.id);
|
session.message_id = Some(message.id);
|
||||||
let (text, kb) = render_list_view(&session.id, &session, &peeked_snapshot);
|
let (text, kb) = render_list_view(&session.id, &session, &peeked_snapshot, &state.config);
|
||||||
let session_clone = session.clone();
|
let session_clone = session.clone();
|
||||||
state
|
state
|
||||||
.sessions
|
.sessions
|
||||||
|
|
@ -2230,7 +2246,10 @@ async fn handle_list_callback(
|
||||||
bot.edit_message_text(message.chat.id, message.id, text)
|
bot.edit_message_text(message.chat.id, message.id, text)
|
||||||
.reply_markup(kb)
|
.reply_markup(kb)
|
||||||
.await?;
|
.await?;
|
||||||
if let Err(err) = send_embedded_media_for_selected(&bot, message.chat.id, &state, &session_clone).await {
|
if let Err(err) =
|
||||||
|
send_embedded_media_for_view(&bot, message.chat.id, &state, &session_clone, &peeked_snapshot)
|
||||||
|
.await
|
||||||
|
{
|
||||||
error!("send embedded media failed: {:#}", err);
|
error!("send embedded media failed: {:#}", err);
|
||||||
}
|
}
|
||||||
bot.answer_callback_query(q.id).await?;
|
bot.answer_callback_query(q.id).await?;
|
||||||
|
|
@ -3260,6 +3279,28 @@ fn displayed_indices_for_view(
|
||||||
ListView::Peek { mode, page } => peek_indices_for_session(session, peeked, mode, page),
|
ListView::Peek { mode, page } => peek_indices_for_session(session, peeked, mode, page),
|
||||||
ListView::Selected { index, .. } => vec![index],
|
ListView::Selected { index, .. } => vec![index],
|
||||||
ListView::FinishConfirm { index, .. } => vec![index],
|
ListView::FinishConfirm { index, .. } => vec![index],
|
||||||
|
ListView::DeleteConfirm { index, .. } => vec![index],
|
||||||
|
_ => Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn embedded_lines_for_view(session: &ListSession, peeked: &HashSet<String>) -> Vec<String> {
|
||||||
|
match session.view {
|
||||||
|
ListView::Peek { mode, page } => peek_indices_for_session(session, peeked, mode, page)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|index| session.entries.get(index))
|
||||||
|
.flat_map(|entry| entry.preview_lines())
|
||||||
|
.collect(),
|
||||||
|
ListView::Selected { index, .. } => session
|
||||||
|
.entries
|
||||||
|
.get(index)
|
||||||
|
.map(|entry| entry.display_lines())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
ListView::FinishConfirm { index, .. } | ListView::DeleteConfirm { index, .. } => session
|
||||||
|
.entries
|
||||||
|
.get(index)
|
||||||
|
.map(|entry| entry.preview_lines())
|
||||||
|
.unwrap_or_default(),
|
||||||
_ => Vec::new(),
|
_ => Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3560,16 +3601,19 @@ fn render_list_view(
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
session: &ListSession,
|
session: &ListSession,
|
||||||
peeked: &HashSet<String>,
|
peeked: &HashSet<String>,
|
||||||
|
config: &Config,
|
||||||
) -> (String, InlineKeyboardMarkup) {
|
) -> (String, InlineKeyboardMarkup) {
|
||||||
match &session.view {
|
match &session.view {
|
||||||
ListView::Menu => build_menu_view(session_id, session),
|
ListView::Menu => build_menu_view(session_id, session),
|
||||||
ListView::Peek { mode, page } => build_peek_view(session_id, session, *mode, *page, peeked),
|
ListView::Peek { mode, page } => {
|
||||||
ListView::Selected { index, .. } => build_selected_view(session_id, session, *index),
|
build_peek_view(session_id, session, *mode, *page, peeked, config)
|
||||||
|
}
|
||||||
|
ListView::Selected { index, .. } => build_selected_view(session_id, session, *index, config),
|
||||||
ListView::FinishConfirm { index, .. } => {
|
ListView::FinishConfirm { index, .. } => {
|
||||||
build_finish_confirm_view(session_id, session, *index)
|
build_finish_confirm_view(session_id, session, *index, config)
|
||||||
}
|
}
|
||||||
ListView::DeleteConfirm { step, index, .. } => {
|
ListView::DeleteConfirm { step, index, .. } => {
|
||||||
build_delete_confirm_view(session_id, session, *index, *step)
|
build_delete_confirm_view(session_id, session, *index, *step, config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3634,6 +3678,7 @@ fn build_peek_view(
|
||||||
mode: ListMode,
|
mode: ListMode,
|
||||||
page: usize,
|
page: usize,
|
||||||
peeked: &HashSet<String>,
|
peeked: &HashSet<String>,
|
||||||
|
config: &Config,
|
||||||
) -> (String, InlineKeyboardMarkup) {
|
) -> (String, InlineKeyboardMarkup) {
|
||||||
let total_unpeeked = count_visible_entries(session, peeked);
|
let total_unpeeked = count_visible_entries(session, peeked);
|
||||||
let indices = peek_indices_for_session(session, peeked, mode, page);
|
let indices = peek_indices_for_session(session, peeked, mode, page);
|
||||||
|
|
@ -3666,7 +3711,7 @@ fn build_peek_view(
|
||||||
} else {
|
} else {
|
||||||
for (display_index, entry_index) in indices.iter().enumerate() {
|
for (display_index, entry_index) in indices.iter().enumerate() {
|
||||||
if let Some(entry) = session.entries.get(*entry_index) {
|
if let Some(entry) = session.entries.get(*entry_index) {
|
||||||
let preview = entry.preview_lines();
|
let preview = format_embedded_references_for_lines(&entry.preview_lines(), config);
|
||||||
text.push_str(&format!("{}) ", display_index + 1));
|
text.push_str(&format!("{}) ", display_index + 1));
|
||||||
if let Some(first) = preview.get(0) {
|
if let Some(first) = preview.get(0) {
|
||||||
text.push_str(first);
|
text.push_str(first);
|
||||||
|
|
@ -3719,10 +3764,11 @@ fn build_selected_view(
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
session: &ListSession,
|
session: &ListSession,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
config: &Config,
|
||||||
) -> (String, InlineKeyboardMarkup) {
|
) -> (String, InlineKeyboardMarkup) {
|
||||||
let entry = session.entries.get(index);
|
let entry = session.entries.get(index);
|
||||||
let text = if let Some(entry) = entry {
|
let text = if let Some(entry) = entry {
|
||||||
let lines = entry.display_lines();
|
let lines = format_embedded_references_for_lines(&entry.display_lines(), config);
|
||||||
format!("Selected item:\n\n{}", lines.join("\n"))
|
format!("Selected item:\n\n{}", lines.join("\n"))
|
||||||
} else {
|
} else {
|
||||||
"Selected item not found.".to_string()
|
"Selected item not found.".to_string()
|
||||||
|
|
@ -3818,9 +3864,12 @@ fn build_finish_confirm_view(
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
session: &ListSession,
|
session: &ListSession,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
config: &Config,
|
||||||
) -> (String, InlineKeyboardMarkup) {
|
) -> (String, InlineKeyboardMarkup) {
|
||||||
let entry = session.entries.get(index);
|
let entry = session.entries.get(index);
|
||||||
let preview = entry.map(|e| e.preview_lines()).unwrap_or_default();
|
let preview = entry
|
||||||
|
.map(|e| format_embedded_references_for_lines(&e.preview_lines(), config))
|
||||||
|
.unwrap_or_default();
|
||||||
let mut text = String::from("Finish this item?\n\n");
|
let mut text = String::from("Finish this item?\n\n");
|
||||||
if let Some(first) = preview.get(0) {
|
if let Some(first) = preview.get(0) {
|
||||||
text.push_str(first);
|
text.push_str(first);
|
||||||
|
|
@ -3854,9 +3903,12 @@ fn build_delete_confirm_view(
|
||||||
session: &ListSession,
|
session: &ListSession,
|
||||||
index: usize,
|
index: usize,
|
||||||
step: u8,
|
step: u8,
|
||||||
|
config: &Config,
|
||||||
) -> (String, InlineKeyboardMarkup) {
|
) -> (String, InlineKeyboardMarkup) {
|
||||||
let entry = session.entries.get(index);
|
let entry = session.entries.get(index);
|
||||||
let preview = entry.map(|e| e.preview_lines()).unwrap_or_default();
|
let preview = entry
|
||||||
|
.map(|e| format_embedded_references_for_lines(&e.preview_lines(), config))
|
||||||
|
.unwrap_or_default();
|
||||||
let mut text = format!("Confirm delete ({}/2)?\n\n", step);
|
let mut text = format!("Confirm delete ({}/2)?\n\n", step);
|
||||||
if let Some(first) = preview.get(0) {
|
if let Some(first) = preview.get(0) {
|
||||||
text.push_str(first);
|
text.push_str(first);
|
||||||
|
|
@ -4019,19 +4071,14 @@ async fn send_error(bot: &Bot, chat_id: ChatId, text: &str) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_embedded_media_for_selected(
|
async fn send_embedded_media_for_view(
|
||||||
bot: &Bot,
|
bot: &Bot,
|
||||||
chat_id: ChatId,
|
chat_id: ChatId,
|
||||||
state: &std::sync::Arc<AppState>,
|
state: &std::sync::Arc<AppState>,
|
||||||
session: &ListSession,
|
session: &ListSession,
|
||||||
|
peeked: &HashSet<String>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let ListView::Selected { index, .. } = session.view else {
|
let lines = embedded_lines_for_view(session, peeked);
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let Some(entry) = session.entries.get(index) else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let lines = entry.display_lines();
|
|
||||||
let embeds = extract_embedded_paths(&lines, &state.config);
|
let embeds = extract_embedded_paths(&lines, &state.config);
|
||||||
for path in embeds {
|
for path in embeds {
|
||||||
if is_image_path(&path) {
|
if is_image_path(&path) {
|
||||||
|
|
@ -4497,6 +4544,56 @@ fn build_media_entry_text(filename: &str, caption: Option<&str>) -> String {
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn format_embedded_references_for_lines(lines: &[String], config: &Config) -> Vec<String> {
|
||||||
|
let mut labels: HashMap<PathBuf, usize> = HashMap::new();
|
||||||
|
let mut next_label = 1usize;
|
||||||
|
let mut output = Vec::with_capacity(lines.len());
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let mut formatted = String::with_capacity(line.len());
|
||||||
|
let mut index = 0;
|
||||||
|
while let Some(start_rel) = line[index..].find("![[") {
|
||||||
|
let marker_start = index + start_rel;
|
||||||
|
formatted.push_str(&line[index..marker_start]);
|
||||||
|
|
||||||
|
let marker_content_start = marker_start + 3;
|
||||||
|
let Some(end_rel) = line[marker_content_start..].find("]]") else {
|
||||||
|
formatted.push_str(&line[marker_start..]);
|
||||||
|
index = line.len();
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let marker_content_end = marker_content_start + end_rel;
|
||||||
|
let marker_end = marker_content_end + 2;
|
||||||
|
let marker_inner = &line[marker_content_start..marker_content_end];
|
||||||
|
|
||||||
|
if let Some(path) = resolve_embedded_path(marker_inner, config) {
|
||||||
|
let label = match labels.get(&path) {
|
||||||
|
Some(label) => *label,
|
||||||
|
None => {
|
||||||
|
let assigned = next_label;
|
||||||
|
labels.insert(path.clone(), assigned);
|
||||||
|
next_label += 1;
|
||||||
|
assigned
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if is_image_path(&path) {
|
||||||
|
formatted.push_str(&format!("image #{}", label));
|
||||||
|
} else {
|
||||||
|
formatted.push_str(&format!("file #{}", label));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formatted.push_str(&line[marker_start..marker_end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = marker_end;
|
||||||
|
}
|
||||||
|
formatted.push_str(&line[index..]);
|
||||||
|
output.push(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
fn pick_best_photo(photos: &[teloxide::types::PhotoSize]) -> Option<&teloxide::types::PhotoSize> {
|
fn pick_best_photo(photos: &[teloxide::types::PhotoSize]) -> Option<&teloxide::types::PhotoSize> {
|
||||||
photos.iter().max_by_key(|photo| {
|
photos.iter().max_by_key(|photo| {
|
||||||
photo.file.size.max((photo.width * photo.height) as u32) as u64
|
photo.file.size.max((photo.width * photo.height) as u32) as u64
|
||||||
|
|
@ -4513,10 +4610,6 @@ async fn download_telegram_file(bot: &Bot, file_id: &str, dest_path: &Path) -> R
|
||||||
fn extract_embedded_paths(lines: &[String], config: &Config) -> Vec<PathBuf> {
|
fn extract_embedded_paths(lines: &[String], config: &Config) -> Vec<PathBuf> {
|
||||||
let mut paths = Vec::new();
|
let mut paths = Vec::new();
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
let vault_root = config
|
|
||||||
.read_later_path
|
|
||||||
.parent()
|
|
||||||
.unwrap_or_else(|| Path::new("."));
|
|
||||||
for line in lines {
|
for line in lines {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
while let Some(start_rel) = line[index..].find("![[") {
|
while let Some(start_rel) = line[index..].find("![[") {
|
||||||
|
|
@ -4525,23 +4618,11 @@ fn extract_embedded_paths(lines: &[String], config: &Config) -> Vec<PathBuf> {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
let end = start + end_rel;
|
let end = start + end_rel;
|
||||||
let mut inner = line[start..end].trim();
|
let inner = &line[start..end];
|
||||||
if let Some((path_part, _)) = inner.split_once('|') {
|
if let Some(path) = resolve_embedded_path(inner, config) {
|
||||||
inner = path_part.trim();
|
if seen.insert(path.clone()) {
|
||||||
}
|
paths.push(path);
|
||||||
if inner.is_empty() {
|
}
|
||||||
index = end + 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let path = if Path::new(inner).is_absolute() {
|
|
||||||
PathBuf::from(inner)
|
|
||||||
} else if inner.contains('/') || inner.contains('\\') {
|
|
||||||
vault_root.join(inner)
|
|
||||||
} else {
|
|
||||||
config.media_dir.join(inner)
|
|
||||||
};
|
|
||||||
if path.exists() && seen.insert(path.clone()) {
|
|
||||||
paths.push(path);
|
|
||||||
}
|
}
|
||||||
index = end + 2;
|
index = end + 2;
|
||||||
}
|
}
|
||||||
|
|
@ -4549,6 +4630,34 @@ fn extract_embedded_paths(lines: &[String], config: &Config) -> Vec<PathBuf> {
|
||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_embedded_path(inner: &str, config: &Config) -> Option<PathBuf> {
|
||||||
|
let mut inner = inner.trim();
|
||||||
|
if let Some((path_part, _)) = inner.split_once('|') {
|
||||||
|
inner = path_part.trim();
|
||||||
|
}
|
||||||
|
if inner.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let vault_root = config
|
||||||
|
.read_later_path
|
||||||
|
.parent()
|
||||||
|
.unwrap_or_else(|| Path::new("."));
|
||||||
|
let path = if Path::new(inner).is_absolute() {
|
||||||
|
PathBuf::from(inner)
|
||||||
|
} else if inner.contains('/') || inner.contains('\\') {
|
||||||
|
vault_root.join(inner)
|
||||||
|
} else {
|
||||||
|
config.media_dir.join(inner)
|
||||||
|
};
|
||||||
|
|
||||||
|
if path.exists() {
|
||||||
|
Some(path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_image_path(path: &Path) -> bool {
|
fn is_image_path(path: &Path) -> bool {
|
||||||
match path.extension().and_then(|ext| ext.to_str()) {
|
match path.extension().and_then(|ext| ext.to_str()) {
|
||||||
Some(ext) => matches!(
|
Some(ext) => matches!(
|
||||||
|
|
@ -4635,6 +4744,20 @@ mod tests {
|
||||||
EntryBlock::from_text(text)
|
EntryBlock::from_text(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_config() -> Config {
|
||||||
|
Config {
|
||||||
|
token: "token".to_string(),
|
||||||
|
user_id: 1,
|
||||||
|
read_later_path: PathBuf::from("/tmp/read-later.md"),
|
||||||
|
finished_path: PathBuf::from("/tmp/finished.md"),
|
||||||
|
resources_path: PathBuf::from("/tmp/resources"),
|
||||||
|
media_dir: PathBuf::from("/tmp/media"),
|
||||||
|
data_dir: PathBuf::from("/tmp/data"),
|
||||||
|
retry_interval_seconds: None,
|
||||||
|
sync: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalize_markdown_links_replaces_single_link() {
|
fn normalize_markdown_links_replaces_single_link() {
|
||||||
let input = "See [post](https://example.com/post) now";
|
let input = "See [post](https://example.com/post) now";
|
||||||
|
|
@ -4750,10 +4873,52 @@ mod tests {
|
||||||
for entry in &entries {
|
for entry in &entries {
|
||||||
peeked.insert(entry.block_string());
|
peeked.insert(entry.block_string());
|
||||||
}
|
}
|
||||||
let (text, _kb) = build_peek_view("session", &session, ListMode::Top, 0, &peeked);
|
let config = test_config();
|
||||||
|
let (text, _kb) = build_peek_view("session", &session, ListMode::Top, 0, &peeked, &config);
|
||||||
assert!(text.contains("Everything's been peeked already."));
|
assert!(text.contains("Everything's been peeked already."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_embedded_references_labels_images_and_files() {
|
||||||
|
let temp = TempDir::new().unwrap();
|
||||||
|
let media_dir = temp.path().join("media");
|
||||||
|
fs::create_dir_all(&media_dir).unwrap();
|
||||||
|
fs::write(media_dir.join("image-1.jpg"), b"x").unwrap();
|
||||||
|
fs::write(media_dir.join("doc-1.pdf"), b"x").unwrap();
|
||||||
|
|
||||||
|
let mut config = test_config();
|
||||||
|
config.media_dir = media_dir;
|
||||||
|
|
||||||
|
let lines = vec![
|
||||||
|
"![[image-1.jpg]] and ![[doc-1.pdf]]".to_string(),
|
||||||
|
"repeat ![[image-1.jpg]]".to_string(),
|
||||||
|
];
|
||||||
|
let rendered = format_embedded_references_for_lines(&lines, &config);
|
||||||
|
|
||||||
|
assert_eq!(rendered[0], "image #1 and file #2");
|
||||||
|
assert_eq!(rendered[1], "repeat image #1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn embedded_lines_for_peek_use_preview_only() {
|
||||||
|
let entry = EntryBlock::from_text("first line\nsecond line\n![[image-2.jpg]]");
|
||||||
|
let session = ListSession {
|
||||||
|
id: "session".to_string(),
|
||||||
|
chat_id: 0,
|
||||||
|
kind: SessionKind::List,
|
||||||
|
entries: vec![entry],
|
||||||
|
view: ListView::Peek {
|
||||||
|
mode: ListMode::Top,
|
||||||
|
page: 0,
|
||||||
|
},
|
||||||
|
seen_random: HashSet::new(),
|
||||||
|
message_id: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let lines = embedded_lines_for_view(&session, &HashSet::new());
|
||||||
|
assert_eq!(lines, vec!["first line".to_string(), "second line...".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_undos_view_includes_labels_and_previews() {
|
fn build_undos_view_includes_labels_and_previews() {
|
||||||
let record_one = UndoRecord {
|
let record_one = UndoRecord {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue