mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-04 00:47:00 +01:00
feat(app): re-enter a share's link on the desktop
A keyring that was reset, or a link password that changed, left a share the desktop could only remove and add again, losing its catalog, watch history and downloads. The phones have had a way back since B18; the desktop's share rows now have Re-enter link, which stores the new secret through ShareStore::replace_secrets, reopens the share and crawls it.
This commit is contained in:
1 parent
3ff55a9af9
commit
5dfb717235
3 files changed
+112
-1
No files matched your search
@@ -131,6 +131,12 @@ pub enum Action {
|
|||||||
PasteShare(String),
|
PasteShare(String),
|
||||||
/// Go to the add form, caret in the link field.
|
/// Go to the add form, caret in the link field.
|
||||||
OpenShareForm,
|
OpenShareForm,
|
||||||
|
/// Store a new link, and password, for a share already in the library.
|
||||||
|
RepairShare {
|
||||||
|
id: String,
|
||||||
|
url: String,
|
||||||
|
password: Option<String>,
|
||||||
|
},
|
||||||
AddShare {
|
AddShare {
|
||||||
name: String,
|
name: String,
|
||||||
url: String,
|
url: String,
|
||||||
@@ -531,6 +537,10 @@ pub struct ShareForm {
|
|||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
/// Put the caret in the link field on the next frame the form is drawn.
|
/// Put the caret in the link field on the next frame the form is drawn.
|
||||||
pub focus: bool,
|
pub focus: bool,
|
||||||
|
/// The share whose link is being re-entered in its own row, if any.
|
||||||
|
pub repairing: Option<String>,
|
||||||
|
pub repair_url: String,
|
||||||
|
pub repair_password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@@ -1534,6 +1544,13 @@ impl App {
|
|||||||
self.form.error = None;
|
self.form.error = None;
|
||||||
self.note(ctx, "share link pasted — add it, or name it first", false);
|
self.note(ctx, "share link pasted — add it, or name it first", false);
|
||||||
}
|
}
|
||||||
|
Action::RepairShare { id, url, password } => {
|
||||||
|
self.crawling = true;
|
||||||
|
self.form.repairing = None;
|
||||||
|
self.form.repair_url.clear();
|
||||||
|
self.form.repair_password.clear();
|
||||||
|
self.engine.repair_share(id, url, password);
|
||||||
|
}
|
||||||
Action::OpenShareForm => {
|
Action::OpenShareForm => {
|
||||||
self.page = Page::Shares;
|
self.page = Page::Shares;
|
||||||
self.form.focus = true;
|
self.form.focus = true;
|
||||||
|
|||||||
@@ -682,6 +682,34 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Re-supply the link behind a share whose stored secret is unreadable —
|
||||||
|
/// a keyring that was reset — or whose custom password changed, keeping
|
||||||
|
/// its catalog, watch state and downloads. See
|
||||||
|
/// [`ShareStore::replace_secrets`]. Then crawl it, which is what proves
|
||||||
|
/// the new secret works.
|
||||||
|
pub fn repair_share(&self, id: String, url: String, password: Option<String>) {
|
||||||
|
let engine = self.clone();
|
||||||
|
self.runtime.spawn(async move {
|
||||||
|
let share = match engine.store.replace_secrets(&id, &url, password.as_deref()) {
|
||||||
|
Ok(share) => share,
|
||||||
|
Err(error) => {
|
||||||
|
// The add form is not where this was asked, and nothing
|
||||||
|
// will be crawled to say the crawl is over.
|
||||||
|
engine.emit(Event::CrawlFinished);
|
||||||
|
return engine.fail("update the link", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
engine.emit(Event::Status(format!(
|
||||||
|
"updated the link for {}",
|
||||||
|
share.name
|
||||||
|
)));
|
||||||
|
engine.load_shares();
|
||||||
|
// Its client was opened with the old secret.
|
||||||
|
engine.connection.lock().take();
|
||||||
|
engine.connect_and_crawl(Some(vec![share.id]), false).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a folder of the signed-in account as a share, then crawl it.
|
/// Add a folder of the signed-in account as a share, then crawl it.
|
||||||
pub fn add_account_folder(&self, name: String, folder: NodeUid) {
|
pub fn add_account_folder(&self, name: String, folder: NodeUid) {
|
||||||
let engine = self.clone();
|
let engine = self.clone();
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub fn show(
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|title| title.share_ids.contains(&share.id))
|
.filter(|title| title.share_ids.contains(&share.id))
|
||||||
.count();
|
.count();
|
||||||
share_row(ui, share, titles, crawls.get(&share.id), actions);
|
share_row(ui, share, titles, crawls.get(&share.id), form, actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(theme::space::XXL);
|
ui.add_space(theme::space::XXL);
|
||||||
@@ -57,6 +57,7 @@ fn share_row(
|
|||||||
share: &Share,
|
share: &Share,
|
||||||
titles: usize,
|
titles: usize,
|
||||||
crawl: Option<&CrawlRecord>,
|
crawl: Option<&CrawlRecord>,
|
||||||
|
form: &mut ShareForm,
|
||||||
actions: &mut Vec<Action>,
|
actions: &mut Vec<Action>,
|
||||||
) {
|
) {
|
||||||
egui::Frame::new()
|
egui::Frame::new()
|
||||||
@@ -107,12 +108,77 @@ fn share_row(
|
|||||||
if ui.button("Crawl").clicked() {
|
if ui.button("Crawl").clicked() {
|
||||||
actions.push(Action::Crawl(Some(share.id.clone())));
|
actions.push(Action::Crawl(Some(share.id.clone())));
|
||||||
}
|
}
|
||||||
|
// Not for a folder of the account, which has no link.
|
||||||
|
if share.folder.is_none()
|
||||||
|
&& ui
|
||||||
|
.button("Re-enter link")
|
||||||
|
.on_hover_text(
|
||||||
|
"For a keyring that lost the link, or a password that changed. \
|
||||||
|
The library, history and downloads stay.",
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
form.repairing = Some(share.id.clone());
|
||||||
|
form.repair_url.clear();
|
||||||
|
form.repair_password.clear();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
if form.repairing.as_deref() == Some(share.id.as_str()) {
|
||||||
|
repair_form(ui, share, form, actions);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ui.add_space(theme::space::S);
|
ui.add_space(theme::space::S);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The same link again, and a password if it has one, for a share already in
|
||||||
|
/// the library.
|
||||||
|
fn repair_form(ui: &mut egui::Ui, share: &Share, form: &mut ShareForm, actions: &mut Vec<Action>) {
|
||||||
|
ui.add_space(theme::space::M);
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::singleline(&mut form.repair_url)
|
||||||
|
.password(true)
|
||||||
|
.hint_text("The share link, including everything after the #")
|
||||||
|
.desired_width(f32::INFINITY),
|
||||||
|
);
|
||||||
|
ui.add_space(theme::space::S);
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::singleline(&mut form.repair_password)
|
||||||
|
.password(true)
|
||||||
|
.hint_text("Link password, if it has one")
|
||||||
|
.desired_width(f32::INFINITY),
|
||||||
|
);
|
||||||
|
ui.add_space(theme::space::S);
|
||||||
|
let problem = (!form.repair_url.trim().is_empty())
|
||||||
|
.then(|| pstr_core::shares::share_token(form.repair_url.trim()).err())
|
||||||
|
.flatten();
|
||||||
|
if let Some(problem) = &problem {
|
||||||
|
ui.label(
|
||||||
|
theme::Role::Caption
|
||||||
|
.rich(problem.to_string())
|
||||||
|
.color(theme::danger()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let ready = !form.repair_url.trim().is_empty() && problem.is_none();
|
||||||
|
if ui
|
||||||
|
.add_enabled_ui(ready, |ui| ui::accent_button(ui, "Save"))
|
||||||
|
.inner
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
actions.push(Action::RepairShare {
|
||||||
|
id: share.id.clone(),
|
||||||
|
url: form.repair_url.trim().to_owned(),
|
||||||
|
password: Some(form.repair_password.clone())
|
||||||
|
.filter(|password| !password.is_empty()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ui.button("Cancel").clicked() {
|
||||||
|
form.repairing = None;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// The add form's link field, which the library's empty state focuses.
|
/// The add form's link field, which the library's empty state focuses.
|
||||||
pub fn link_field_id() -> egui::Id {
|
pub fn link_field_id() -> egui::Id {
|
||||||
egui::Id::new("share-link")
|
egui::Id::new("share-link")
|
||||||
|
|||||||
Reference in new issue
Block a user