mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 00:50:31 +00:00
Various small fixes [release]
This commit is contained in:
parent
1b23c833ed
commit
c936e37509
48 changed files with 285 additions and 315 deletions
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -42,11 +42,6 @@
|
|||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.loading-comp .spinner {
|
||||
-jfx-radius: 2.15em;
|
||||
-fx-background-color:transparent;
|
||||
}
|
||||
|
||||
.loading-comp {
|
||||
-fx-background-color: #FFFFFFAA;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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) -> {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -22,6 +22,7 @@ open module io.xpipe.ext.base {
|
|||
requires org.apache.commons.lang3;
|
||||
|
||||
provides ActionProvider with
|
||||
DeleteStoreChildrenAction,
|
||||
AddStoreAction,
|
||||
EditStoreAction,
|
||||
StreamExportAction,
|
||||
|
|
|
@ -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
|
||||
|
|
2
version
2
version
|
@ -1 +1 @@
|
|||
0.5.4
|
||||
0.5.5
|
Loading…
Reference in a new issue