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 @Override
protected Region createSimple() { 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 -> { return new ListBoxViewComp<>(FXCollections.observableList(list), FXCollections.observableList(list), bookmark -> {
var imgView = var imgView =
new PrettyImageComp(new SimpleStringProperty(bookmark.entry().getProvider().getDisplayIconFileName()), 16, 16).createRegion(); 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 return model.getStore().getValue() != null
? DataStorage.get() ? DataStorage.get()
.getEntryByStore(model.getStore().getValue()) .getStoreEntry(model.getStore().getValue())
.orElseThrow()
.getName() .getName()
: null; : null;
}, },
@ -194,8 +193,7 @@ public class FileBrowserComp extends SimpleComp {
() -> { () -> {
return model.getStore().getValue() != null return model.getStore().getValue() != null
? DataStorage.get() ? DataStorage.get()
.getEntryByStore(model.getStore().getValue()) .getStoreEntry(model.getStore().getValue())
.orElseThrow()
.getProvider() .getProvider()
.getDisplayIconFileName() .getDisplayIconFileName()
: null; : null;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -54,7 +54,7 @@ public class StoreViewState {
} }
private void addStorageGroupListeners() { private void addStorageGroupListeners() {
allEntries.setAll(FXCollections.observableArrayList(DataStorage.get().getStores().stream() allEntries.setAll(FXCollections.observableArrayList(DataStorage.get().getStoreEntries().stream()
.map(StoreEntryWrapper::new) .map(StoreEntryWrapper::new)
.toList())); .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 { default DataStore resolveStore(@NonNull DataStore in, boolean acceptDisabled) throws ClientException {
try { try {
if (in instanceof NamedStore n) { if (in instanceof NamedStore n) {
var found = DataStorage.get().getStore(n.getName(), acceptDisabled); var found = DataStorage.get().getStoreEntry(n.getName(), acceptDisabled);
return found.getStore(); return found.getStore();
} }
} catch (IllegalArgumentException ex) { } 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 { 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()) { if (store.isEmpty()) {
throw new ClientException("No store with name " + name + " was found"); throw new ClientException("No store with name " + name + " was found");
} }

View file

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

View file

@ -18,9 +18,9 @@ public class ReadDrainExchangeImpl extends ReadDrainExchange
@Override @Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception { 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()) { 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)) { 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 { public Response handleRequest(BeaconHandler handler, Request msg) throws Exception {
var e = getSourceEntry(msg.getRef(), null, true); var e = getSourceEntry(msg.getRef(), null, true);
var id = DataStorage.get().getId(e); var id = DataStorage.get().getId(e);
DataStorage.get().deleteEntry(e); DataStorage.get().deleteSourceEntry(e);
return Response.builder().id(id).build(); return Response.builder().id(id).build();
} }
} }

View file

@ -11,7 +11,7 @@ public class RemoveStoreExchangeImpl extends RemoveStoreExchange
@Override @Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception { 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()) { if (!s.getConfiguration().isDeletable()) {
throw new ClientException("Store is not deletable"); throw new ClientException("Store is not deletable");
} }

View file

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

View file

@ -10,8 +10,8 @@ public class RenameStoreExchangeImpl extends RenameStoreExchange
@Override @Override
public Response handleRequest(BeaconHandler handler, Request msg) throws Exception { 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);
DataStorage.get().renameStore(s, msg.getNewName()); DataStorage.get().renameStoreEntry(s, msg.getNewName());
return Response.builder().build(); return Response.builder().build();
} }
} }

View file

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

View file

@ -2,6 +2,7 @@ package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.CustomComboBoxBuilder; import io.xpipe.app.util.CustomComboBoxBuilder;
import io.xpipe.app.util.XPipeDaemon; import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.impl.FileStore; import io.xpipe.core.impl.FileStore;
@ -23,7 +24,7 @@ public class FileSystemStoreChoiceComp extends SimpleComp {
} }
private static String getName(FileSystemStore store) { private static String getName(FileSystemStore store) {
var name = XPipeDaemon.getInstance().getNamedStores().stream() var name = DataStorage.get().getUsableStores().stream()
.filter(e -> e.equals(store)) .filter(e -> e.equals(store))
.findAny() .findAny()
.map(e -> XPipeDaemon.getInstance().getStoreName(e).orElse("?")) .map(e -> XPipeDaemon.getInstance().getStoreName(e).orElse("?"))
@ -61,7 +62,7 @@ public class FileSystemStoreChoiceComp extends SimpleComp {
var comboBox = var comboBox =
new CustomComboBoxBuilder<FileSystemStore>(fileSystemProperty, this::createGraphic, null, v -> true); new CustomComboBoxBuilder<FileSystemStore>(fileSystemProperty, this::createGraphic, null, v -> true);
comboBox.setSelectedDisplay(this::createDisplayGraphic); comboBox.setSelectedDisplay(this::createDisplayGraphic);
XPipeDaemon.getInstance().getNamedStores().stream() DataStorage.get().getUsableStores().stream()
.filter(e -> e instanceof FileSystemStore) .filter(e -> e instanceof FileSystemStore)
.map(e -> (FileSystemStore) e) .map(e -> (FileSystemStore) e)
.forEach(comboBox::add); .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.core.AppI18n;
import io.xpipe.app.ext.DataStoreProviders; import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.CustomComboBoxBuilder; import io.xpipe.app.util.CustomComboBoxBuilder;
import io.xpipe.app.util.XPipeDaemon; import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
@ -52,7 +53,7 @@ public class ShellStoreChoiceComp<T extends ShellStore> extends SimpleComp {
var imgView = var imgView =
new PrettyImageComp(new SimpleStringProperty(provider.getDisplayIconFileName()), 16, 16).createRegion(); 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)) .filter(e -> e.equals(s))
.findAny() .findAny()
.flatMap(store -> { .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); new CustomComboBoxBuilder<T>(selected, this::createGraphic, new Label(AppI18n.get("none")), n -> true);
comboBox.setUnknownNode(t -> createGraphic(t)); comboBox.setUnknownNode(t -> createGraphic(t));
var available = XPipeDaemon.getInstance().getNamedStores().stream() var available = DataStorage.get().getUsableStores().stream()
.filter(s -> s != self) .filter(s -> s != self)
.filter(s -> storeClass.isAssignableFrom(s.getClass()) && applicableCheck.test((T) s)) .filter(s -> storeClass.isAssignableFrom(s.getClass()) && applicableCheck.test((T) s))
.map(s -> (ShellStore) 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); JavaType listType = mapper.getTypeFactory().constructCollectionType(ArrayList.class, UUID.class);
var entries = new LinkedHashMap<UUID, DataSourceEntry>(); var entries = new LinkedHashMap<UUID, DataSourceEntry>();
for (var u : mapper.<List<UUID>>readValue(dir.resolve("entries.json").toFile(), listType)) { 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); entries.put(u, v);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -60,7 +60,7 @@ public class XPipeInstanceHelper {
} }
public static XPipeInstance refresh() { 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) .filter(entry -> entry.getStore() instanceof ShellStore)
.collect(Collectors.toMap( .collect(Collectors.toMap(
entry -> entry.getStore().asNeeded(), entry -> entry.getStore().asNeeded(),

View file

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

View file

@ -1,7 +1,7 @@
package io.xpipe.app.util; package io.xpipe.app.util;
import io.xpipe.app.core.AppI18n; 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.DataStore;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.ValidationException; import io.xpipe.core.util.ValidationException;
@ -22,7 +22,7 @@ public class Validators {
} }
public static void namedStoreExists(DataStore store, String name) throws ValidationException { 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)); throw new ValidationException(AppI18n.get("app.missingStore", name));
} }
} }

View file

@ -1,19 +1,10 @@
package io.xpipe.app.util; 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.DataSource;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.store.DataStore; 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.Optional;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.function.Predicate;
public interface XPipeDaemon { public interface XPipeDaemon {
@ -25,35 +16,6 @@ public interface XPipeDaemon {
return ServiceLoader.load(XPipeDaemon.class).findFirst(); 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<DataSource<?>> getSource(String id);
Optional<String> getStoreName(DataStore store); Optional<String> getStoreName(DataStore store);

View file

@ -1,102 +1,15 @@
package io.xpipe.app.util; 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.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.update.AppDownloads;
import io.xpipe.core.source.DataSource; import io.xpipe.core.source.DataSource;
import io.xpipe.core.source.DataSourceId; import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference; import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.store.DataStore; 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.Optional;
import java.util.function.Predicate;
public class XPipeDaemonProvider implements XPipeDaemon { 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 @Override
public Optional<DataSource<?>> getSource(String id) { public Optional<DataSource<?>> getSource(String id) {
var sourceId = DataSourceId.fromString(id); var sourceId = DataSourceId.fromString(id);
@ -111,7 +24,7 @@ public class XPipeDaemonProvider implements XPipeDaemon {
return Optional.empty(); return Optional.empty();
} }
return DataStorage.get().getStores().stream() return DataStorage.get().getStoreEntries().stream()
.filter(entry -> !entry.isDisabled() && entry.getStore().equals(store)) .filter(entry -> !entry.isDisabled() && entry.getStore().equals(store))
.findFirst() .findFirst()
.map(entry -> entry.getName()); .map(entry -> entry.getName());
@ -119,7 +32,7 @@ public class XPipeDaemonProvider implements XPipeDaemon {
@Override @Override
public Optional<String> getSourceId(DataSource<?> source) { public Optional<String> getSourceId(DataSource<?> source) {
var entry = DataStorage.get().getEntryBySource(source); var entry = DataStorage.get().getSourceEntry(source);
return entry.map( return entry.map(
dataSourceEntry -> DataStorage.get().getId(dataSourceEntry).toString()); dataSourceEntry -> DataStorage.get().getId(dataSourceEntry).toString());
} }

View file

@ -15,6 +15,7 @@ dataSourceIntroCollection=Collection data sources contain multiple sub data sour
storeIntroTitle=Adding Connections storeIntroTitle=Adding Connections
storeIntroDescription=Connect to remote systems, databases, and more. storeIntroDescription=Connect to remote systems, databases, and more.
storeStreamDescription=Stream connections produce raw byte data\nthat can be used to construct data sources from. 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. 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: 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; -fx-padding: 0;
} }
.loading-comp .spinner {
-jfx-radius: 2.15em;
-fx-background-color:transparent;
}
.loading-comp { .loading-comp {
-fx-background-color: #FFFFFFAA; -fx-background-color: #FFFFFFAA;
} }

View file

@ -1,11 +1,11 @@
package io.xpipe.ext.base; package io.xpipe.ext.base;
import io.xpipe.app.comp.source.store.DsStreamStoreChoiceComp;
import io.xpipe.app.ext.DataStoreProvider; import io.xpipe.app.ext.DataStoreProvider;
import io.xpipe.app.ext.GuiDialog; import io.xpipe.app.ext.GuiDialog;
import io.xpipe.app.util.DataStoreFormatter; import io.xpipe.app.util.DataStoreFormatter;
import io.xpipe.app.util.DialogHelper; import io.xpipe.app.util.DialogHelper;
import io.xpipe.app.util.SimpleValidator; import io.xpipe.app.util.SimpleValidator;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.dialog.Dialog; import io.xpipe.core.dialog.Dialog;
import io.xpipe.core.impl.FileStore; import io.xpipe.core.impl.FileStore;
import io.xpipe.core.impl.LocalStore; import io.xpipe.core.impl.LocalStore;
@ -24,7 +24,8 @@ public class FileStoreProvider implements DataStoreProvider {
@Override @Override
public GuiDialog guiDialog(Property<DataStore> store) { public GuiDialog guiDialog(Property<DataStore> store) {
var val = new SimpleValidator(); 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); 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.DataStorage;
import io.xpipe.app.storage.DataStoreEntry; import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.StorageElement; import io.xpipe.app.storage.StorageElement;
import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.impl.LocalStore; import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.store.DataStore; import io.xpipe.core.store.DataStore;
@ -33,7 +32,7 @@ public class LocalStoreProvider implements DataStoreProvider {
@Override @Override
public void storageInit() throws Exception { public void storageInit() throws Exception {
var hasLocal = XPipeDaemon.getInstance().getNamedStores().stream() var hasLocal = DataStorage.get().getUsableStores().stream()
.anyMatch(dataStore -> dataStore instanceof LocalStore); .anyMatch(dataStore -> dataStore instanceof LocalStore);
if (hasLocal) { if (hasLocal) {
return; 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 @Override
public ActionProvider.Action createAction(DataStore store) { public ActionProvider.Action createAction(DataStore store) {
return new Action(DataStorage.get().getStore(store)); return new Action(DataStorage.get().getStoreEntry(store));
} }
@Override @Override
@ -53,7 +53,7 @@ public class EditStoreAction implements ActionProvider {
@Override @Override
public boolean isApplicable(DataStore o) throws Exception { public boolean isApplicable(DataStore o) throws Exception {
return DataStorage.get().getStore(o).getConfiguration().isEditable(); return DataStorage.get().getStoreEntry(o).getConfiguration().isEditable();
} }
@Override @Override

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1 +1 @@
0.5.4 0.5.5