fix(cloud-files): Pin loest Hydration aus, Icon-Refresh via SHChangeNotify

CfSetPinState aendert nur das Pin-Flag - ohne expliziten Call
passiert am Disk-Inhalt nichts und das Explorer-Icon bleibt
unveraendert. Darum klickte "Immer offline verfuegbar" scheinbar
ins Leere.

- Bei Pin: CfHydratePlaceholder triggert FETCH_DATA und laedt die
  Datei komplett herunter
- Bei Unpin: CfDehydratePlaceholder (war schon da)
- Nach jeder Zustandsaenderung SHChangeNotify(SHCNE_UPDATEITEM)
  damit das Overlay-Icon sofort neu gezeichnet wird, ohne dass
  der User F5 druecken muss
- Log bekommt zusaetzlich hydrate_err fuer Debugging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker 2026-04-23 23:09:28 +02:00
parent 78615d8897
commit dd40c55f7d
1 changed files with 51 additions and 17 deletions

View File

@ -560,11 +560,22 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
CF::CfSetPinState(handle, state, CF::CF_SET_PIN_FLAG_NONE, None) CF::CfSetPinState(handle, state, CF::CF_SET_PIN_FLAG_NONE, None)
}; };
// "Speicher freigeben" (unpin): sofort dehydrieren, damit der Platz // Hydrate bei Pin / Dehydrate bei Unpin. CfSetPinState aendert nur
// wirklich frei wird. CfSetPinState allein aendert nur den Zustand. // das Flag - ohne explizite Hydrate-/Dehydrate-Calls passiert am
// Der Rueckgabewert wird geloggt aber nicht hart als Fehler gewertet, // Disk-Inhalt und am Icon nichts Sichtbares.
// weil die Datei evtl. schon dehydriert ist. let (hydrate_err, dehydrate_err) = if set_res.is_ok() {
let dehydrate_err = if !pinned && set_res.is_ok() { if pinned {
let r = unsafe {
CF::CfHydratePlaceholder(
handle,
0,
-1,
CF::CF_HYDRATE_FLAG_NONE,
None,
)
};
(r.err().map(|e| format!("{:?}", e)), None)
} else {
let r = unsafe { let r = unsafe {
CF::CfDehydratePlaceholder( CF::CfDehydratePlaceholder(
handle, handle,
@ -574,15 +585,19 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
None, None,
) )
}; };
r.err().map(|e| format!("{:?}", e)) (None, r.err().map(|e| format!("{:?}", e)))
}
} else { } else {
None (None, None)
}; };
unsafe { unsafe {
let _ = windows::Win32::Foundation::CloseHandle(handle); let _ = windows::Win32::Foundation::CloseHandle(handle);
} }
// Explorer Icon-Overlay aktualisieren
notify_file_update(file);
// Log-Verzeichnis ist der Mount-Ordner oder dessen Parent // Log-Verzeichnis ist der Mount-Ordner oder dessen Parent
let log_dir = file let log_dir = file
.ancestors() .ancestors()
@ -592,10 +607,11 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
log_msg( log_msg(
&log_dir, &log_dir,
&format!( &format!(
"set_pin_state file={} pinned={} result={:?} dehydrate_err={:?}", "set_pin_state file={} pinned={} result={:?} hydrate_err={:?} dehydrate_err={:?}",
file.display(), file.display(),
pinned, pinned,
set_res, set_res,
hydrate_err,
dehydrate_err dehydrate_err
), ),
); );
@ -603,3 +619,21 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
set_res.map_err(|e| format!("CfSetPinState: {e}"))?; set_res.map_err(|e| format!("CfSetPinState: {e}"))?;
Ok(()) Ok(())
} }
/// Sagt dem Shell "diese Datei hat sich geaendert" damit das Overlay-
/// Icon (Wolke/Haken) aktualisiert wird, ohne dass der User F5 druecken
/// muss.
fn notify_file_update(file: &Path) {
use windows::Win32::UI::Shell::{SHChangeNotify, SHCNE_UPDATEITEM, SHCNF_PATHW};
let Ok(w) = U16CString::from_str(file.to_string_lossy().as_ref()) else {
return;
};
unsafe {
SHChangeNotify(
SHCNE_UPDATEITEM,
SHCNF_PATHW,
Some(w.as_ptr() as _),
None,
);
}
}