Merge branch 'identity-sync' into 23-release

This commit is contained in:
crschnick
2026-04-23 15:38:42 +00:00
parent 4616584e52
commit 101d182ef0
24 changed files with 209 additions and 106 deletions
@@ -5,6 +5,7 @@ import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppImages;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreEntryComp;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.hub.comp.StoreSection;
@@ -160,7 +161,7 @@ public interface DataStoreProvider {
return null;
}
default GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
default GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
return null;
}
@@ -72,7 +72,7 @@ public class StoreCreationComp extends ModalOverlayContentComp {
var activeDialog = new SimpleObjectProperty<GuiDialog>();
model.getProvider().subscribe(n -> {
if (n != null) {
var d = n.guiDialog(model.getExistingEntry(), model.getStore());
var d = n.guiDialog(model, model.getStore());
activeDialog.set(d);
if (d == null) {
return;
@@ -31,11 +31,11 @@ public class StoreCreationDialog {
}
public static StoreCreationModel showEdit(DataStoreEntry e, Consumer<DataStoreEntry> c) {
return showEdit(e, e.getStore(), true, c);
return showEdit(e, e.getStore(), true, true, c);
}
public static StoreCreationModel showEdit(
DataStoreEntry e, DataStore base, boolean addToStorage, Consumer<DataStoreEntry> c) {
DataStoreEntry e, DataStore base, boolean addToStorage, boolean switchCategory, Consumer<DataStoreEntry> c) {
StoreCreationConsumer consumer = (newE, validated) -> {
ThreadHelper.runAsync(() -> {
if (!addToStorage) {
@@ -66,14 +66,13 @@ public class StoreCreationDialog {
}
}
// Select new category if needed
var cat = DataStorage.get()
.getStoreCategoryIfPresent(e.getCategoryUuid())
.orElseThrow();
PlatformThread.runLaterIfNeeded(() -> {
StoreViewState.get()
.selectCategoryIntoViewIfNeeded(StoreViewState.get().getCategoryWrapper(cat));
});
if (switchCategory) {
// Select new category if needed
var cat = DataStorage.get().getStoreCategoryIfPresent(e.getCategoryUuid()).orElseThrow();
PlatformThread.runLaterIfNeeded(() -> {
StoreViewState.get().selectCategoryIntoViewIfNeeded(StoreViewState.get().getCategoryWrapper(cat));
});
}
c.accept(e);
});
@@ -46,6 +46,7 @@ public class StoreCreationModel {
DataStoreEntry existingEntry;
boolean staticDisplay;
StoreCreationConsumer consumer;
ObservableBooleanValue syncable;
public StoreCreationModel(
Property<DataStoreProvider> provider,
@@ -67,7 +68,7 @@ public class StoreCreationModel {
store.unbind();
store.setValue(null);
if (n != null) {
store.setValue(n.defaultStore(getTargetCategory(existingEntry)));
store.setValue(n.defaultStore(getTargetCategory(existingEntry != null ? existingEntry.getCategoryUuid() : null)));
}
});
@@ -82,6 +83,7 @@ public class StoreCreationModel {
newValue.validate();
});
});
this.entry = Bindings.createObjectBinding(
() -> {
if (name.getValue() == null
@@ -98,13 +100,21 @@ public class StoreCreationModel {
var entryRef = existingEntry != null
? existingEntry
: DataStorage.get().getDefaultDisplayParent(initial).orElse(initial);
var targetCategory = getTargetCategory(entryRef);
var targetCategory = getTargetCategory(entryRef.getCategoryUuid());
return DataStoreEntry.createNew(
UUID.randomUUID(), targetCategory.getUuid(), name.getValue(), store.getValue());
},
name,
store);
this.syncable = Bindings.createBooleanBinding(() -> {
var targetCategory = getTargetCategory(existingEntry != null ? existingEntry.getCategoryUuid() :
StoreViewState.get().getActiveCategory().getValue().getCategory().getUuid());
var entry = DataStoreEntry.createNew(
UUID.randomUUID(), targetCategory.getUuid(), "Temp", store.getValue());
return DataStorage.get().shouldSync(entry);
}, store, StoreViewState.get().getActiveCategory());
skippable.bind(Bindings.createBooleanBinding(
() -> {
if (entry.getValue() != null
@@ -124,9 +134,9 @@ public class StoreCreationModel {
name));
}
private DataStoreCategory getTargetCategory(DataStoreEntry base) {
var targetCategory = base != null
? base.getCategoryUuid()
private DataStoreCategory getTargetCategory(UUID baseCategory) {
var targetCategory = baseCategory != null
? baseCategory
: DataStorage.get().getSelectedCategory().getUuid();
var rootCategory = DataStorage.get()
.getRootCategory(DataStorage.get()
@@ -81,7 +81,7 @@ public class StoreQuickConnect {
return true;
}
StoreCreationDialog.showEdit(quickConnectEntry, newStore, false, finished -> {
StoreCreationDialog.showEdit(quickConnectEntry, newStore, false, true, finished -> {
update(finished.getStore());
ThreadHelper.runAsync(() -> {
try {
@@ -3,10 +3,7 @@ package io.xpipe.ext.base.desktop;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.comp.BaseRegionBuilder;
import io.xpipe.app.ext.*;
import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.hub.comp.SystemStateComp;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
@@ -63,7 +60,7 @@ public class DesktopApplicationStoreProvider implements DataStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
DesktopApplicationStore st = (DesktopApplicationStore) store.getValue();
var host = new SimpleObjectProperty<>(st.getDesktop());
var path = new SimpleStringProperty(st.getPath());
@@ -71,8 +68,7 @@ public class DesktopApplicationStoreProvider implements DataStoreProvider {
return new OptionsBuilder()
.nameAndDescription("desktopBase")
.addComp(
new StoreChoiceComp<>(
entry,
new StoreChoiceComp<>(model.getExistingEntry(),
host,
DesktopBaseStore.class,
desktopStoreDataStoreEntryRef ->
@@ -6,7 +6,6 @@ import io.xpipe.app.ext.*;
import io.xpipe.app.hub.comp.*;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.DocumentationLink;
import javafx.beans.binding.Bindings;
@@ -69,7 +68,7 @@ public class AbstractHostStoreProvider implements DataStoreProvider {
@SneakyThrows
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
AbstractHostStore st = store.getValue().asNeeded();
var host = new SimpleObjectProperty<>(st.getHost());
@@ -81,8 +80,7 @@ public class AbstractHostStoreProvider implements DataStoreProvider {
.nonNull()
.nameAndDescription("abstractHostGateway")
.addComp(
new StoreChoiceComp<>(
entry,
new StoreChoiceComp<>(model.getExistingEntry(),
gateway,
NetworkTunnelStore.class,
null,
@@ -3,7 +3,10 @@ package io.xpipe.ext.base.identity;
import io.xpipe.app.comp.RegionBuilder;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.base.InputGroupComp;
import io.xpipe.app.comp.base.ModalButton;
import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.cred.SshIdentityStrategy;
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
import io.xpipe.app.ext.ProcessControlProvider;
@@ -11,16 +14,20 @@ import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.platform.LabelGraphic;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
import io.xpipe.app.platform.PlatformThread;
import io.xpipe.app.secret.EncryptedValue;
import io.xpipe.app.secret.SecretRetrievalStrategy;
import io.xpipe.app.secret.SecretStrategyChoiceConfig;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageUserHandler;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.*;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue;
import lombok.AllArgsConstructor;
@@ -35,6 +42,7 @@ import java.util.List;
public class IdentityChoiceBuilder {
ObjectProperty<IdentityValue> identity;
ObservableBooleanValue syncedBase;
boolean allowCustomUserInput;
boolean requireUserInput;
boolean requirePassword;
@@ -45,7 +53,7 @@ public class IdentityChoiceBuilder {
ObservableValue<DataStoreEntryRef<ShellStore>> fileSystem;
public IdentityChoiceBuilder(
ObjectProperty<IdentityValue> identity,
ObjectProperty<IdentityValue> identity, ObservableBooleanValue syncedBase,
boolean allowCustomUserInput,
boolean requireUserInput,
boolean requirePassword,
@@ -53,6 +61,7 @@ public class IdentityChoiceBuilder {
boolean requireKeyInput,
String userChoiceTranslationKey,
String passwordChoiceTranslationKey) {
this.syncedBase = syncedBase;
this.identity = identity;
this.allowCustomUserInput = allowCustomUserInput;
this.requireUserInput = requireUserInput;
@@ -62,17 +71,46 @@ public class IdentityChoiceBuilder {
this.userChoiceTranslationKey = userChoiceTranslationKey;
this.passwordChoiceTranslationKey = new ReadOnlyStringWrapper(passwordChoiceTranslationKey);
this.fileSystem = new ReadOnlyObjectWrapper<>(DataStorage.get().local().ref());
addSyncCheckListener();
}
public static OptionsBuilder ssh(ObjectProperty<IdentityValue> identity, boolean requireUser) {
var i = new IdentityChoiceBuilder(
identity, true, requireUser, true, true, true, "identityChoice", "passwordAuthentication");
private void addSyncCheckListener() {
identity.addListener((observable, oldValue, newValue) -> {
if (DataStorage.get().supportsSync() && syncedBase.getValue() && newValue instanceof IdentityValue.Ref r && r.unwrap() instanceof LocalIdentityStore) {
var modal = ModalOverlay.of("unsyncedIdentityTitle", AppDialog.dialogTextKey("unsyncedIdentityContent").prefWidth(600));
modal.addButton(new ModalButton("documentation", () -> {
DocumentationLink.IDENTITIES.open();
}, false, false));
modal.addButtonBarComp(RegionBuilder.hspacer());
modal.addButton(new ModalButton("syncIdentity", () -> {
IdentityConvert.syncLocal(r.getRef().asNeeded(), false, updated -> {
Platform.runLater(() -> {
identity.set(null);
identity.set(IdentityValue.Ref.builder().ref(updated.asNeeded()).build());
});
});
}, true, false));
modal.addButton(new ModalButton("convertToMulti", () -> {
IdentityConvert.createMulti(r, true, created -> {
Platform.runLater(() -> {
identity.set(IdentityValue.Ref.builder().ref(created.asNeeded()).build());
});
});
}, true, false));
modal.addButton(new ModalButton("ignore", null, true, false));
modal.show();
}
});
}
public static OptionsBuilder ssh(ObjectProperty<IdentityValue> identity, ObservableBooleanValue syncedBase, boolean requireUser) {
var i = new IdentityChoiceBuilder(identity, syncedBase, true, requireUser, true, true, true, "identityChoice", "passwordAuthentication");
return i.build();
}
public static OptionsBuilder container(ObjectProperty<IdentityValue> identity) {
var i = new IdentityChoiceBuilder(
identity, true, false, false, false, false, "customUsername", "customUsernamePassword");
public static OptionsBuilder container(ObjectProperty<IdentityValue> identity, ObservableBooleanValue syncedBase) {
var i = new IdentityChoiceBuilder(identity, syncedBase, true, false, false, false, false, "customUsername", "customUsernamePassword");
return i.build();
}
@@ -98,23 +136,27 @@ public class IdentityChoiceBuilder {
}
public OptionsBuilder build() {
var existing = identity.getValue();
var user = new SimpleStringProperty(
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
? inPlace.unwrap().getUsername().get()
: null);
var pass = new SimpleObjectProperty<>(
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
? inPlace.unwrap().getPassword()
: null);
var identityStrategy = new SimpleObjectProperty<>(
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
? inPlace.unwrap().getSshIdentity()
: null);
var ref = new SimpleObjectProperty<>(existing instanceof IdentityValue.Ref r ? r.getRef() : null);
var user = new SimpleStringProperty();
var pass = new SimpleObjectProperty<SecretRetrievalStrategy>();
var identityStrategy = new SimpleObjectProperty<SshIdentityStrategy>();
var ref = new SimpleObjectProperty<DataStoreEntryRef<IdentityStore>>();
var inPlaceSelected = ref.isNull();
var refSelected = ref.isNotNull();
identity.subscribe(existing -> {
user.set(existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
? inPlace.unwrap().getUsername().get()
: null);
pass.set(existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
? inPlace.unwrap().getPassword()
: null);
identityStrategy.set(
existing instanceof IdentityValue.InPlace inPlace && inPlace.unwrap() != null
? inPlace.unwrap().getSshIdentity()
: null);
ref.set(existing instanceof IdentityValue.Ref r ? r.getRef() : null);
});
var passwordChoice = OptionsChoiceBuilder.builder()
.allowNull(false)
.property(pass)
@@ -0,0 +1,69 @@
package io.xpipe.ext.base.identity;
import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.app.ext.DataStoreCreationCategory;
import io.xpipe.app.hub.comp.StoreCreationDialog;
import io.xpipe.app.secret.EncryptedValue;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntryRef;
import javafx.application.Platform;
import javafx.scene.control.Button;
import java.util.List;
import java.util.function.Consumer;
public class IdentityConvert {
public static void syncLocal(DataStoreEntryRef<LocalIdentityStore> ref, boolean selectCategory, Consumer<DataStoreEntryRef<SyncedIdentityStore>> consumer) {
var st = ref.getStore();
var synced = SyncedIdentityStore.builder()
.username(st.getUsername().get())
.password(EncryptedValue.VaultKey.of(st.getPassword()))
.sshIdentity(EncryptedValue.VaultKey.of(st.getSshIdentity()))
.perUser(false)
.build();
StoreCreationDialog.showEdit(ref.get(), synced, true, selectCategory, updated -> {
if (updated.getStore() instanceof SyncedIdentityStore) {
consumer.accept(updated.ref());
}
});
// Ugly solution to sync key file if needed
Platform.runLater(() -> {
var found = AppMainWindow.get().getStage().getScene().getRoot().lookupAll(".git-sync-file-button");
if (found.size() != 1) {
return;
}
var first = found.iterator().next();
if (first instanceof Button b) {
b.fire();
}
});
}
public static void createMulti(IdentityValue val, boolean moveToSync, Consumer<DataStoreEntryRef<MultiIdentityStore>> consumer) {
if (!(val instanceof IdentityValue.Ref ref)) {
throw new IllegalArgumentException("Not a identity reference");
}
var synced = MultiIdentityStore.builder()
.identities(List.of(ref.getRef().get().getUuid()))
.perUser(val.isPerUser())
.build();
StoreCreationDialog.showCreation(ref.getRef().get().getName() + "-multi", synced, DataStoreCreationCategory.IDENTITY, created -> {
if (created.getStore() instanceof MultiIdentityStore) {
if (moveToSync) {
var cat = DataStorage.get().getStoreCategoryIfPresent(created.getCategoryUuid()).orElseThrow();
var inSynced = DataStorage.get().getCategoryParentHierarchy(cat).stream().anyMatch(
dataStoreCategory -> dataStoreCategory.getUuid().equals(DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID));
var targetCategory = DataStorage.get().getStoreCategoryIfPresent(
inSynced ? created.getCategoryUuid() : DataStorage.SYNCED_IDENTITIES_CATEGORY_UUID).orElseThrow();
DataStorage.get().moveEntryToCategory(created, targetCategory);
}
consumer.accept(created.ref());
}
}, false);
}
}
@@ -200,16 +200,24 @@ public class IdentitySelectComp extends RegionBuilder<HBox> {
}
}
Runnable updateMap = () -> {
map.clear();
for (DataStoreEntry storeEntry : DataStorage.get().getStoreEntries()) {
if (storeEntry.getValidity().isUsable() && storeEntry.getStore() instanceof IdentityStore) {
map.put(formatName(storeEntry), storeEntry.ref());
}
}
};
StoreViewState.get().getAllEntries().getList().addListener((ListChangeListener<? super StoreEntryWrapper>)
c -> {
map.clear();
for (DataStoreEntry storeEntry : DataStorage.get().getStoreEntries()) {
if (storeEntry.getValidity().isUsable() && storeEntry.getStore() instanceof IdentityStore) {
map.put(formatName(storeEntry), storeEntry.ref());
}
}
updateMap.run();
});
selectedReference.addListener((observable, oldValue, newValue) -> {
updateMap.run();
});
var prop = new SimpleStringProperty();
if (inPlaceUser.getValue() != null) {
prop.setValue(inPlaceUser.getValue());
@@ -67,27 +67,7 @@ public class LocalIdentityConvertHubLeafProvider implements HubLeafProvider<Loca
@Override
public void executeImpl() {
var st = ref.getStore();
var synced = SyncedIdentityStore.builder()
.username(st.getUsername().get())
.password(EncryptedValue.VaultKey.of(st.getPassword()))
.sshIdentity(EncryptedValue.VaultKey.of(st.getSshIdentity()))
.perUser(false)
.build();
StoreCreationDialog.showEdit(ref.get(), synced, true, ignored -> {});
// Ugly solution to sync key file if needed
Platform.runLater(() -> {
var found = AppMainWindow.get().getStage().getScene().getRoot().lookupAll(".git-sync-file-button");
if (found.size() != 1) {
return;
}
var first = found.iterator().next();
if (first instanceof Button b) {
b.fire();
}
});
IdentityConvert.syncLocal(ref, true, ignored -> {});
}
}
}
@@ -4,6 +4,7 @@ import io.xpipe.app.cred.NoIdentityStrategy;
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
import io.xpipe.app.secret.EncryptedValue;
@@ -32,7 +33,7 @@ public class LocalIdentityStoreProvider extends IdentityStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
LocalIdentityStore st = (LocalIdentityStore) store.getValue();
var user = new SimpleStringProperty(st.getUsername().get());
@@ -1,9 +1,9 @@
package io.xpipe.ext.base.identity;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppInstallation;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreListChoiceComp;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.platform.OptionsBuilder;
@@ -15,14 +15,13 @@ import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.UUID;
public class MultiIdentityStoreProvider extends IdentityStoreProvider {
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
MultiIdentityStore st = (MultiIdentityStore) store.getValue();
var initialAvailableIdentities = st.getAvailableIdentities();
@@ -5,6 +5,7 @@ import io.xpipe.app.cred.PasswordManagerAgentStrategy;
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
@@ -13,7 +14,6 @@ import io.xpipe.app.prefs.PasswordManagerTestComp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageUserHandler;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
@@ -43,7 +43,7 @@ public class PasswordManagerIdentityStoreProvider extends IdentityStoreProvider
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
PasswordManagerIdentityStore st = (PasswordManagerIdentityStore) store.getValue();
var key = new SimpleStringProperty(st.getKey());
@@ -6,6 +6,7 @@ import io.xpipe.app.cred.NoIdentityStrategy;
import io.xpipe.app.cred.SshIdentityStrategyChoiceConfig;
import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreEntryWrapper;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
@@ -42,7 +43,7 @@ public class SyncedIdentityStoreProvider extends IdentityStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
SyncedIdentityStore st = (SyncedIdentityStore) store.getValue();
var user = new SimpleStringProperty(st.getUsername().get());
@@ -8,7 +8,6 @@ import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.platform.OptionsChoiceBuilder;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.DocumentationLink;
import io.xpipe.app.util.StoreStateFormat;
@@ -56,7 +55,7 @@ public class ScriptCollectionSourceStoreProvider implements DataStoreProvider {
@SneakyThrows
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
ScriptCollectionSourceStore st = store.getValue().asNeeded();
var source = new SimpleObjectProperty<>(st.getSource());
@@ -83,7 +83,7 @@ public class ScriptStoreProvider implements DataStoreProvider {
@SneakyThrows
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
ScriptStore st = store.getValue().asNeeded();
var textSource = new SimpleObjectProperty<>(
@@ -155,7 +155,7 @@ public class ScriptStoreProvider implements DataStoreProvider {
new StoreListChoiceComp<>(
others,
ScriptStore.class,
scriptStore -> !scriptStore.get().equals(entry) && !others.contains(scriptStore),
scriptStore -> !scriptStore.get().equals(model.getExistingEntry()) && !others.contains(scriptStore),
StoreViewState.get().getAllScriptsCategory()),
others)
.bind(
@@ -3,6 +3,7 @@ package io.xpipe.ext.base.service;
import io.xpipe.app.ext.*;
import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreComboChoiceComp;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.platform.BindingsHelper;
import io.xpipe.app.platform.OptionsBuilder;
@@ -53,7 +54,7 @@ public class CustomServiceStoreProvider extends AbstractServiceStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
CustomServiceStore st = store.getValue().asNeeded();
var comboHost = new SimpleObjectProperty<>(StoreComboChoiceComp.ComboValue.of(st.getAddress(), st.getHost()));
@@ -91,15 +92,13 @@ public class CustomServiceStoreProvider extends AbstractServiceStoreProvider {
hostStore -> {
HostAddress addr = hostStore.getHostAddress();
return addr != null && !addr.isEmpty() ? addr.get() : null;
},
entry,
}, model.getExistingEntry(),
comboHost,
HostAddressStore.class,
n -> true,
StoreViewState.get().getAllConnectionsCategory(),
false);
var gatewayChoice = new StoreChoiceComp<>(
entry,
var gatewayChoice = new StoreChoiceComp<>(model.getExistingEntry(),
gateway,
NetworkTunnelStore.class,
ref -> !ref.get().equals(DataStorage.get().local()),
@@ -4,6 +4,7 @@ import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.ext.LocalStore;
import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStorage;
@@ -35,7 +36,7 @@ public class FixedServiceStoreProvider extends AbstractServiceStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
FixedServiceStore st = store.getValue().asNeeded();
var host = new ReadOnlyObjectWrapper<>(st.getHost());
var localPort = new SimpleObjectProperty<>(st.getLocalPort());
@@ -60,8 +61,7 @@ public class FixedServiceStoreProvider extends AbstractServiceStoreProvider {
var q = new OptionsBuilder()
.nameAndDescription("serviceHost")
.addComp(
new StoreChoiceComp<>(
entry,
new StoreChoiceComp<>(model.getExistingEntry(),
host,
DataStore.class,
null,
@@ -4,6 +4,7 @@ import io.xpipe.app.ext.DataStore;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.ext.LocalStore;
import io.xpipe.app.hub.comp.StoreChoiceComp;
import io.xpipe.app.hub.comp.StoreCreationModel;
import io.xpipe.app.hub.comp.StoreViewState;
import io.xpipe.app.platform.OptionsBuilder;
import io.xpipe.app.storage.DataStorage;
@@ -36,7 +37,7 @@ public class MappedServiceStoreProvider extends FixedServiceStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
MappedServiceStore st = store.getValue().asNeeded();
var host = new SimpleObjectProperty<>(st.getHost());
@@ -62,8 +63,7 @@ public class MappedServiceStoreProvider extends FixedServiceStoreProvider {
var q = new OptionsBuilder()
.nameAndDescription("serviceHost")
.addComp(
new StoreChoiceComp<>(
entry,
new StoreChoiceComp<>(model.getExistingEntry(),
host,
DataStore.class,
null,
@@ -71,7 +71,7 @@ public class IncusContainerStoreProvider implements ShellStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
IncusContainerStore st = (IncusContainerStore) store.getValue();
var identity = new SimpleObjectProperty<>(st.getIdentity());
@@ -79,7 +79,7 @@ public class IncusContainerStoreProvider implements ShellStoreProvider {
.name("container")
.description("containerDescription")
.addStaticString((st.getProjectName() != null ? st.getProjectName() + "/" : "") + st.getContainerName())
.sub(IdentityChoiceBuilder.container(identity), identity)
.sub(IdentityChoiceBuilder.container(identity, model.getSyncable()), identity)
.bind(
() -> {
return IncusContainerStore.builder()
@@ -71,7 +71,7 @@ public class LxdContainerStoreProvider implements ShellStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
LxdContainerStore st = (LxdContainerStore) store.getValue();
var identity = new SimpleObjectProperty<>(st.getIdentity());
@@ -79,7 +79,7 @@ public class LxdContainerStoreProvider implements ShellStoreProvider {
.name("container")
.description("containerDescription")
.addStaticString((st.getProjectName() != null ? st.getProjectName() + "/" : "") + st.getContainerName())
.sub(IdentityChoiceBuilder.container(identity), identity)
.sub(IdentityChoiceBuilder.container(identity, model.getSyncable()), identity)
.bind(
() -> {
return LxdContainerStore.builder()
@@ -55,14 +55,13 @@ public class PodmanContainerStoreProvider implements ShellStoreProvider {
}
@Override
public GuiDialog guiDialog(DataStoreEntry entry, Property<DataStore> store) {
public GuiDialog guiDialog(StoreCreationModel model, Property<DataStore> store) {
PodmanContainerStore st = (PodmanContainerStore) store.getValue();
return new OptionsBuilder()
.name("host")
.description("podmanHostDescription")
.addComp(new StoreChoiceComp<>(
entry,
.addComp(new StoreChoiceComp<>(model.getExistingEntry(),
new ReadOnlyObjectWrapper<>(
st.getCmd() != null ? st.getCmd().getStore().getHost() : null),
ShellStore.class,
+2
View File
@@ -2108,3 +2108,5 @@ systemDefault=System default
hardwareSecurityKey=Hardware security key (PKCS#11)
macOsKeychain=macOS keychain
customLibrary=Custom library
unsyncedIdentityTitle=Unsynced identity
unsyncedIdentityContent=This connection is marked to sync across multiple systems, but the identity you selected is local-only. This will lead to a missing identity on other systems.\n\nYou can either convert this local identity to a synced identity, or convert it to a multi identity to switch between multiple local identities on each synced system.