refactor(v2): inline offline status, settings, and pending playlist delegates
Inlines bodies of get_offline_status, get_offline_settings, set_manual_offline, check_network, and get_pending_playlists from offline::commands directly into the v2 wrappers in commands_v2/library.rs. Deletes the entire offline::commands submodule (the inlined commands and the additional dead Tauri command shims that lived alongside them — none were registered with Tauri or referenced outside the submodule). Prunes the now-unused tauri AppHandle/Emitter imports from offline/mod.rs. Step 8 of legacy cleanup, group H. cc
This commit is contained in:
2 files changed
+153
-372
No files matched your search
@@ -1296,14 +1296,56 @@ pub fn v2_set_show_network_folders_in_manual_offline(
|
||||
pub async fn v2_get_offline_status(
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<crate::offline::OfflineStatus, String> {
|
||||
crate::offline::commands::get_offline_status(state).await
|
||||
let settings = {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.get_settings()?
|
||||
};
|
||||
|
||||
// If manual offline mode is enabled, return immediately
|
||||
if settings.manual_offline_mode {
|
||||
return Ok(crate::offline::OfflineStatus {
|
||||
is_offline: true,
|
||||
reason: Some(crate::offline::OfflineReason::ManualOverride),
|
||||
manual_mode_enabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Check network connectivity
|
||||
let has_network = crate::offline::check_network_connectivity().await;
|
||||
|
||||
if !has_network {
|
||||
return Ok(crate::offline::OfflineStatus {
|
||||
is_offline: true,
|
||||
reason: Some(crate::offline::OfflineReason::NoNetwork),
|
||||
manual_mode_enabled: false,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(crate::offline::OfflineStatus {
|
||||
is_offline: false,
|
||||
reason: None,
|
||||
manual_mode_enabled: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn v2_get_offline_settings(
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<crate::offline::OfflineSettings, String> {
|
||||
crate::offline::commands::get_offline_settings(state)
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.get_settings()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1313,12 +1355,111 @@ pub async fn v2_set_manual_offline(
|
||||
audio_state: State<'_, crate::config::audio_settings::AudioSettingsState>,
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> Result<crate::offline::OfflineStatus, String> {
|
||||
crate::offline::commands::set_manual_offline(enabled, state, audio_state, app_handle).await
|
||||
use tauri::Emitter;
|
||||
|
||||
// 1) Persist the manual offline flag first so any concurrent reads
|
||||
// see the new mode immediately.
|
||||
let (was_offline, stream_first_before): (bool, Option<bool>) = {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
let prev = store
|
||||
.get_settings()
|
||||
.map(|s| s.manual_offline_mode)
|
||||
.unwrap_or(false);
|
||||
let snapshot = store
|
||||
.get_pre_offline_stream_first_track()
|
||||
.unwrap_or(None);
|
||||
store.set_manual_offline_mode(enabled)?;
|
||||
(prev, snapshot)
|
||||
};
|
||||
|
||||
// 2) Coordinate the stream_first_track snapshot/restore. This is best-
|
||||
// effort — if the audio store isn't initialized yet (pre-login), we
|
||||
// just skip; the offline flag itself has been persisted.
|
||||
if enabled && !was_offline {
|
||||
// Entering offline mode: stash current value and force to false.
|
||||
let current = {
|
||||
let guard = audio_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Audio settings lock error: {}", e))?;
|
||||
guard
|
||||
.as_ref()
|
||||
.and_then(|s| s.get_settings().ok())
|
||||
.map(|s| s.stream_first_track)
|
||||
};
|
||||
if let Some(current_value) = current {
|
||||
{
|
||||
let guard = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_pre_offline_stream_first_track(Some(current_value));
|
||||
}
|
||||
}
|
||||
if current_value {
|
||||
let guard = audio_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Audio settings lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_stream_first_track(false);
|
||||
log::info!(
|
||||
"[Offline] stream_first_track snapshot={}; forced to false while offline",
|
||||
current_value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !enabled && was_offline {
|
||||
// Exiting offline mode: restore snapshot if we have one.
|
||||
if let Some(snapshot_value) = stream_first_before {
|
||||
{
|
||||
let guard = audio_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Audio settings lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_stream_first_track(snapshot_value);
|
||||
log::info!(
|
||||
"[Offline] restored stream_first_track to {}",
|
||||
snapshot_value
|
||||
);
|
||||
}
|
||||
}
|
||||
{
|
||||
let guard = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_pre_offline_stream_first_track(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Get updated status
|
||||
let status = v2_get_offline_status(state).await?;
|
||||
|
||||
// 4) Emit events. offline-status-changed for the offline store;
|
||||
// audio-settings-changed so the SettingsView reloads its local
|
||||
// state of stream_first_track (we may have just mutated it).
|
||||
let _ = app_handle.emit("offline-status-changed", &status);
|
||||
let _ = app_handle.emit("audio-settings-changed", ());
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn v2_check_network() -> bool {
|
||||
crate::offline::commands::check_network().await
|
||||
crate::offline::check_network_connectivity().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1391,7 +1532,14 @@ pub fn v2_mark_scrobbles_sent(ids: Vec<i64>, state: State<'_, OfflineState>) ->
|
||||
pub fn v2_get_pending_playlists(
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<Vec<crate::offline::PendingPlaylist>, String> {
|
||||
crate::offline::commands::get_pending_playlists(state)
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.get_pending_playlists()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -11,7 +11,6 @@ use rusqlite::{params, Connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
|
||||
/// Reason why the app is in offline mode
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@@ -670,369 +669,3 @@ pub async fn check_network_connectivity() -> bool {
|
||||
log::info!("Network connectivity check failed after all attempts");
|
||||
false
|
||||
}
|
||||
|
||||
// Tauri commands
|
||||
pub mod commands {
|
||||
use super::*;
|
||||
use tauri::State;
|
||||
|
||||
/// Get current offline status
|
||||
/// Note: Login state is not checked here because the frontend already handles
|
||||
/// showing LoginView when not logged in. This prevents race conditions on startup.
|
||||
#[tauri::command]
|
||||
pub async fn get_offline_status(
|
||||
offline_state: State<'_, OfflineState>,
|
||||
) -> Result<OfflineStatus, String> {
|
||||
let settings = {
|
||||
let guard__ = offline_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.get_settings()?
|
||||
};
|
||||
|
||||
// If manual offline mode is enabled, return immediately
|
||||
if settings.manual_offline_mode {
|
||||
return Ok(OfflineStatus {
|
||||
is_offline: true,
|
||||
reason: Some(OfflineReason::ManualOverride),
|
||||
manual_mode_enabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Check network connectivity
|
||||
let has_network = check_network_connectivity().await;
|
||||
|
||||
if !has_network {
|
||||
return Ok(OfflineStatus {
|
||||
is_offline: true,
|
||||
reason: Some(OfflineReason::NoNetwork),
|
||||
manual_mode_enabled: false,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(OfflineStatus {
|
||||
is_offline: false,
|
||||
reason: None,
|
||||
manual_mode_enabled: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get offline settings
|
||||
#[tauri::command]
|
||||
pub fn get_offline_settings(state: State<'_, OfflineState>) -> Result<OfflineSettings, String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.get_settings()
|
||||
}
|
||||
|
||||
/// Enable or disable manual offline mode.
|
||||
///
|
||||
/// When entering offline mode, snapshots the user's current
|
||||
/// `stream_first_track` audio setting and forces it to `false` so the
|
||||
/// player won't attempt a network pre-buffer. When exiting, restores the
|
||||
/// snapshot. This makes "offline mode" mean what it says — issue #279.
|
||||
#[tauri::command]
|
||||
pub async fn set_manual_offline(
|
||||
enabled: bool,
|
||||
state: State<'_, OfflineState>,
|
||||
audio_state: State<'_, crate::config::audio_settings::AudioSettingsState>,
|
||||
app_handle: AppHandle,
|
||||
) -> Result<OfflineStatus, String> {
|
||||
// 1) Persist the manual offline flag first so any concurrent reads
|
||||
// see the new mode immediately.
|
||||
let (was_offline, stream_first_before): (bool, Option<bool>) = {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
let prev = store
|
||||
.get_settings()
|
||||
.map(|s| s.manual_offline_mode)
|
||||
.unwrap_or(false);
|
||||
let snapshot = store
|
||||
.get_pre_offline_stream_first_track()
|
||||
.unwrap_or(None);
|
||||
store.set_manual_offline_mode(enabled)?;
|
||||
(prev, snapshot)
|
||||
};
|
||||
|
||||
// 2) Coordinate the stream_first_track snapshot/restore. This is best-
|
||||
// effort — if the audio store isn't initialized yet (pre-login), we
|
||||
// just skip; the offline flag itself has been persisted.
|
||||
if enabled && !was_offline {
|
||||
// Entering offline mode: stash current value and force to false.
|
||||
let current = {
|
||||
let guard = audio_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Audio settings lock error: {}", e))?;
|
||||
guard
|
||||
.as_ref()
|
||||
.and_then(|s| s.get_settings().ok())
|
||||
.map(|s| s.stream_first_track)
|
||||
};
|
||||
if let Some(current_value) = current {
|
||||
{
|
||||
let guard = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_pre_offline_stream_first_track(Some(current_value));
|
||||
}
|
||||
}
|
||||
if current_value {
|
||||
let guard = audio_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Audio settings lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_stream_first_track(false);
|
||||
log::info!(
|
||||
"[Offline] stream_first_track snapshot={}; forced to false while offline",
|
||||
current_value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !enabled && was_offline {
|
||||
// Exiting offline mode: restore snapshot if we have one.
|
||||
if let Some(snapshot_value) = stream_first_before {
|
||||
{
|
||||
let guard = audio_state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Audio settings lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_stream_first_track(snapshot_value);
|
||||
log::info!(
|
||||
"[Offline] restored stream_first_track to {}",
|
||||
snapshot_value
|
||||
);
|
||||
}
|
||||
}
|
||||
{
|
||||
let guard = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
if let Some(store) = guard.as_ref() {
|
||||
let _ = store.set_pre_offline_stream_first_track(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Get updated status
|
||||
let status = get_offline_status(state).await?;
|
||||
|
||||
// 4) Emit events. offline-status-changed for the offline store;
|
||||
// audio-settings-changed so the SettingsView reloads its local
|
||||
// state of stream_first_track (we may have just mutated it).
|
||||
let _ = app_handle.emit("offline-status-changed", &status);
|
||||
let _ = app_handle.emit("audio-settings-changed", ());
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
/// Set whether to show playlists with partial local content in offline mode
|
||||
#[tauri::command]
|
||||
pub fn set_show_partial_playlists(
|
||||
enabled: bool,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.set_show_partial_playlists(enabled)
|
||||
}
|
||||
|
||||
/// Set whether to allow Chromecast while in manual offline mode
|
||||
#[tauri::command]
|
||||
pub fn set_allow_cast_while_offline(
|
||||
enabled: bool,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.set_allow_cast_while_offline(enabled)
|
||||
}
|
||||
|
||||
/// Set whether to allow immediate scrobbling to Last.fm in manual offline mode
|
||||
#[tauri::command]
|
||||
pub fn set_allow_immediate_scrobbling(
|
||||
enabled: bool,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.set_allow_immediate_scrobbling(enabled)
|
||||
}
|
||||
|
||||
/// Set whether to queue scrobbles for later submission when back online
|
||||
#[tauri::command]
|
||||
pub fn set_allow_accumulated_scrobbling(
|
||||
enabled: bool,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.set_allow_accumulated_scrobbling(enabled)
|
||||
}
|
||||
|
||||
/// Set whether to show network folder content in manual offline mode
|
||||
#[tauri::command]
|
||||
pub fn set_show_network_folders_in_manual_offline(
|
||||
enabled: bool,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.set_show_network_folders_in_manual_offline(enabled)
|
||||
}
|
||||
|
||||
/// Check if network connectivity is available
|
||||
#[tauri::command]
|
||||
pub async fn check_network() -> bool {
|
||||
super::check_network_connectivity().await
|
||||
}
|
||||
|
||||
// === Pending Playlist Sync Commands ===
|
||||
|
||||
/// Get all playlists pending sync
|
||||
#[tauri::command]
|
||||
pub fn get_pending_playlists(
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<Vec<PendingPlaylist>, String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.get_pending_playlists()
|
||||
}
|
||||
|
||||
/// Add tracks to a pending playlist
|
||||
#[tauri::command]
|
||||
pub fn add_tracks_to_pending_playlist(
|
||||
pending_id: i64,
|
||||
qobuz_track_ids: Vec<u64>,
|
||||
local_track_paths: Vec<String>,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.add_tracks_to_pending_playlist(pending_id, &qobuz_track_ids, &local_track_paths)
|
||||
}
|
||||
|
||||
/// Update Qobuz playlist ID without marking as synced (for partial sync recovery)
|
||||
#[tauri::command]
|
||||
pub fn update_pending_playlist_qobuz_id(
|
||||
pending_id: i64,
|
||||
qobuz_playlist_id: u64,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.update_qobuz_playlist_id(pending_id, qobuz_playlist_id)
|
||||
}
|
||||
|
||||
/// Mark a pending playlist as synced after successful Qobuz creation
|
||||
#[tauri::command]
|
||||
pub fn mark_pending_playlist_synced(
|
||||
pending_id: i64,
|
||||
qobuz_playlist_id: u64,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.mark_playlist_synced(pending_id, qobuz_playlist_id)
|
||||
}
|
||||
|
||||
/// Delete a pending playlist
|
||||
#[tauri::command]
|
||||
pub fn delete_pending_playlist(
|
||||
pending_id: i64,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.delete_pending_playlist(pending_id)
|
||||
}
|
||||
|
||||
// === Scrobble Queue Commands ===
|
||||
|
||||
/// Mark scrobbles as sent after successful Last.fm submission
|
||||
#[tauri::command]
|
||||
pub fn mark_scrobbles_sent(
|
||||
ids: Vec<i64>,
|
||||
state: State<'_, OfflineState>,
|
||||
) -> Result<(), String> {
|
||||
let guard__ = state
|
||||
.store
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {}", e))?;
|
||||
let store = guard__
|
||||
.as_ref()
|
||||
.ok_or("No active session - please log in")?;
|
||||
store.mark_scrobbles_sent(&ids)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user