feat: let new installs choose the data folder
This commit is contained in:
parent
776b5a8562
commit
3d0c880096
13 changed files with 234 additions and 103 deletions
|
|
@ -111,6 +111,7 @@ pub struct OnboardingSelectionState {
|
|||
/// `None` = don't import
|
||||
pub import_folder: State<Option<String>>,
|
||||
pub import_dedicated: State<bool>,
|
||||
pub picks_location: State<bool>,
|
||||
}
|
||||
|
||||
pub fn use_provide_onboarding_selection(state: OnboardingSelectionState) {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use freya::router::{Outlet, use_route};
|
|||
use crate::Route;
|
||||
use crate::components::OnboardingNavbar;
|
||||
use crate::hooks::{
|
||||
OnboardingSelectionState, has_migration_data, onboarding_bundles_items, use_migration,
|
||||
use_onboarding_bundles, use_provide_onboarding_selection, use_settings_snapshot,
|
||||
OnboardingSelectionState, has_migration_data, onboarding_bundles_items, use_launcher,
|
||||
use_migration, use_onboarding_bundles, use_provide_onboarding_selection, use_settings_snapshot,
|
||||
};
|
||||
use crate::theme::colors;
|
||||
use crate::view::onboarding::{
|
||||
|
|
@ -28,6 +28,10 @@ impl Component for OnboardingShell {
|
|||
let setup_started = use_state(|| false);
|
||||
let import_folder = use_state(|| None::<String>);
|
||||
let import_dedicated = use_state(|| false);
|
||||
let picks_location = use_state({
|
||||
let fresh = use_launcher().needs_location;
|
||||
move || fresh
|
||||
});
|
||||
use_provide_onboarding_selection(OnboardingSelectionState {
|
||||
selected,
|
||||
user_touched,
|
||||
|
|
@ -38,13 +42,15 @@ impl Component for OnboardingShell {
|
|||
setup_started,
|
||||
import_folder,
|
||||
import_dedicated,
|
||||
picks_location,
|
||||
});
|
||||
|
||||
let migration_query = use_migration();
|
||||
let detected_migration = has_migration_data(&migration_query);
|
||||
|
||||
let route = use_route::<Route>();
|
||||
let step_index = onboarding_step_index(&route, detected_migration);
|
||||
let choosing_location = *picks_location.read();
|
||||
let step_index = onboarding_step_index(&route, detected_migration, choosing_location);
|
||||
|
||||
let onboarded = use_settings_snapshot().settings.seen_onboarding;
|
||||
let reconsent = onboarded && matches!(&route, Route::OnboardingTerms {});
|
||||
|
|
@ -89,8 +95,11 @@ impl Component for OnboardingShell {
|
|||
.content(Content::Flex)
|
||||
.layer(Layer::Relative(30))
|
||||
.maybe_child((!reconsent).then(|| {
|
||||
progress_bar(step_index, onboarding_total(detected_migration))
|
||||
.into_element()
|
||||
progress_bar(
|
||||
step_index,
|
||||
onboarding_total(detected_migration, choosing_location),
|
||||
)
|
||||
.into_element()
|
||||
}))
|
||||
.child(OnboardingNavbar)
|
||||
.child(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::layout::RootLayout;
|
|||
use crate::layout::SettingsShell;
|
||||
|
||||
use crate::view::{
|
||||
NotFound, Relocating, SetupLocation, Startup,
|
||||
NotFound, Relocating, Startup,
|
||||
app::{
|
||||
AccountSkins, Clusters, Debug, Home, Stats,
|
||||
browser::{Browser, BrowserPackage},
|
||||
|
|
@ -24,7 +24,8 @@ use crate::view::{
|
|||
},
|
||||
onboarding::{
|
||||
OnboardingAccount, OnboardingBundles, OnboardingDownloading, OnboardingLanguage,
|
||||
OnboardingMigration, OnboardingPreferences, OnboardingTerms, OnboardingWelcome,
|
||||
OnboardingLocation, OnboardingMigration, OnboardingPreferences, OnboardingTerms,
|
||||
OnboardingWelcome,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -34,14 +35,14 @@ pub enum Route {
|
|||
#[layout(RootLayout)]
|
||||
#[route("/")]
|
||||
Startup {},
|
||||
#[route("/setup/location")]
|
||||
SetupLocation {},
|
||||
#[route("/moving")]
|
||||
Relocating {},
|
||||
|
||||
#[layout(OnboardingShell)]
|
||||
#[route("/onboarding")]
|
||||
OnboardingWelcome {},
|
||||
#[route("/onboarding/location")]
|
||||
OnboardingLocation {},
|
||||
#[route("/onboarding/terms")]
|
||||
OnboardingTerms {},
|
||||
#[route("/onboarding/migration")]
|
||||
|
|
@ -128,9 +129,9 @@ impl Route {
|
|||
pub fn title(&self) -> String {
|
||||
match self {
|
||||
Route::Startup { .. } => "Startup".to_string(),
|
||||
Route::SetupLocation { .. } => "Setup".to_string(),
|
||||
Route::Relocating { .. } => "Moving".to_string(),
|
||||
Route::OnboardingWelcome { .. } => "Welcome".to_string(),
|
||||
Route::OnboardingLocation { .. } => "Folder".to_string(),
|
||||
Route::OnboardingTerms { .. } => "Terms & Privacy".to_string(),
|
||||
Route::OnboardingMigration { .. } => "Migration".to_string(),
|
||||
Route::OnboardingLanguage { .. } => "Language".to_string(),
|
||||
|
|
|
|||
|
|
@ -195,9 +195,7 @@ impl Component for DataFolder {
|
|||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Source {
|
||||
/// Whatever folder the picker comes back with
|
||||
Picked,
|
||||
/// The folder OneClient uses when nothing is set
|
||||
Default,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
mod not_found;
|
||||
mod relocating;
|
||||
mod setup_location;
|
||||
mod startup;
|
||||
|
||||
pub mod app;
|
||||
|
|
@ -9,5 +8,4 @@ pub mod onboarding;
|
|||
|
||||
pub use not_found::NotFound;
|
||||
pub use relocating::Relocating;
|
||||
pub use setup_location::SetupLocation;
|
||||
pub use startup::Startup;
|
||||
|
|
|
|||
|
|
@ -3,29 +3,40 @@ use std::path::PathBuf;
|
|||
use freya::prelude::*;
|
||||
use freya::router::RouterContext;
|
||||
|
||||
use crate::components::Button;
|
||||
use crate::hooks::{use_dispatch, use_launcher};
|
||||
use crate::components::IconType;
|
||||
use crate::hooks::{use_dispatch, use_launcher, use_onboarding_selection};
|
||||
use crate::routes::Route;
|
||||
use crate::state::AppChannel;
|
||||
use crate::theme::colors;
|
||||
use crate::ui::note;
|
||||
use crate::view::onboarding::choice_row;
|
||||
use crate::view::onboarding::{
|
||||
choice_row, onboarding_illustration, onboarding_nav_action, onboarding_page, step_heading,
|
||||
};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct SetupLocation;
|
||||
pub struct OnboardingLocation;
|
||||
|
||||
impl Component for SetupLocation {
|
||||
impl Component for OnboardingLocation {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let actions = use_dispatch();
|
||||
let launcher = use_launcher();
|
||||
let selection = use_onboarding_selection();
|
||||
|
||||
let mut picked = use_state(|| None::<PathBuf>);
|
||||
let mut warning = use_state(|| None::<String>);
|
||||
let mut error = use_state(|| None::<String>);
|
||||
let mut busy = use_state(|| false);
|
||||
let mut waiting = use_state(|| false);
|
||||
|
||||
if !launcher.needs_location {
|
||||
let _ = RouterContext::get().replace(Route::Startup {});
|
||||
let answered = !launcher.needs_location;
|
||||
let settled = answered && launcher.ready;
|
||||
|
||||
if !*selection.picks_location.read() && settled {
|
||||
let _ = RouterContext::get().replace(Route::OnboardingTerms {});
|
||||
return rect().into_element();
|
||||
}
|
||||
|
||||
if *waiting.read() && launcher.ready {
|
||||
let _ = RouterContext::get().replace(Route::OnboardingTerms {});
|
||||
return rect().into_element();
|
||||
}
|
||||
|
||||
|
|
@ -33,8 +44,13 @@ impl Component for SetupLocation {
|
|||
.map(|dir| dir.display().to_string())
|
||||
.unwrap_or_default();
|
||||
|
||||
let confirm = move |_| {
|
||||
if *busy.peek() {
|
||||
let confirm = move |()| {
|
||||
if *waiting.peek() {
|
||||
return;
|
||||
}
|
||||
|
||||
if settled {
|
||||
let _ = RouterContext::get().replace(Route::OnboardingTerms {});
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -43,19 +59,17 @@ impl Component for SetupLocation {
|
|||
let station = actions.station();
|
||||
let events = actions.events();
|
||||
|
||||
busy.set(true);
|
||||
waiting.set(true);
|
||||
error.set(None);
|
||||
|
||||
spawn_forever(async move {
|
||||
if let Err(message) =
|
||||
oneclient_core::settings::data_dir::apply(chosen).await
|
||||
{
|
||||
error.set(Some(message));
|
||||
busy.set(false);
|
||||
return;
|
||||
}
|
||||
if !answered {
|
||||
if let Err(message) = oneclient_core::settings::data_dir::apply(chosen).await {
|
||||
error.set(Some(message));
|
||||
waiting.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
let mut station = station;
|
||||
let mut guard = station.write_channel(AppChannel::Launcher);
|
||||
guard.launcher.needs_location = false;
|
||||
|
|
@ -63,40 +77,50 @@ impl Component for SetupLocation {
|
|||
|
||||
match crate::events::start_launcher(station, events).await {
|
||||
Ok(()) => actions.sync_bundles(),
|
||||
Err(err) => crate::events::report_startup_failure(&station, &err),
|
||||
Err(err) => {
|
||||
crate::events::report_startup_failure(&station, &err);
|
||||
waiting.set(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let chosen = picked.read().clone();
|
||||
let running = *busy.read();
|
||||
let running = *waiting.read();
|
||||
|
||||
let mut content = rect()
|
||||
.vertical()
|
||||
.width(Size::px(520.))
|
||||
.spacing(20.)
|
||||
.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::fill())
|
||||
.spacing(8.)
|
||||
.child(
|
||||
label()
|
||||
.text("Choose a folder location for OneClient")
|
||||
.font_size(26.)
|
||||
.font_weight(FontWeight::BOLD)
|
||||
.color(colors::fg_primary()),
|
||||
)
|
||||
.child(
|
||||
label()
|
||||
.text(
|
||||
"Any content downloaded via OneClient (such as Minecraft versions, mods, your worlds) will go in this folder. Make sure you have sufficient storage in your chosen disk.",
|
||||
)
|
||||
.font_size(14.)
|
||||
.color(colors::fg_secondary()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
.width(Size::fill())
|
||||
.spacing(16.)
|
||||
.child(step_heading(
|
||||
"Where should OneClient live?",
|
||||
"All your data including mods, worlds and game settings gets stored here. This can be changed later from Settings.",
|
||||
));
|
||||
|
||||
content = if answered {
|
||||
let folder = if launcher.data_dir.is_empty() {
|
||||
chosen
|
||||
.as_ref()
|
||||
.map(|path| path.display().to_string())
|
||||
.unwrap_or_else(|| default_dir.clone())
|
||||
} else {
|
||||
launcher.data_dir.clone()
|
||||
};
|
||||
|
||||
content
|
||||
.child(choice_row("Your folder", &folder, true, |()| {}))
|
||||
.child(
|
||||
label()
|
||||
.text(
|
||||
"This is where OneClient is set up. Settings can move it once you are \
|
||||
through setup.",
|
||||
)
|
||||
.font_size(13.)
|
||||
.max_lines(2)
|
||||
.color(colors::fg_secondary()),
|
||||
)
|
||||
} else {
|
||||
content.child(
|
||||
rect()
|
||||
.vertical()
|
||||
.width(Size::fill())
|
||||
|
|
@ -120,33 +144,38 @@ impl Component for SetupLocation {
|
|||
chosen.is_some(),
|
||||
move |()| browse(picked, warning, error),
|
||||
)),
|
||||
);
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(message) = warning.read().clone() {
|
||||
content = content.child(note(message, colors::code_warn()));
|
||||
}
|
||||
|
||||
if let Some(message) = error.read().clone() {
|
||||
let failure = error.read().clone().or_else(|| launcher.error.clone());
|
||||
|
||||
if let Some(message) = failure {
|
||||
content = content.child(note(message, colors::danger()));
|
||||
}
|
||||
|
||||
content = content.child(
|
||||
Button::new()
|
||||
.primary()
|
||||
.width(Size::px(160.))
|
||||
.enabled(!running)
|
||||
.on_press(confirm)
|
||||
.text(if running { "Setting up…" } else { "Continue" }),
|
||||
);
|
||||
let next_label = if running {
|
||||
"Setting up…"
|
||||
} else if answered && !settled {
|
||||
"Try again"
|
||||
} else {
|
||||
"Next"
|
||||
};
|
||||
|
||||
rect()
|
||||
.width(Size::fill())
|
||||
.height(Size::fill())
|
||||
.center()
|
||||
.background(colors::page())
|
||||
.window_drag()
|
||||
.child(content)
|
||||
.into_element()
|
||||
onboarding_page(
|
||||
onboarding_illustration(IconType::Folder),
|
||||
content.into_element(),
|
||||
onboarding_nav_action(
|
||||
Some(Route::OnboardingWelcome {}),
|
||||
next_label,
|
||||
!running,
|
||||
confirm,
|
||||
),
|
||||
)
|
||||
.into_element()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2,6 +2,7 @@ mod account;
|
|||
mod bundles;
|
||||
mod downloading;
|
||||
mod language;
|
||||
mod location;
|
||||
mod migration;
|
||||
mod preferences;
|
||||
mod selection;
|
||||
|
|
@ -19,6 +20,7 @@ pub use account::OnboardingAccount;
|
|||
pub use bundles::OnboardingBundles;
|
||||
pub use downloading::{LoadingBackdrop, OnboardingDownloading};
|
||||
pub use language::OnboardingLanguage;
|
||||
pub use location::OnboardingLocation;
|
||||
pub use migration::OnboardingMigration;
|
||||
pub(crate) use migration::matching_new_cluster_id;
|
||||
pub use preferences::OnboardingPreferences;
|
||||
|
|
@ -37,18 +39,19 @@ use crate::hooks::{has_migration_data, use_migration, use_onboarding_selection};
|
|||
use crate::theme::colors;
|
||||
use crate::ui::{border_all_color, entrance_motion_layer};
|
||||
|
||||
/// The v1-migration step only exists and is only counted when old launcher data was detected
|
||||
pub fn onboarding_total(has_migration: bool) -> usize {
|
||||
if has_migration { 8 } else { 7 }
|
||||
pub fn onboarding_total(has_migration: bool, picks_location: bool) -> usize {
|
||||
7 + usize::from(has_migration) + usize::from(picks_location)
|
||||
}
|
||||
|
||||
pub fn onboarding_step_index(route: &Route, has_migration: bool) -> usize {
|
||||
let shift = if has_migration { 1 } else { 0 };
|
||||
pub fn onboarding_step_index(route: &Route, has_migration: bool, picks_location: bool) -> usize {
|
||||
let location = usize::from(picks_location);
|
||||
let shift = location + usize::from(has_migration);
|
||||
|
||||
match route {
|
||||
Route::OnboardingWelcome {} => 0,
|
||||
Route::OnboardingTerms {} => 1,
|
||||
Route::OnboardingMigration {} => 2,
|
||||
Route::OnboardingLocation {} => 1,
|
||||
Route::OnboardingTerms {} => 1 + location,
|
||||
Route::OnboardingMigration {} => 2 + location,
|
||||
Route::OnboardingLanguage {} => 2 + shift,
|
||||
Route::OnboardingAccount {} => 3 + shift,
|
||||
Route::OnboardingBundles {} => 4 + shift,
|
||||
|
|
@ -65,8 +68,13 @@ const SLIDE_DISTANCE: f32 = 44.;
|
|||
pub(crate) fn onboarding_slide(content: impl IntoElement) -> impl IntoElement {
|
||||
let route = use_route::<Route>();
|
||||
let migration_query = use_migration();
|
||||
let step = onboarding_step_index(&route, has_migration_data(&migration_query));
|
||||
let reduce_motion = use_onboarding_selection().reduce_motion;
|
||||
let selection = use_onboarding_selection();
|
||||
let step = onboarding_step_index(
|
||||
&route,
|
||||
has_migration_data(&migration_query),
|
||||
*selection.picks_location.read(),
|
||||
);
|
||||
let reduce_motion = selection.reduce_motion;
|
||||
|
||||
let direction = use_hook(|| {
|
||||
let prev = LAST_STEP.swap(step, Ordering::Relaxed);
|
||||
|
|
@ -157,6 +165,19 @@ pub(crate) fn onboarding_nav(
|
|||
next: Route,
|
||||
next_enabled: bool,
|
||||
) -> impl IntoElement {
|
||||
onboarding_nav_action(back, "Next", next_enabled, move |()| {
|
||||
let _ = RouterContext::get().replace(next.clone());
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn onboarding_nav_action(
|
||||
back: Option<Route>,
|
||||
next_label: &str,
|
||||
next_enabled: bool,
|
||||
on_next: impl FnMut(()) + 'static,
|
||||
) -> impl IntoElement {
|
||||
let mut on_next = on_next;
|
||||
|
||||
rect()
|
||||
.horizontal()
|
||||
.width(Size::fill())
|
||||
|
|
@ -179,10 +200,8 @@ pub(crate) fn onboarding_nav(
|
|||
.primary()
|
||||
.width(Size::px(140.))
|
||||
.enabled(next_enabled)
|
||||
.on_press(move |_| {
|
||||
let _ = RouterContext::get().replace(next.clone());
|
||||
})
|
||||
.text("Next")
|
||||
.on_press(move |_| on_next(()))
|
||||
.text(next_label.to_string())
|
||||
.child(Icon::new(IconType::ArrowRight).size(16.)),
|
||||
)
|
||||
.into_element()
|
||||
|
|
@ -347,3 +366,69 @@ pub(crate) fn step_heading(title: &str, subtitle: &str) -> impl IntoElement {
|
|||
)
|
||||
.into_element()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn flow(has_migration: bool, picks_location: bool) -> Vec<Route> {
|
||||
let mut routes = vec![Route::OnboardingWelcome {}];
|
||||
|
||||
if picks_location {
|
||||
routes.push(Route::OnboardingLocation {});
|
||||
}
|
||||
|
||||
routes.push(Route::OnboardingTerms {});
|
||||
|
||||
if has_migration {
|
||||
routes.push(Route::OnboardingMigration {});
|
||||
}
|
||||
|
||||
routes.extend([
|
||||
Route::OnboardingLanguage {},
|
||||
Route::OnboardingAccount {},
|
||||
Route::OnboardingBundles {},
|
||||
Route::OnboardingPreferences {},
|
||||
Route::OnboardingDownloading {},
|
||||
]);
|
||||
|
||||
routes
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_run_numbers_its_steps_one_after_another() {
|
||||
for has_migration in [false, true] {
|
||||
for picks_location in [false, true] {
|
||||
let routes = flow(has_migration, picks_location);
|
||||
let total = onboarding_total(has_migration, picks_location);
|
||||
|
||||
assert_eq!(
|
||||
routes.len(),
|
||||
total,
|
||||
"the bar is drawn out of {total} but the run has {} steps \
|
||||
(migration: {has_migration}, folder: {picks_location})",
|
||||
routes.len()
|
||||
);
|
||||
|
||||
for (position, route) in routes.iter().enumerate() {
|
||||
assert_eq!(
|
||||
onboarding_step_index(route, has_migration, picks_location),
|
||||
position,
|
||||
"{route:?} is numbered off its place in the run \
|
||||
(migration: {has_migration}, folder: {picks_location})"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_run_without_the_folder_step_is_numbered_as_it_always_was() {
|
||||
assert_eq!(onboarding_total(false, false), 7);
|
||||
assert_eq!(onboarding_total(true, false), 8);
|
||||
assert_eq!(
|
||||
onboarding_step_index(&Route::OnboardingTerms {}, false, false),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::components::{
|
|||
};
|
||||
use crate::hooks::{
|
||||
TermsQuery, has_migration_data, terms_document, terms_error, terms_is_loading, use_dispatch,
|
||||
use_migration, use_settings_snapshot, use_terms,
|
||||
use_migration, use_onboarding_selection, use_settings_snapshot, use_terms,
|
||||
};
|
||||
use crate::platform::open_url;
|
||||
use crate::routes::Route;
|
||||
|
|
@ -49,7 +49,13 @@ impl Component for OnboardingTerms {
|
|||
} else {
|
||||
Route::OnboardingLanguage {}
|
||||
};
|
||||
let back = (!returning).then_some(Route::OnboardingWelcome {});
|
||||
let back = (!returning).then(|| {
|
||||
if *use_onboarding_selection().picks_location.read() {
|
||||
Route::OnboardingLocation {}
|
||||
} else {
|
||||
Route::OnboardingWelcome {}
|
||||
}
|
||||
});
|
||||
|
||||
let terms_version = document.as_ref().map(|doc| doc.version).unwrap_or(1);
|
||||
let privacy_version = document
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use freya::prelude::*;
|
||||
|
||||
use crate::components::IconType;
|
||||
use crate::hooks::use_onboarding_selection;
|
||||
use crate::routes::Route;
|
||||
use crate::theme::colors;
|
||||
use crate::view::onboarding::{
|
||||
|
|
@ -12,6 +13,12 @@ pub struct OnboardingWelcome;
|
|||
|
||||
impl Component for OnboardingWelcome {
|
||||
fn render(&self) -> impl IntoElement {
|
||||
let next = if *use_onboarding_selection().picks_location.read() {
|
||||
Route::OnboardingLocation {}
|
||||
} else {
|
||||
Route::OnboardingTerms {}
|
||||
};
|
||||
|
||||
let content = rect()
|
||||
.vertical()
|
||||
.width(Size::fill())
|
||||
|
|
@ -31,7 +38,7 @@ impl Component for OnboardingWelcome {
|
|||
onboarding_page(
|
||||
onboarding_illustration(IconType::OnboardingWelcome),
|
||||
content,
|
||||
onboarding_nav(None, Route::OnboardingTerms {}, true),
|
||||
onboarding_nav(None, next, true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ impl Component for Startup {
|
|||
let logo = use_memo(|| AppAssets::get_bytes("logo.svg").unwrap_or_default());
|
||||
|
||||
if launcher.needs_location {
|
||||
let _ = RouterContext::get().replace(Route::SetupLocation {});
|
||||
let _ = RouterContext::get().replace(Route::OnboardingWelcome {});
|
||||
return rect().into_element();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ fn ready_to_move(state: &LauncherState) -> Result<PathBuf, String> {
|
|||
Ok(from)
|
||||
}
|
||||
|
||||
/// `settled` names what the destination is allowed to be holding already
|
||||
async fn weigh(
|
||||
state: &LauncherState,
|
||||
from: PathBuf,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ const LOW_SPACE_BYTES: u64 = 5 * 1000 * 1000 * 1000;
|
|||
|
||||
const PROBE_NAME: &str = ".oneclient_write_test";
|
||||
|
||||
/// What the operating system leaves lying around on its own. A folder holding
|
||||
/// nothing else is one the person who picked it would call empty.
|
||||
const OS_CLUTTER: &[&str] = &[
|
||||
".DS_Store",
|
||||
".localized",
|
||||
|
|
@ -31,8 +29,6 @@ pub struct DataDirCheck {
|
|||
pub warning: Option<String>,
|
||||
}
|
||||
|
||||
/// Whether `dir` holds nothing worth worrying about: a folder that is missing,
|
||||
/// unreadable, or carrying only [`OS_CLUTTER`] and `ignored` names counts as empty
|
||||
pub async fn looks_empty(dir: &Path, ignored: &[OsString]) -> bool {
|
||||
let Ok(mut entries) = polyio::read_dir(dir).await else {
|
||||
return true;
|
||||
|
|
@ -67,7 +63,6 @@ pub async fn resolve(picked: &Path) -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
/// Where the game data sits when nothing is set: alongside the settings
|
||||
pub fn default_path() -> Result<PathBuf, String> {
|
||||
paths::config_dir()
|
||||
.map(Path::to_path_buf)
|
||||
|
|
@ -83,7 +78,6 @@ pub async fn check(picked: &Path) -> Result<DataDirCheck, String> {
|
|||
check_exact(&resolve(picked).await).await
|
||||
}
|
||||
|
||||
/// Checks the folder handed over rather than one derived from it
|
||||
pub async fn check_exact(path: &Path) -> Result<DataDirCheck, String> {
|
||||
let path = path.to_path_buf();
|
||||
|
||||
|
|
|
|||
|
|
@ -167,8 +167,12 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn natives_that_refuse_to_link_are_not_usable() {
|
||||
assert!(!has_usable_awt(&awt("true", "true", "false")));
|
||||
fn a_link_probe_that_fails_does_not_rule_the_image_out() {
|
||||
assert!(has_usable_awt(&awt("true", "true", "false")));
|
||||
assert!(has_usable_awt(&info(&[
|
||||
("java.awt", "true"),
|
||||
("java.awt.natives", "true"),
|
||||
])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue