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)
};
// "Speicher freigeben" (unpin): sofort dehydrieren, damit der Platz
// wirklich frei wird. CfSetPinState allein aendert nur den Zustand.
// Der Rueckgabewert wird geloggt aber nicht hart als Fehler gewertet,
// weil die Datei evtl. schon dehydriert ist.
let dehydrate_err = if !pinned && set_res.is_ok() {
// Hydrate bei Pin / Dehydrate bei Unpin. CfSetPinState aendert nur
// das Flag - ohne explizite Hydrate-/Dehydrate-Calls passiert am
// Disk-Inhalt und am Icon nichts Sichtbares.
let (hydrate_err, dehydrate_err) = if 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 {
CF::CfDehydratePlaceholder(
handle,
@ -574,15 +585,19 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
None,
)
};
r.err().map(|e| format!("{:?}", e))
(None, r.err().map(|e| format!("{:?}", e)))
}
} else {
None
(None, None)
};
unsafe {
let _ = windows::Win32::Foundation::CloseHandle(handle);
}
// Explorer Icon-Overlay aktualisieren
notify_file_update(file);
// Log-Verzeichnis ist der Mount-Ordner oder dessen Parent
let log_dir = file
.ancestors()
@ -592,10 +607,11 @@ pub fn set_pin_state(file: &Path, pinned: bool) -> Result<(), String> {
log_msg(
&log_dir,
&format!(
"set_pin_state file={} pinned={} result={:?} dehydrate_err={:?}",
"set_pin_state file={} pinned={} result={:?} hydrate_err={:?} dehydrate_err={:?}",
file.display(),
pinned,
set_res,
hydrate_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}"))?;
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,
);
}
}