mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +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),
|
||||
/// Go to the add form, caret in the link field.
|
||||
OpenShareForm,
|
||||
/// Store a new link, and password, for a share already in the library.
|
||||
RepairShare {
|
||||
id: String,
|
||||
url: String,
|
||||
password: Option<String>,
|
||||
},
|
||||
AddShare {
|
||||
name: String,
|
||||
url: String,
|
||||
@@ -531,6 +537,10 @@ pub struct ShareForm {
|
||||
pub error: Option<String>,
|
||||
/// Put the caret in the link field on the next frame the form is drawn.
|
||||
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 {
|
||||
@@ -1534,6 +1544,13 @@ impl App {
|
||||
self.form.error = None;
|
||||
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 => {
|
||||
self.page = Page::Shares;
|
||||
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.
|
||||
pub fn add_account_folder(&self, name: String, folder: NodeUid) {
|
||||
let engine = self.clone();
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn show(
|
||||
.iter()
|
||||
.filter(|title| title.share_ids.contains(&share.id))
|
||||
.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);
|
||||
@@ -57,6 +57,7 @@ fn share_row(
|
||||
share: &Share,
|
||||
titles: usize,
|
||||
crawl: Option<&CrawlRecord>,
|
||||
form: &mut ShareForm,
|
||||
actions: &mut Vec<Action>,
|
||||
) {
|
||||
egui::Frame::new()
|
||||
@@ -107,12 +108,77 @@ fn share_row(
|
||||
if ui.button("Crawl").clicked() {
|
||||
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);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn link_field_id() -> egui::Id {
|
||||
egui::Id::new("share-link")
|
||||
|
||||
Reference in new issue
Block a user