mirror of
https://github.com/modrinth/code.git
synced 2026-09-03 05:25:58 +00:00
fix: ux changes for sync settings/overrides
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
UPDATE sync_feature_settings
|
||||
SET globally_enabled = 0, new_instance_default = 1
|
||||
WHERE feature IN (
|
||||
'command_history',
|
||||
'multiplayer_servers',
|
||||
'creative_hotbars'
|
||||
);
|
||||
|
||||
UPDATE instance_sync_preferences
|
||||
SET enabled = 1
|
||||
WHERE feature IN (
|
||||
'command_history',
|
||||
'multiplayer_servers',
|
||||
'creative_hotbars'
|
||||
);
|
||||
@@ -67,9 +67,14 @@ pub async fn list_screenshots(
|
||||
|
||||
pub async fn list_synced_screenshots() -> crate::Result<Vec<InstanceScreenshot>>
|
||||
{
|
||||
if !super::super::synced_options::get_global_options()
|
||||
.await?
|
||||
.screenshots
|
||||
{
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let state = State::get().await?;
|
||||
let sources =
|
||||
instance_rows::list_synced_screenshot_sources(&state.pool).await?;
|
||||
let sources = instance_rows::list_screenshot_sources(&state.pool).await?;
|
||||
list_source_screenshot_sets(&state, sources).await
|
||||
}
|
||||
|
||||
|
||||
@@ -288,47 +288,28 @@ async fn version_capability(
|
||||
pub async fn set_global_option(
|
||||
option: SyncedOption,
|
||||
enabled: bool,
|
||||
base_instance_id: Option<&str>,
|
||||
) -> crate::Result<GlobalSyncedOptions> {
|
||||
let state = State::get().await?;
|
||||
let _guard = state.lock_synced_options().await;
|
||||
let reset_participation =
|
||||
enabled && !canonical_exists(option, &state).await?;
|
||||
|
||||
let option_name = option.as_str();
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO sync_feature_settings
|
||||
(feature, globally_enabled, new_instance_default)
|
||||
VALUES (?, ?, 1)
|
||||
ON CONFLICT(feature) DO UPDATE SET
|
||||
globally_enabled = excluded.globally_enabled
|
||||
",
|
||||
option_name,
|
||||
enabled,
|
||||
)
|
||||
.execute(&state.pool)
|
||||
.await?;
|
||||
if enabled && option != SyncedOption::Screenshots {
|
||||
let base_instance_id = base_instance_id.ok_or_else(|| {
|
||||
ErrorKind::InputError(
|
||||
"Choose an instance to use as the sync source.".to_string(),
|
||||
)
|
||||
})?;
|
||||
return enable_global_option_from_base(
|
||||
option,
|
||||
base_instance_id,
|
||||
&state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
set_global_option_enabled(option, enabled, &state).await?;
|
||||
|
||||
let instances = crate::state::list_instances(&state.pool).await?;
|
||||
if reset_participation {
|
||||
for metadata in instances {
|
||||
if instance_option_enabled(&metadata, option) {
|
||||
instance_rows::set_instance_sync_preference(
|
||||
&metadata.instance.id,
|
||||
option,
|
||||
false,
|
||||
&state.pool,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
if !sync_files_are_protected(&metadata)
|
||||
&& !instance_is_running(&metadata, &state).await?
|
||||
{
|
||||
detach_option(&metadata, option, &state).await?;
|
||||
}
|
||||
}
|
||||
return get_global_options_with_state(&state).await;
|
||||
}
|
||||
for metadata in instances {
|
||||
if sync_files_are_protected(&metadata)
|
||||
|| instance_is_running(&metadata, &state).await?
|
||||
@@ -353,6 +334,112 @@ pub async fn set_global_option(
|
||||
get_global_options_with_state(&state).await
|
||||
}
|
||||
|
||||
async fn set_global_option_enabled(
|
||||
option: SyncedOption,
|
||||
enabled: bool,
|
||||
state: &State,
|
||||
) -> crate::Result<()> {
|
||||
let option_name = option.as_str();
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO sync_feature_settings
|
||||
(feature, globally_enabled, new_instance_default)
|
||||
VALUES (?, ?, 1)
|
||||
ON CONFLICT(feature) DO UPDATE SET
|
||||
globally_enabled = excluded.globally_enabled
|
||||
",
|
||||
option_name,
|
||||
enabled,
|
||||
)
|
||||
.execute(&state.pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn enable_global_option_from_base(
|
||||
option: SyncedOption,
|
||||
base_instance_id: &str,
|
||||
state: &State,
|
||||
) -> crate::Result<GlobalSyncedOptions> {
|
||||
let source = crate::state::get_instance(base_instance_id, &state.pool)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
ErrorKind::InputError("Unknown sync source instance.".to_string())
|
||||
})?;
|
||||
if sync_files_are_protected(&source)
|
||||
|| instance_is_running(&source, state).await?
|
||||
{
|
||||
return Err(ErrorKind::InputError(
|
||||
"Close the source instance before using it for syncing."
|
||||
.to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
match capability_status(&source, option, true, state).await {
|
||||
CapabilityStatus::Supported => {}
|
||||
CapabilityStatus::Unsupported(reason)
|
||||
| CapabilityStatus::Indeterminate(reason) => {
|
||||
return Err(ErrorKind::InputError(reason).into());
|
||||
}
|
||||
}
|
||||
|
||||
let instances = crate::state::list_instances(&state.pool).await?;
|
||||
for metadata in &instances {
|
||||
if instance_option_enabled(metadata, option)
|
||||
&& (sync_files_are_protected(metadata)
|
||||
|| instance_is_running(metadata, state).await?)
|
||||
{
|
||||
return Err(ErrorKind::InputError(
|
||||
"Close all instances using this synced setting before choosing a new sync source."
|
||||
.to_string(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
for metadata in &instances {
|
||||
if instance_option_enabled(metadata, option) {
|
||||
detach_option(metadata, option, state).await?;
|
||||
}
|
||||
}
|
||||
if !instance_option_enabled(&source, option) {
|
||||
detach_option(&source, option, state).await?;
|
||||
}
|
||||
|
||||
seed_from_instance(&source, option, state).await?;
|
||||
instance_rows::set_instance_sync_preference(
|
||||
base_instance_id,
|
||||
option,
|
||||
true,
|
||||
&state.pool,
|
||||
)
|
||||
.await?;
|
||||
set_global_option_enabled(option, true, state).await?;
|
||||
|
||||
for metadata in crate::state::list_instances(&state.pool).await? {
|
||||
if sync_files_are_protected(&metadata)
|
||||
|| instance_is_running(&metadata, state).await?
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if !instance_option_enabled(&metadata, option) {
|
||||
detach_option(&metadata, option, state).await?;
|
||||
continue;
|
||||
}
|
||||
match capability_status(&metadata, option, true, state).await {
|
||||
CapabilityStatus::Supported => {
|
||||
ensure_option(&metadata, option, state).await?
|
||||
}
|
||||
CapabilityStatus::Unsupported(_) => {
|
||||
detach_option(&metadata, option, state).await?
|
||||
}
|
||||
CapabilityStatus::Indeterminate(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
get_global_options_with_state(state).await
|
||||
}
|
||||
|
||||
pub async fn set_instance_option(
|
||||
instance_id: &str,
|
||||
option: SyncedOption,
|
||||
|
||||
@@ -172,7 +172,48 @@ pub(in crate::api::instance) async fn detach_servers(
|
||||
) -> crate::Result<()> {
|
||||
let generated = generated_path(state, &metadata.instance.id);
|
||||
let local = instance_dir(metadata, state).join(SERVERS_FILE);
|
||||
detach_link(&generated, &local).await
|
||||
let Some(current_checkpoint) = checkpoint(
|
||||
&metadata.instance.id,
|
||||
SyncedOption::MultiplayerServers,
|
||||
"default",
|
||||
state,
|
||||
)
|
||||
.await?
|
||||
else {
|
||||
return detach_link(&generated, &local).await;
|
||||
};
|
||||
let linked_to_generated = tokio::fs::symlink_metadata(&local)
|
||||
.await
|
||||
.is_ok_and(|metadata| metadata.file_type().is_symlink())
|
||||
&& tokio::fs::read_link(&local)
|
||||
.await
|
||||
.is_ok_and(|target| target == generated);
|
||||
let matches_checkpoint = local.exists()
|
||||
&& sha1_file(&local).await? == current_checkpoint.expected_sha1;
|
||||
if current_checkpoint.status != CheckpointStatus::Ready
|
||||
|| (!linked_to_generated && !matches_checkpoint)
|
||||
{
|
||||
return detach_link(&generated, &local).await;
|
||||
}
|
||||
|
||||
let current = read_servers(&local).await?;
|
||||
let projections =
|
||||
load_projection_entries(&metadata.instance.id, state).await?;
|
||||
let projection_matches = match_projection_entries(¤t, &projections);
|
||||
let instance_servers = current
|
||||
.into_iter()
|
||||
.zip(projection_matches)
|
||||
.filter_map(|(server, projection)| {
|
||||
projection
|
||||
.is_none_or(|projection| {
|
||||
projection.owner == ProjectionOwner::Instance
|
||||
})
|
||||
.then_some(server)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
detach_link(&generated, &local).await?;
|
||||
write_servers(&local, &instance_servers).await
|
||||
}
|
||||
|
||||
pub(in crate::api::instance) async fn reconcile_servers(
|
||||
|
||||
@@ -494,32 +494,6 @@ pub(crate) async fn get_instance_screenshot_source(
|
||||
Ok(source)
|
||||
}
|
||||
|
||||
pub(crate) async fn list_synced_screenshot_sources(
|
||||
pool: &SqlitePool,
|
||||
) -> crate::Result<Vec<InstanceScreenshotSource>> {
|
||||
let sources = sqlx::query_as!(
|
||||
InstanceScreenshotSource,
|
||||
"
|
||||
SELECT instances.id, instances.name, instances.path
|
||||
FROM instances
|
||||
INNER JOIN instance_sync_preferences preferences
|
||||
ON preferences.instance_id = instances.id
|
||||
WHERE preferences.feature = 'screenshots'
|
||||
AND preferences.enabled = 1
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM sync_feature_settings
|
||||
WHERE feature = 'screenshots' AND globally_enabled = 1
|
||||
)
|
||||
ORDER BY instances.name, instances.id
|
||||
",
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(sources)
|
||||
}
|
||||
|
||||
pub(crate) async fn list_screenshot_sources(
|
||||
pool: &SqlitePool,
|
||||
) -> crate::Result<Vec<InstanceScreenshotSource>> {
|
||||
|
||||
Reference in New Issue
Block a user