Various small fixes [release]

This commit is contained in:
crschnick 2023-03-04 10:45:52 +00:00
parent 1b23c833ed
commit c936e37509
48 changed files with 285 additions and 315 deletions

View file

@ -22,7 +22,7 @@ final class BookmarkList extends SimpleComp {
@Override
protected Region createSimple() {
var list = DataStorage.get().getStores().stream().filter(entry -> entry.getStore() instanceof ShellStore).map(entry -> new Bookmark(entry)).toList();
var list = DataStorage.get().getStoreEntries().stream().filter(entry -> entry.getStore() instanceof ShellStore).map(entry -> new Bookmark(entry)).toList();
return new ListBoxViewComp<>(FXCollections.observableList(list), FXCollections.observableList(list), bookmark -> {
var imgView =
new PrettyImageComp(new SimpleStringProperty(bookmark.entry().getProvider().getDisplayIconFileName()), 16, 16).createRegion();

View file

@ -184,8 +184,7 @@ public class FileBrowserComp extends SimpleComp {
() -> {
return model.getStore().getValue() != null
? DataStorage.get()
.getEntryByStore(model.getStore().getValue())
.orElseThrow()
.getStoreEntry(model.getStore().getValue())
.getName()
: null;
},
@ -194,8 +193,7 @@ public class FileBrowserComp extends SimpleComp {
() -> {
return model.getStore().getValue() != null
? DataStorage.get()
.getEntryByStore(model.getStore().getValue())
.orElseThrow()
.getStoreEntry(model.getStore().getValue())
.getProvider()
.getDisplayIconFileName()
: null;

View file

@ -19,6 +19,7 @@ import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@ -55,7 +56,7 @@ final class OpenFileSystemModel {
public Optional<String> cd(String path) {
var newPath = FileSystemHelper.normalizeDirectoryPath(this, path);
if (!path.equals(newPath)) {
if (!Objects.equals(path, newPath)) {
return Optional.of(newPath);
}

View file

@ -1,6 +1,6 @@
package io.xpipe.app.comp.base;
import com.jfoenix.controls.JFXSpinner;
import atlantafx.base.controls.RingProgressIndicator;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure;
@ -25,13 +25,16 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
public CompStructure<StackPane> createBase() {
var compStruc = comp.createStructure();
JFXSpinner loading = new JFXSpinner();
loading.getStyleClass().add("spinner");
var loading = new RingProgressIndicator(0, false);
loading.setProgress(-1);
loading.setPrefWidth(50);
loading.setPrefHeight(50);
var loadingBg = new StackPane(loading);
loadingBg.getStyleClass().add("loading-comp");
loadingBg.setVisible(showLoading.getValue());
;
var listener = new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean busy) {
@ -64,7 +67,7 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
}
}
};
showLoading.addListener(listener);
PlatformThread.sync(showLoading).addListener(listener);
var stack = new StackPane(compStruc.get(), loadingBg);
return new SimpleCompStructure<>(stack);

View file

@ -82,7 +82,7 @@ public class GuiDsCreatorTransferStep extends MultiStepComp.Step<CompStructure<?
@Override
public void onBack() {
var e = entry.getValue();
DataStorage.get().deleteEntry(e);
DataStorage.get().deleteSourceEntry(e);
}
@Override
@ -93,7 +93,7 @@ public class GuiDsCreatorTransferStep extends MultiStepComp.Step<CompStructure<?
}
var e = entry.getValue();
DataStorage.get().deleteEntry(e);
DataStorage.get().deleteSourceEntry(e);
}
@Override

View file

@ -71,7 +71,7 @@ public class NamedSourceChoiceComp extends SimpleComp implements Validatable {
if (!filter.getValue().test(source)) {
return false;
}
var e = DataStorage.get().getEntryBySource(source).orElseThrow();
var e = DataStorage.get().getSourceEntry(source).orElseThrow();
return filterString.get() == null
|| e.getName().toLowerCase().contains(filterString.get().toLowerCase());
});
@ -109,7 +109,7 @@ public class NamedSourceChoiceComp extends SimpleComp implements Validatable {
var filterComp = new FilterComp(filterString).hide(Bindings.greaterThan(5, Bindings.size(shown)));
var view = new ListViewComp<>(shown, list, prop, (T s) -> {
var e = DataStorage.get().getEntryBySource(s).orElseThrow();
var e = DataStorage.get().getSourceEntry(s).orElseThrow();
var provider = e.getProvider();
var graphic = provider.getDisplayIconFileName();
var top = String.format("%s (%s)", e.getName(), provider.getDisplayName());

View file

@ -91,7 +91,7 @@ public class GuiDsStoreCreator extends MultiStepComp.Step<CompStructure<?>> {
show(e.getName(), e.getProvider(), e.getStore(), v -> true, newE -> {
ThreadHelper.runAsync(() -> {
e.applyChanges(newE);
if (!DataStorage.get().getStores().contains(e)) {
if (!DataStorage.get().getStoreEntries().contains(e)) {
DataStorage.get().addStoreEntry(e);
ScanAlert.showIfNeeded(e.getStore());
}

View file

@ -72,7 +72,7 @@ public class NamedStoreChoiceComp extends SimpleComp implements Validatable {
return false;
}
var e = DataStorage.get().getStore(store);
var e = DataStorage.get().getStoreEntry(store);
return filter.getValue().test(e);
};
},
@ -116,8 +116,8 @@ public class NamedStoreChoiceComp extends SimpleComp implements Validatable {
refreshShown(list, shown);
});
var prop = new SimpleObjectProperty<>(
DataStorage.get().getEntryByStore(selected.getValue()).orElse(null));
var prop = new SimpleObjectProperty<>(selected.getValue() != null ?
DataStorage.get().getStoreEntryIfPresent(selected.getValue()).orElse(null):null);
setUpListener(prop);
var filterComp = new FilterComp(filterString)

View file

@ -73,7 +73,7 @@ public class SourceCollectionWrapper implements StorageFilter.Filterable {
public void clean() {
var entries = List.copyOf(collection.getEntries());
entries.forEach(e -> DataStorage.get().deleteEntry(e));
entries.forEach(e -> DataStorage.get().deleteSourceEntry(e));
}
private void setupListeners() {

View file

@ -72,7 +72,7 @@ public class SourceEntryWrapper implements StorageFilter.Filterable {
return;
}
DataStorage.get().deleteEntry(entry);
DataStorage.get().deleteSourceEntry(entry);
}
private <T extends DataSource<?>> void update() {

View file

@ -35,7 +35,7 @@ public class StoreEntryListComp extends SimpleComp {
BindingsHelper.persist(
Bindings.not(Bindings.isEmpty(StoreViewState.get().getShownEntries()))));
map.put(new StoreStorageEmptyIntroComp(), StoreViewState.get().emptyProperty());
map.put(new StoreIntroComp(), StoreViewState.get().emptyProperty());
map.put(
new StoreNotFoundComp(),
BindingsHelper.persist(Bindings.and(

View file

@ -11,7 +11,6 @@ import io.xpipe.app.storage.DataStoreEntry;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue;
import lombok.Getter;
@ -31,7 +30,7 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
private final Property<DataStoreEntry.State> state = new SimpleObjectProperty<>();
private final StringProperty information = new SimpleStringProperty();
private final StringProperty summary = new SimpleStringProperty();
private final Map<ActionProvider, ObservableBooleanValue> actionProviders;
private final Map<ActionProvider, BooleanProperty> actionProviders;
private final ObservableValue<ActionProvider> defaultActionProvider;
private final BooleanProperty editable = new SimpleBooleanProperty();
private final BooleanProperty renamable = new SimpleBooleanProperty();
@ -53,33 +52,26 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
.isAssignableFrom(entry.getStore().getClass());
})
.forEach(dataStoreActionProvider -> {
var property = Bindings.createBooleanBinding(
() -> {
if (!entry.getState().isUsable()) {
return false;
}
return dataStoreActionProvider
.getDataStoreCallSite()
.isApplicable(entry.getStore().asNeeded());
},
disabledProperty(),
state,
lastAccess);
actionProviders.put(dataStoreActionProvider, property);
actionProviders.put(dataStoreActionProvider, new SimpleBooleanProperty(true));
});
this.defaultActionProvider = Bindings.createObjectBinding(() -> {
var found = actionProviders.entrySet().stream()
.filter(e -> e.getValue().get())
.filter(e -> e.getKey().getDataStoreCallSite() != null
&& e.getKey().getDataStoreCallSite().isDefault())
.findFirst();
return found.map(p -> p.getKey()).orElse(null);
}, actionProviders.values().toArray(Observable[]::new));
this.defaultActionProvider = Bindings.createObjectBinding(
() -> {
var found = actionProviders.entrySet().stream()
.filter(e -> e.getValue().get())
.filter(e -> e.getKey().getDataStoreCallSite() != null
&& e.getKey().getDataStoreCallSite().isDefault())
.findFirst();
return found.map(p -> p.getKey()).orElse(null);
},
actionProviders.values().toArray(Observable[]::new));
setupListeners();
update();
}
public boolean isInStorage() {
return DataStorage.get().getStoreEntries().contains(entry);
}
public void editDialog() {
GuiDsStoreCreator.showEdit(entry);
}
@ -130,6 +122,33 @@ public class StoreEntryWrapper implements StorageFilter.Filterable {
|| AppPrefs.get().developerDisableGuiRestrictions().getValue());
deletable.setValue(entry.getConfiguration().isDeletable()
|| AppPrefs.get().developerDisableGuiRestrictions().getValue());
actionProviders.keySet().forEach(dataStoreActionProvider -> {
if (!isInStorage()) {
actionProviders.get(dataStoreActionProvider).set(false);
return;
}
if (!entry.getState().isUsable()
&& !dataStoreActionProvider
.getDataStoreCallSite()
.activeType()
.equals(ActionProvider.DataStoreCallSite.ActiveType.ALWAYS_ENABLE)) {
actionProviders.get(dataStoreActionProvider).set(false);
return;
}
try {
actionProviders
.get(dataStoreActionProvider)
.set(dataStoreActionProvider
.getDataStoreCallSite()
.isApplicable(entry.getStore().asNeeded()));
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
actionProviders.get(dataStoreActionProvider).set(false);
}
});
}
@Override

View file

@ -4,8 +4,11 @@ import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.util.Hyperlinks;
import io.xpipe.app.util.ScanAlert;
import io.xpipe.core.impl.LocalStore;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
@ -14,7 +17,8 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.kordamp.ikonli.javafx.FontIcon;
public class StoreStorageEmptyIntroComp extends SimpleComp {
public class StoreIntroComp extends SimpleComp {
@Override
public Region createSimple() {
@ -22,29 +26,18 @@ public class StoreStorageEmptyIntroComp extends SimpleComp {
AppFont.setSize(title, 7);
title.getStyleClass().add("title-header");
var descFi = new FontIcon("mdi2i-information-outline");
var introDesc = new Label(AppI18n.get("storeIntroDescription"));
introDesc.heightProperty().addListener((c, o, n) -> {
descFi.iconSizeProperty().set(n.intValue());
});
var mfi = new FontIcon("mdi2h-home-plus-outline");
var mfi = new FontIcon("mdi2m-magnify");
var machine = new Label(AppI18n.get("storeMachineDescription"), mfi);
machine.heightProperty().addListener((c, o, n) -> {
mfi.iconSizeProperty().set(n.intValue());
});
var dfi = new FontIcon("mdi2d-database-plus-outline");
var database = new Label(AppI18n.get("storeDatabaseDescription"), dfi);
database.heightProperty().addListener((c, o, n) -> {
dfi.iconSizeProperty().set(n.intValue());
});
var fi = new FontIcon("mdi2c-card-plus-outline");
var stream = new Label(AppI18n.get("storeStreamDescription"), fi);
stream.heightProperty().addListener((c, o, n) -> {
fi.iconSizeProperty().set(n.intValue());
});
var scanButton = new Button(AppI18n.get("detectConnections"), new FontIcon("mdi2m-magnify"));
scanButton.setOnAction(event -> ScanAlert.showIfNeeded(new LocalStore()));
var scanPane = new StackPane(scanButton);
scanPane.setAlignment(Pos.CENTER);
var dofi = new FontIcon("mdi2b-book-open-variant");
var documentation = new Label(AppI18n.get("introDocumentation"), dofi);
@ -63,10 +56,7 @@ public class StoreStorageEmptyIntroComp extends SimpleComp {
introDesc,
new Separator(Orientation.HORIZONTAL),
machine,
new Separator(Orientation.HORIZONTAL),
database,
new Separator(Orientation.HORIZONTAL),
stream,
scanPane,
new Separator(Orientation.HORIZONTAL),
documentation,
docLinkPane);

View file

@ -54,7 +54,7 @@ public class StoreViewState {
}
private void addStorageGroupListeners() {
allEntries.setAll(FXCollections.observableArrayList(DataStorage.get().getStores().stream()
allEntries.setAll(FXCollections.observableArrayList(DataStorage.get().getStoreEntries().stream()
.map(StoreEntryWrapper::new)
.toList()));

View file

@ -46,7 +46,7 @@ public interface MessageExchangeImpl<RQ extends RequestMessage, RS extends Respo
default DataStore resolveStore(@NonNull DataStore in, boolean acceptDisabled) throws ClientException {
try {
if (in instanceof NamedStore n) {
var found = DataStorage.get().getStore(n.getName(), acceptDisabled);
var found = DataStorage.get().getStoreEntry(n.getName(), acceptDisabled);
return found.getStore();
}
} catch (IllegalArgumentException ex) {
@ -57,7 +57,7 @@ public interface MessageExchangeImpl<RQ extends RequestMessage, RS extends Respo
}
default DataStoreEntry getStoreEntryByName(@NonNull String name, boolean acceptDisabled) throws ClientException {
var store = DataStorage.get().getStoreIfPresent(name);
var store = DataStorage.get().getStoreEntryIfPresent(name);
if (store.isEmpty()) {
throw new ClientException("No store with name " + name + " was found");
}

View file

@ -14,7 +14,7 @@ public class ListStoresExchangeImpl extends ListStoresExchange
@Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
DataStorage s = DataStorage.get();
var e = s.getStores().stream()
var e = s.getStoreEntries().stream()
.filter(entry -> !entry.isDisabled() && entry.getProvider().shouldShow())
.sorted(Comparator.comparing(dataStoreEntry -> dataStoreEntry.getLastUsed()))
.map(col -> StoreListEntry.builder()

View file

@ -18,9 +18,9 @@ public class ReadDrainExchangeImpl extends ReadDrainExchange
@Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var ds = DataStorage.get().getStoreIfPresent(msg.getName());
var ds = DataStorage.get().getStoreEntryIfPresent(msg.getName());
if (ds.isEmpty()) {
ds = Optional.of(DataStorage.get().addStore(msg.getName(), msg.getStore()));
ds = Optional.of(DataStorage.get().addStoreEntry(msg.getName(), msg.getStore()));
}
if (!(ds.get().getStore() instanceof SinkDrainStore)) {

View file

@ -12,7 +12,7 @@ public class RemoveEntryExchangeImpl extends RemoveEntryExchange
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var e = getSourceEntry(msg.getRef(), null, true);
var id = DataStorage.get().getId(e);
DataStorage.get().deleteEntry(e);
DataStorage.get().deleteSourceEntry(e);
return Response.builder().id(id).build();
}
}

View file

@ -11,7 +11,7 @@ public class RemoveStoreExchangeImpl extends RemoveStoreExchange
@Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var s = DataStorage.get().getStore(msg.getStoreName(), true);
var s = DataStorage.get().getStoreEntry(msg.getStoreName(), true);
if (!s.getConfiguration().isDeletable()) {
throw new ClientException("Store is not deletable");
}

View file

@ -11,7 +11,7 @@ public class RenameEntryExchangeImpl extends RenameEntryExchange
@Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var e = getSourceEntry(msg.getRef(), null, true);
DataStorage.get().deleteEntry(e);
DataStorage.get().deleteSourceEntry(e);
DataStorage.get()
.add(
e,

View file

@ -10,8 +10,8 @@ public class RenameStoreExchangeImpl extends RenameStoreExchange
@Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var s = DataStorage.get().getStore(msg.getStoreName(), true);
DataStorage.get().renameStore(s, msg.getNewName());
var s = DataStorage.get().getStoreEntry(msg.getStoreName(), true);
DataStorage.get().renameStoreEntry(s, msg.getNewName());
return Response.builder().build();
}
}

View file

@ -51,7 +51,7 @@ public class StoreAddExchangeImpl extends StoreAddExchange
return;
}
DataStorage.get().addStore(name.getValue(), store);
DataStorage.get().addStoreEntry(name.getValue(), store);
});
return StoreAddExchange.Response.builder().config(config).build();
@ -81,7 +81,7 @@ public class StoreAddExchangeImpl extends StoreAddExchange
var nameQ = Dialog.retryIf(
Dialog.query("Store name", true, true, false, name.getValue(), QueryConverter.STRING),
(String r) -> {
return DataStorage.get().getStoreIfPresent(r).isPresent()
return DataStorage.get().getStoreEntryIfPresent(r).isPresent()
? "Store with name " + r + " already exists"
: null;
})

View file

@ -2,6 +2,7 @@ package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.CustomComboBoxBuilder;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.impl.FileStore;
@ -23,7 +24,7 @@ public class FileSystemStoreChoiceComp extends SimpleComp {
}
private static String getName(FileSystemStore store) {
var name = XPipeDaemon.getInstance().getNamedStores().stream()
var name = DataStorage.get().getUsableStores().stream()
.filter(e -> e.equals(store))
.findAny()
.map(e -> XPipeDaemon.getInstance().getStoreName(e).orElse("?"))
@ -61,7 +62,7 @@ public class FileSystemStoreChoiceComp extends SimpleComp {
var comboBox =
new CustomComboBoxBuilder<FileSystemStore>(fileSystemProperty, this::createGraphic, null, v -> true);
comboBox.setSelectedDisplay(this::createDisplayGraphic);
XPipeDaemon.getInstance().getNamedStores().stream()
DataStorage.get().getUsableStores().stream()
.filter(e -> e instanceof FileSystemStore)
.map(e -> (FileSystemStore) e)
.forEach(comboBox::add);

View file

@ -3,6 +3,7 @@ package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.CustomComboBoxBuilder;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.store.ShellStore;
@ -52,7 +53,7 @@ public class ShellStoreChoiceComp<T extends ShellStore> extends SimpleComp {
var imgView =
new PrettyImageComp(new SimpleStringProperty(provider.getDisplayIconFileName()), 16, 16).createRegion();
var name = XPipeDaemon.getInstance().getNamedStores().stream()
var name = DataStorage.get().getUsableStores().stream()
.filter(e -> e.equals(s))
.findAny()
.flatMap(store -> {
@ -74,7 +75,7 @@ public class ShellStoreChoiceComp<T extends ShellStore> extends SimpleComp {
new CustomComboBoxBuilder<T>(selected, this::createGraphic, new Label(AppI18n.get("none")), n -> true);
comboBox.setUnknownNode(t -> createGraphic(t));
var available = XPipeDaemon.getInstance().getNamedStores().stream()
var available = DataStorage.get().getUsableStores().stream()
.filter(s -> s != self)
.filter(s -> storeClass.isAssignableFrom(s.getClass()) && applicableCheck.test((T) s))
.map(s -> (ShellStore) s)

View file

@ -66,7 +66,7 @@ public class DataSourceCollection extends StorageElement {
JavaType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, UUID.class);
var entries = new LinkedHashMap<UUID, DataSourceEntry>();
for (var u : mapper.<List<UUID>>readValue(dir.resolve("entries.json").toFile(), listType)) {
var v = storage.getSourceEntryByUuid(u).orElse(null);
var v = storage.getSourceEntry(u).orElse(null);
entries.put(u, v);
}

View file

@ -15,7 +15,7 @@ public class DataStateProviderImpl extends DataStateProvider {
return;
}
var entry = DataStorage.get().getEntryByStore(store);
var entry = DataStorage.get().getStoreEntryIfPresent(store);
if (entry.isEmpty()) {
return;
}
@ -30,7 +30,7 @@ public class DataStateProviderImpl extends DataStateProvider {
return def.get();
}
var entry = DataStorage.get().getEntryByStore(store);
var entry = DataStorage.get().getStoreEntryIfPresent(store);
if (entry.isEmpty()) {
return def.get();
}

View file

@ -79,7 +79,7 @@ public abstract class DataStorage {
return INSTANCE;
}
public DataSourceCollection getInternalCollection() {
public synchronized DataSourceCollection getInternalCollection() {
var found = sourceCollections.stream()
.filter(o -> o.getName() != null && o.getName().equals("Internal"))
.findAny();
@ -95,7 +95,28 @@ public abstract class DataStorage {
return internalCollection;
}
public String createUniqueSourceEntryName(DataSourceCollection col, DataSource<?> source) {
public synchronized List<DataStoreEntry> getStoreChildren(DataStoreEntry entry, boolean deep) {
var children = new ArrayList<>(getStoreEntries().stream().filter(other -> {
if (!other.getState().isUsable()) {
return false;
}
var parent = other
.getProvider()
.getParent(other.getStore());
return entry.getStore().equals(parent);
}).toList());
if (deep) {
for (DataStoreEntry dataStoreEntry : new ArrayList<>(children)) {
children.addAll(getStoreChildren(dataStoreEntry, true));
}
}
return children;
}
public synchronized String createUniqueSourceEntryName(DataSourceCollection col, DataSource<?> source) {
var def = source.determineDefaultName();
if (def.isPresent()) {
return createUniqueSourceEntryName(col, def.get());
@ -117,22 +138,22 @@ public abstract class DataStorage {
return createUniqueSourceEntryName(col, typeName);
}
private String createUniqueStoreEntryName(String base) {
if (DataStorage.get().getStoreIfPresent(base).isEmpty()) {
private synchronized String createUniqueStoreEntryName(String base) {
if (DataStorage.get().getStoreEntryIfPresent(base).isEmpty()) {
return base;
}
int counter = 1;
while (true) {
var name = base + counter;
if (DataStorage.get().getStoreIfPresent(name).isEmpty()) {
if (DataStorage.get().getStoreEntryIfPresent(name).isEmpty()) {
return name;
}
counter++;
}
}
private String createUniqueSourceEntryName(DataSourceCollection col, String base) {
private synchronized String createUniqueSourceEntryName(DataSourceCollection col, String base) {
base = DataSourceId.cleanString(base);
var id = DataSourceId.create(col != null ? col.getName() : null, base);
if (DataStorage.get().getDataSource(DataSourceReference.id(id)).isEmpty()) {
@ -149,11 +170,11 @@ public abstract class DataStorage {
}
}
public DataSourceEntry getLatest() {
public synchronized DataSourceEntry getLatest() {
return latest;
}
public void setLatest(DataSourceEntry latest) {
public synchronized void setLatest(DataSourceEntry latest) {
this.latest = latest;
}
@ -179,7 +200,7 @@ public abstract class DataStorage {
return dir.resolve("streams");
}
public Optional<DataSourceCollection> getCollectionForSourceEntry(DataSourceEntry entry) {
public synchronized Optional<DataSourceCollection> getCollectionForSourceEntry(DataSourceEntry entry) {
if (entry == null) {
return Optional.of(getInternalCollection());
}
@ -189,7 +210,7 @@ public abstract class DataStorage {
.findAny();
}
public Optional<DataSourceCollection> getCollectionForName(String name) {
public synchronized Optional<DataSourceCollection> getCollectionForName(String name) {
if (name == null) {
return Optional.ofNullable(getInternalCollection());
}
@ -199,21 +220,22 @@ public abstract class DataStorage {
.findAny();
}
public Optional<DataStoreEntry> getStoreIfPresent(@NonNull String name) {
return storeEntries.stream()
.filter(n -> n.getName().equalsIgnoreCase(name))
.findFirst();
public synchronized List<DataStore> getUsableStores() {
return new ArrayList<>(getStoreEntries().stream()
.filter(entry -> !entry.isDisabled())
.map(DataStoreEntry::getStore)
.toList());
}
public void renameStore(DataStoreEntry entry, String name) {
if (getStoreIfPresent(name).isPresent()) {
public synchronized void renameStoreEntry(DataStoreEntry entry, String name) {
if (getStoreEntryIfPresent(name).isPresent()) {
throw new IllegalArgumentException("Store with name " + name + " already exists");
}
entry.setName(name);
}
public DataStoreEntry getStore(@NonNull String name, boolean acceptDisabled) {
public synchronized DataStoreEntry getStoreEntry(@NonNull String name, boolean acceptDisabled) {
var entry = storeEntries.stream()
.filter(n -> n.getName().equalsIgnoreCase(name))
.findFirst()
@ -224,7 +246,7 @@ public abstract class DataStorage {
return entry;
}
public DataStoreEntry getStore(@NonNull DataStore store) {
public synchronized DataStoreEntry getStoreEntry(@NonNull DataStore store) {
var entry = storeEntries.stream()
.filter(n -> store.equals(n.getStore()))
.findFirst()
@ -232,13 +254,19 @@ public abstract class DataStorage {
return entry;
}
public Optional<DataStoreEntry> getStoreEntryIfPresent(@NonNull DataStore store) {
public synchronized Optional<DataStoreEntry> getStoreEntryIfPresent(@NonNull DataStore store) {
var entry =
storeEntries.stream().filter(n -> store.equals(n.getStore())).findFirst();
return entry;
}
public Optional<DataSourceEntry> getDataSource(DataSourceReference ref) {
public synchronized Optional<DataStoreEntry> getStoreEntryIfPresent(@NonNull String name) {
return storeEntries.stream()
.filter(n -> n.getName().equalsIgnoreCase(name))
.findFirst();
}
public synchronized Optional<DataSourceEntry> getDataSource(DataSourceReference ref) {
Objects.requireNonNull(ref, "ref");
switch (ref.getType()) {
@ -275,7 +303,7 @@ public abstract class DataStorage {
throw new AssertionError();
}
public DataSourceId getId(DataSourceEntry entry) {
public synchronized DataSourceId getId(DataSourceEntry entry) {
Objects.requireNonNull(entry, "entry");
if (!sourceEntries.contains(entry)) {
throw new IllegalArgumentException("Entry not in storage");
@ -290,7 +318,7 @@ public abstract class DataStorage {
return DataSourceId.create(col, en);
}
public void add(DataSourceEntry e, DataSourceCollection c) {
public synchronized void add(DataSourceEntry e, DataSourceCollection c) {
Objects.requireNonNull(e, "entry");
if (c != null && !sourceCollections.contains(c)) {
@ -339,8 +367,8 @@ public abstract class DataStorage {
});
}
private void propagateUpdate() {
for (DataStoreEntry dataStoreEntry : getStores()) {
private synchronized void propagateUpdate() {
for (DataStoreEntry dataStoreEntry : getStoreEntries()) {
dataStoreEntry.simpleRefresh();
}
@ -349,8 +377,8 @@ public abstract class DataStorage {
}
}
public void addStoreEntry(@NonNull DataStoreEntry e) {
if (getStoreIfPresent(e.getName()).isPresent()) {
public synchronized void addStoreEntry(@NonNull DataStoreEntry e) {
if (getStoreEntryIfPresent(e.getName()).isPresent()) {
throw new IllegalArgumentException("Store with name " + e.getName() + " already exists");
}
@ -362,7 +390,7 @@ public abstract class DataStorage {
this.listeners.forEach(l -> l.onStoreAdd(e));
}
public void addStoreIfNotPresent(@NonNull String name, DataStore store) {
public synchronized void addStoreEntryIfNotPresent(@NonNull String name, DataStore store) {
if (getStoreEntryIfPresent(store).isPresent()) {
return;
}
@ -371,27 +399,29 @@ public abstract class DataStorage {
addStoreEntry(e);
}
public DataStoreEntry addStore(@NonNull String name, DataStore store) {
public synchronized DataStoreEntry addStoreEntry(@NonNull String name, DataStore store) {
var e = DataStoreEntry.createNew(UUID.randomUUID(), createUniqueStoreEntryName(name), store);
addStoreEntry(e);
return e;
}
public void deleteStoreEntry(@NonNull DataStoreEntry store) {
public synchronized void deleteStoreEntry(@NonNull DataStoreEntry store) {
if (!store.getConfiguration().isDeletable()) {
throw new UnsupportedOperationException();
}
this.storeEntries.remove(store);
propagateUpdate();
save();
this.listeners.forEach(l -> l.onStoreRemove(store));
}
public void addListener(StorageListener l) {
public synchronized void addListener(StorageListener l) {
this.listeners.add(l);
}
public DataSourceCollection createOrGetCollection(String name) {
public synchronized DataSourceCollection createOrGetCollection(String name) {
return getCollectionForName(name).orElseGet(() -> {
var col = DataSourceCollection.createNew(name);
addCollection(col);
@ -399,7 +429,7 @@ public abstract class DataStorage {
});
}
public void addCollection(DataSourceCollection c) {
public synchronized void addCollection(DataSourceCollection c) {
checkImmutable();
c.setDirectory(
@ -408,16 +438,16 @@ public abstract class DataStorage {
this.listeners.forEach(l -> l.onCollectionAdd(c));
}
public void deleteCollection(DataSourceCollection c) {
public synchronized void deleteCollection(DataSourceCollection c) {
checkImmutable();
this.sourceCollections.remove(c);
this.listeners.forEach(l -> l.onCollectionRemove(c));
c.getEntries().forEach(this::deleteEntry);
c.getEntries().forEach(this::deleteSourceEntry);
}
public void deleteEntry(DataSourceEntry e) {
public synchronized void deleteSourceEntry(DataSourceEntry e) {
checkImmutable();
this.sourceEntries.remove(e);
@ -430,56 +460,42 @@ public abstract class DataStorage {
public abstract void load();
public void refresh() {
public synchronized void refresh() {
storeEntries.forEach(entry -> {
try {
entry.refresh(false);
} catch (Exception e) {
ErrorEvent.fromThrowable(e).omit().reportable(false).handle();
}
entry.simpleRefresh();
});
save();
}
public abstract void save();
public Optional<DataStoreEntry> getStoreEntryByUuid(UUID id) {
public synchronized Optional<DataStoreEntry> getStoreEntry(UUID id) {
return storeEntries.stream().filter(e -> e.getUuid().equals(id)).findAny();
}
public Optional<DataSourceEntry> getSourceEntryByUuid(UUID id) {
public synchronized Optional<DataSourceEntry> getSourceEntry(UUID id) {
return sourceEntries.stream().filter(e -> e.getUuid().equals(id)).findAny();
}
public Optional<DataSourceEntry> getDataSourceEntryById(DataSourceId id) {
return sourceEntries.stream().filter(e -> getId(e).equals(id)).findAny();
}
public Optional<DataSourceEntry> getEntryBySource(DataSource<?> source) {
public synchronized Optional<DataSourceEntry> getSourceEntry(DataSource<?> source) {
return sourceEntries.stream()
.filter(e -> e.getSource() != null && e.getSource().equals(source))
.findAny();
}
public Optional<DataStoreEntry> getEntryByStore(DataStore store) {
return storeEntries.stream()
.filter(e -> e.getStore() != null && e.getStore().equals(store))
.findAny();
}
public Optional<DataSourceCollection> getCollectionByUuid(UUID id) {
public synchronized Optional<DataSourceCollection> getCollection(UUID id) {
return sourceCollections.stream().filter(e -> e.getUuid().equals(id)).findAny();
}
public List<DataSourceEntry> getSourceEntries() {
return Collections.unmodifiableList(sourceEntries);
public synchronized List<DataSourceEntry> getSourceEntries() {
return new ArrayList<>(sourceEntries);
}
public List<DataSourceCollection> getSourceCollections() {
return Collections.unmodifiableList(sourceCollections);
public synchronized List<DataSourceCollection> getSourceCollections() {
return new ArrayList<>(sourceCollections);
}
public List<DataStoreEntry> getStores() {
return Collections.unmodifiableList(storeEntries);
public synchronized List<DataStoreEntry> getStoreEntries() {
return new ArrayList<>(storeEntries);
}
}

View file

@ -52,7 +52,7 @@ public class DataStorageParser {
return Optional.empty();
}
var entry = DataStorage.get().getStoreEntryByUuid(id);
var entry = DataStorage.get().getStoreEntry(id);
if (entry.isEmpty()) {
TrackEvent.withWarn("storage", "Encountered unknown store").tag("id", id);
return Optional.empty();
@ -70,7 +70,7 @@ public class DataStorageParser {
});
node = replaceReferenceIdsForType(node, "sourceId", id -> {
var foundEntry = DataStorage.get().getSourceEntryByUuid(id);
var foundEntry = DataStorage.get().getSourceEntry(id);
if (foundEntry.isPresent()) {
var sourceNode = mapper.valueToTree(foundEntry.get().getSource());
// return Optional.of(resolvesReferenceIds(sourceNode));

View file

@ -38,8 +38,12 @@ public class DataStorageWriter {
try {
var store = mapper.treeToValue(possibleReference, DataStore.class);
if (store == null) {
return Optional.empty();
}
if (!isRoot) {
var found = DataStorage.get().getEntryByStore(store);
var found = DataStorage.get().getStoreEntryIfPresent(store);
return found.map(dataSourceEntry -> dataSourceEntry.getUuid());
}
} catch (JsonProcessingException e) {
@ -55,7 +59,7 @@ public class DataStorageWriter {
try {
var source = mapper.treeToValue(possibleReference, DataSource.class);
if (!isRoot) {
var found = DataStorage.get().getEntryBySource(source);
var found = DataStorage.get().getSourceEntry(source);
return found.map(dataSourceEntry -> dataSourceEntry.getUuid());
}
} catch (JsonProcessingException e) {

View file

@ -93,7 +93,6 @@ public class DataStoreEntry extends StorageElement {
Configuration configuration) {
var entry = new DataStoreEntry(
directory, uuid, name, lastUsed, lastModified, information, storeNode, false, state, configuration);
entry.refresh(false);
return entry;
}
@ -236,7 +235,7 @@ public class DataStoreEntry extends StorageElement {
information = null;
throw e;
} finally {
listeners.forEach(l -> l.onUpdate());
propagateUpdate();
}
}
}

View file

@ -45,7 +45,7 @@ public class StandardStorage extends DataStorage {
return;
}
var entry = getStoreEntryByUuid(uuid);
var entry = getStoreEntry(uuid);
if (entry.isEmpty()) {
TrackEvent.withTrace("storage", "Deleting leftover store directory")
.tag("uuid", uuid)
@ -77,7 +77,7 @@ public class StandardStorage extends DataStorage {
return;
}
var entry = getSourceEntryByUuid(uuid);
var entry = getSourceEntry(uuid);
if (entry.isEmpty()) {
TrackEvent.withTrace("storage", "Deleting leftover entry directory")
.tag("uuid", uuid)
@ -109,7 +109,7 @@ public class StandardStorage extends DataStorage {
return;
}
var col = getCollectionByUuid(uuid);
var col = getCollection(uuid);
if (col.isEmpty()) {
TrackEvent.withTrace("storage", "Deleting leftover collection directory")
.tag("uuid", uuid)
@ -125,7 +125,7 @@ public class StandardStorage extends DataStorage {
}
}
public void load() {
public synchronized void load() {
var newSession = isNewSession();
var entriesDir = getSourcesDir().resolve("entries");
var collectionsDir = getSourcesDir().resolve("collections");
@ -236,7 +236,7 @@ public class StandardStorage extends DataStorage {
deleteLeftovers();
}
public void save() {
public synchronized void save() {
var entriesDir = getSourcesDir().resolve("entries");
var collectionsDir = getSourcesDir().resolve("collections");

View file

@ -60,7 +60,7 @@ public class XPipeInstanceHelper {
}
public static XPipeInstance refresh() {
Map<ShellStore, Optional<XPipeInstance>> map = DataStorage.get().getStores().stream()
Map<ShellStore, Optional<XPipeInstance>> map = DataStorage.get().getStoreEntries().stream()
.filter(entry -> entry.getStore() instanceof ShellStore)
.collect(Collectors.toMap(
entry -> entry.getStore().asNeeded(),

View file

@ -1,5 +1,6 @@
package io.xpipe.app.util;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.core.charsetter.NewLine;
import io.xpipe.core.charsetter.StreamCharset;
import io.xpipe.core.dialog.Dialog;
@ -32,7 +33,7 @@ public class DialogHelper {
return new LocalStore();
}
var stored = XPipeDaemon.getInstance().getNamedStore(name);
var stored = DataStorage.get().getStoreEntryIfPresent(name).map(entry -> entry.getStore());
if (stored.isEmpty()) {
throw new IllegalArgumentException(String.format("Store not found: %s", name));
}
@ -57,7 +58,7 @@ public class DialogHelper {
return new LocalStore();
}
var stored = XPipeDaemon.getInstance().getNamedStore(name);
var stored = DataStorage.get().getStoreEntryIfPresent(name).map(entry -> entry.getStore());
if (stored.isEmpty()) {
throw new IllegalArgumentException(String.format("Store not found: %s", name));
}
@ -98,7 +99,7 @@ public class DialogHelper {
var name = XPipeDaemon.getInstance().getStoreName(store).orElse(null);
return Dialog.query("Store", false, true, false, name, QueryConverter.STRING)
.map((String newName) -> {
var found = XPipeDaemon.getInstance().getNamedStore(newName).orElseThrow();
var found = DataStorage.get().getStoreEntryIfPresent(newName).map(entry -> entry.getStore()).orElseThrow();
if (!filter.isAssignableFrom(found.getClass())) {
throw new IllegalArgumentException("Incompatible store type");
}

View file

@ -1,7 +1,7 @@
package io.xpipe.app.util;
import io.xpipe.app.core.AppI18n;
import io.xpipe.core.impl.LocalStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.ValidationException;
@ -22,7 +22,7 @@ public class Validators {
}
public static void namedStoreExists(DataStore store, String name) throws ValidationException {
if (!XPipeDaemon.getInstance().getNamedStores().contains(store) && !(store instanceof LocalStore)) {
if (!DataStorage.get().getUsableStores().contains(store)) {
throw new ValidationException(AppI18n.get("app.missingStore", name));
}
}

View file

@ -1,19 +1,10 @@
package io.xpipe.app.util;
import io.xpipe.app.ext.DataSourceProvider;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.store.DataStore;
import javafx.beans.property.Property;
import javafx.beans.value.ObservableValue;
import javafx.scene.image.Image;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Predicate;
public interface XPipeDaemon {
@ -25,35 +16,6 @@ public interface XPipeDaemon {
return ServiceLoader.load(XPipeDaemon.class).findFirst();
}
List<DataStore> getNamedStores();
String getVersion();
Image image(String file);
String svgImage(String file);
<T extends Comp<?> & Validatable> T streamStoreChooser(
Property<DataStore> storeProperty,
Property<DataSourceProvider<?>> provider,
boolean showAnonymous,
boolean showSaved);
<T extends Comp<?> & Validatable> T namedStoreChooser(
ObservableValue<Predicate<DataStore>> filter,
Property<? extends DataStore> selected,
DataStoreProvider.DataCategory category);
<T extends Comp<?> & Validatable> T namedSourceChooser(
ObservableValue<Predicate<DataSource<?>>> filter,
Property<? extends DataSource<?>> selected,
DataSourceProvider.Category category);
<T extends Comp<?> & Validatable> T sourceProviderChooser(
Property<DataSourceProvider<?>> provider, DataSourceProvider.Category category, DataSourceType filter);
Optional<DataStore> getNamedStore(String name);
Optional<DataSource<?>> getSource(String id);
Optional<String> getStoreName(DataStore store);

View file

@ -1,102 +1,15 @@
package io.xpipe.app.util;
import io.xpipe.app.comp.source.DsProviderChoiceComp;
import io.xpipe.app.comp.source.NamedSourceChoiceComp;
import io.xpipe.app.comp.source.store.DsStreamStoreChoiceComp;
import io.xpipe.app.comp.source.store.NamedStoreChoiceComp;
import io.xpipe.app.core.AppImages;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.ext.DataSourceProvider;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.update.AppDownloads;
import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.store.DataStore;
import javafx.beans.property.Property;
import javafx.beans.value.ObservableValue;
import javafx.scene.image.Image;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
public class XPipeDaemonProvider implements XPipeDaemon {
@Override
public List<DataStore> getNamedStores() {
return DataStorage.get().getStores().stream()
.filter(entry -> !entry.isDisabled())
.map(DataStoreEntry::getStore)
.toList();
}
@Override
public String getVersion() {
var version = AppProperties.get().getVersion() != null
? AppProperties.get().getVersion()
: AppDownloads.getLatestVersion();
return version;
}
@Override
public Image image(String file) {
return AppImages.image(file);
}
@Override
public String svgImage(String file) {
return AppImages.svgImage(file);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Comp<?> & Validatable> T streamStoreChooser(
Property<DataStore> storeProperty,
Property<DataSourceProvider<?>> provider,
boolean showAnonymous,
boolean showSaved) {
return (T) new DsStreamStoreChoiceComp(
storeProperty, provider, showAnonymous, showSaved, DsStreamStoreChoiceComp.Mode.WRITE);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Comp<?> & Validatable> T namedStoreChooser(
ObservableValue<Predicate<DataStore>> filter,
Property<? extends DataStore> selected,
DataStoreProvider.DataCategory category) {
return (T) new NamedStoreChoiceComp(filter, selected, category);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Comp<?> & Validatable> T namedSourceChooser(
ObservableValue<Predicate<DataSource<?>>> filter,
Property<? extends DataSource<?>> selected,
DataSourceProvider.Category category) {
return (T) new NamedSourceChoiceComp(filter, selected, category);
}
@Override
@SuppressWarnings("unchecked")
public <T extends Comp<?> & Validatable> T sourceProviderChooser(
Property<DataSourceProvider<?>> provider, DataSourceProvider.Category category, DataSourceType filter) {
return (T) new DsProviderChoiceComp(category, provider, filter);
}
@Override
public Optional<DataStore> getNamedStore(String name) {
if (name == null) {
return Optional.empty();
}
return DataStorage.get().getStoreIfPresent(name).map(DataStoreEntry::getStore);
}
@Override
public Optional<DataSource<?>> getSource(String id) {
var sourceId = DataSourceId.fromString(id);
@ -111,7 +24,7 @@ public class XPipeDaemonProvider implements XPipeDaemon {
return Optional.empty();
}
return DataStorage.get().getStores().stream()
return DataStorage.get().getStoreEntries().stream()
.filter(entry -> !entry.isDisabled() && entry.getStore().equals(store))
.findFirst()
.map(entry -> entry.getName());
@ -119,7 +32,7 @@ public class XPipeDaemonProvider implements XPipeDaemon {
@Override
public Optional<String> getSourceId(DataSource<?> source) {
var entry = DataStorage.get().getEntryBySource(source);
var entry = DataStorage.get().getSourceEntry(source);
return entry.map(
dataSourceEntry -> DataStorage.get().getId(dataSourceEntry).toString());
}

View file

@ -15,6 +15,7 @@ dataSourceIntroCollection=Collection data sources contain multiple sub data sour
storeIntroTitle=Adding Connections
storeIntroDescription=Connect to remote systems, databases, and more.
storeStreamDescription=Stream connections produce raw byte data\nthat can be used to construct data sources from.
storeMachineDescription=Shell connections allow you to access local and remote shells.\nThrough them, you can then interact with any remote machine.
storeMachineDescription=You can quickly search for available remote connections automatically.\nAlternatively, you can also of course add them manually.
detectConnections=Detect connections
storeDatabaseDescription=Database connections allow you to connect to\na database server and interact with its contained data.
storeDocumentation=In case you prefer a more structured approach to\nfamiliarizing yourself with X-Pipe, check out the documentation:

View file

@ -42,11 +42,6 @@
-fx-padding: 0;
}
.loading-comp .spinner {
-jfx-radius: 2.15em;
-fx-background-color:transparent;
}
.loading-comp {
-fx-background-color: #FFFFFFAA;
}

View file

@ -1,11 +1,11 @@
package io.xpipe.ext.base;
import io.xpipe.app.comp.source.store.DsStreamStoreChoiceComp;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.util.DataStoreFormatter;
import io.xpipe.app.util.DialogHelper;
import io.xpipe.app.util.SimpleValidator;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.dialog.Dialog;
import io.xpipe.core.impl.FileStore;
import io.xpipe.core.impl.LocalStore;
@ -24,7 +24,8 @@ public class FileStoreProvider implements DataStoreProvider {
@Override
public GuiDialog guiDialog(Property<DataStore> store) {
var val = new SimpleValidator();
var comp = XPipeDaemon.getInstance().streamStoreChooser(store, null, false, false);
var comp = new DsStreamStoreChoiceComp(
store, null, false, false, DsStreamStoreChoiceComp.Mode.WRITE);
return new GuiDialog(comp, val);
}

View file

@ -4,7 +4,6 @@ import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.StorageElement;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.DataStore;
@ -33,7 +32,7 @@ public class LocalStoreProvider implements DataStoreProvider {
@Override
public void storageInit() throws Exception {
var hasLocal = XPipeDaemon.getInstance().getNamedStores().stream()
var hasLocal = DataStorage.get().getUsableStores().stream()
.anyMatch(dataStore -> dataStore instanceof LocalStore);
if (hasLocal) {
return;

View file

@ -0,0 +1,66 @@
package io.xpipe.ext.base.actions;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.store.DataStore;
import javafx.beans.value.ObservableValue;
import lombok.Value;
public class DeleteStoreChildrenAction implements ActionProvider {
@Value
static class Action implements ActionProvider.Action {
DataStoreEntry store;
@Override
public boolean requiresPlatform() {
return false;
}
@Override
public void execute() throws Exception {
DataStorage.get().getStoreChildren(store,true).forEach(entry -> {
DataStorage.get().deleteStoreEntry(entry);
});
}
}
@Override
public DataStoreCallSite<?> getDataStoreCallSite() {
return new DataStoreCallSite<DataStore>() {
@Override
public boolean isMajor() {
return false;
}
@Override
public ActionProvider.Action createAction(DataStore store) {
return new Action(DataStorage.get().getStoreEntry(store));
}
@Override
public Class<DataStore> getApplicableClass() {
return DataStore.class;
}
@Override
public boolean isApplicable(DataStore o) throws Exception {
return DataStorage.get().getStoreChildren(DataStorage.get().getStoreEntry(o),true).size() > 1;
}
@Override
public ObservableValue<String> getName(DataStore store) {
return AppI18n.observable("base.deleteChildren");
}
@Override
public String getIcon(DataStore store) {
return "mdal-delete_outline";
}
};
}
}

View file

@ -43,7 +43,7 @@ public class EditStoreAction implements ActionProvider {
@Override
public ActionProvider.Action createAction(DataStore store) {
return new Action(DataStorage.get().getStore(store));
return new Action(DataStorage.get().getStoreEntry(store));
}
@Override
@ -53,7 +53,7 @@ public class EditStoreAction implements ActionProvider {
@Override
public boolean isApplicable(DataStore o) throws Exception {
return DataStorage.get().getStore(o).getConfiguration().isEditable();
return DataStorage.get().getStoreEntry(o).getConfiguration().isEditable();
}
@Override

View file

@ -1,6 +1,7 @@
package io.xpipe.ext.base.apps;
import io.xpipe.app.comp.source.GuiDsTableMappingConfirmation;
import io.xpipe.app.comp.source.NamedSourceChoiceComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataSourceProvider;
import io.xpipe.app.ext.DataSourceProviders;
@ -10,7 +11,6 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.ChainedValidator;
import io.xpipe.app.util.DynamicOptionsBuilder;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.source.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty;
@ -34,8 +34,7 @@ public class DataSourceOutputTarget implements DataSourceTarget {
var target = new SimpleObjectProperty<DataSource<?>>();
var sourceType =
DataSourceProviders.byDataSourceClass(source.getClass()).getPrimaryType();
var chooser = XPipeDaemon.getInstance()
.namedSourceChooser(
var chooser = new NamedSourceChoiceComp(
new SimpleObjectProperty<>(s -> s != source
&& s.getFlow().hasOutput()
&& DataSourceProviders.byDataSourceClass(s.getClass())

View file

@ -1,5 +1,7 @@
package io.xpipe.ext.base.apps;
import io.xpipe.app.comp.source.DsProviderChoiceComp;
import io.xpipe.app.comp.source.store.DsStreamStoreChoiceComp;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataSourceProvider;
@ -12,7 +14,6 @@ import io.xpipe.app.fxcomps.util.SimpleChangeListener;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.ChainedValidator;
import io.xpipe.app.util.DynamicOptionsBuilder;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.WriteMode;
@ -67,8 +68,7 @@ public class FileOutputTarget implements DataSourceTarget {
});
var layout = new BorderPane();
var providerChoice = XPipeDaemon.getInstance()
.sourceProviderChooser(provider, DataSourceProvider.Category.STREAM, sourceProvider.getPrimaryType());
var providerChoice = new DsProviderChoiceComp(DataSourceProvider.Category.STREAM, provider, sourceProvider.getPrimaryType());
providerChoice.apply(GrowAugment.create(true, false));
var providerChoiceRegion = providerChoice.createRegion();
var top = new VBox(providerChoiceRegion, new Separator());
@ -76,7 +76,7 @@ public class FileOutputTarget implements DataSourceTarget {
layout.setTop(top);
layout.getStyleClass().add("data-input-creation-step");
var chooser = XPipeDaemon.getInstance().streamStoreChooser(target, provider, true, true);
var chooser = new DsStreamStoreChoiceComp(target, provider, true, true, DsStreamStoreChoiceComp.Mode.WRITE);
var mode = new SimpleObjectProperty<WriteMode>();
var modeComp = new WriteModeChoiceComp(mode, availableModes);
target.addListener((c, o, n) -> {

View file

@ -1,11 +1,11 @@
package io.xpipe.ext.base.apps;
import io.xpipe.app.comp.source.store.NamedStoreChoiceComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataSourceTarget;
import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.fxcomps.augment.GrowAugment;
import io.xpipe.app.util.DynamicOptionsBuilder;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.store.StreamDataStore;
@ -25,8 +25,7 @@ public class RawFileOutputTarget implements DataSourceTarget {
public InstructionsDisplay createRetrievalInstructions(DataSource<?> source, ObservableValue<DataSourceId> id) {
var target = new SimpleObjectProperty<StreamDataStore>();
var storeChoice = XPipeDaemon.getInstance()
.namedStoreChooser(
var storeChoice = new NamedStoreChoiceComp(
new SimpleObjectProperty<>(store -> store instanceof StreamDataStore
&& (store.getFlow().hasOutput())),
target,

View file

@ -22,6 +22,7 @@ open module io.xpipe.ext.base {
requires org.apache.commons.lang3;
provides ActionProvider with
DeleteStoreChildrenAction,
AddStoreAction,
EditStoreAction,
StreamExportAction,

View file

@ -12,6 +12,7 @@ options=Options
copyShareLink=Copy share link
selectStore=Select Store
saveSource=Save for later
deleteChildren=Delete children
selectSource=Select Source
commandLineRead=Update
commandLineWrite=Write

View file

@ -1 +1 @@
0.5.4
0.5.5