Improve git storage functionality

This commit is contained in:
crschnick 2023-11-26 15:54:14 +00:00
parent f5eebdae73
commit aa00cd6521
11 changed files with 124 additions and 37 deletions

View file

@ -43,7 +43,7 @@ public class StoreCategoryComp extends SimpleComp {
@Override
protected Region createSimple() {
var i = Bindings.createStringBinding(() -> {
if (!DataStorage.get().supportsSharing()) {
if (!DataStorage.get().supportsSharing() || category.getCategory().getParentCategory() == null) {
return "mdal-keyboard_arrow_right";
}
@ -108,25 +108,27 @@ public class StoreCategoryComp extends SimpleComp {
});
contextMenu.getItems().add(newCategory);
var share = new MenuItem();
share.textProperty().bind(Bindings.createStringBinding(() -> {
if (category.getShare().getValue()) {
return AppI18n.get("unshare");
} else {
return AppI18n.get("share");
}
}, category.getShare()));
share.graphicProperty().bind(Bindings.createObjectBinding(() -> {
if (category.getShare().getValue()) {
return new FontIcon("mdi2b-block-helper");
} else {
return new FontIcon("mdi2s-share");
}
}, category.getShare()));
share.setOnAction(event -> {
category.getShare().setValue(!category.getShare().getValue());
});
contextMenu.getItems().add(share);
if (category.getCategory().getParentCategory() != null) {
var share = new MenuItem();
share.textProperty().bind(Bindings.createStringBinding(() -> {
if (category.getShare().getValue()) {
return AppI18n.get("unshare");
} else {
return AppI18n.get("share");
}
}, category.getShare()));
share.graphicProperty().bind(Bindings.createObjectBinding(() -> {
if (category.getShare().getValue()) {
return new FontIcon("mdi2b-block-helper");
} else {
return new FontIcon("mdi2s-share");
}
}, category.getShare()));
share.setOnAction(event -> {
category.getShare().setValue(!category.getShare().getValue());
});
contextMenu.getItems().add(share);
}
var refresh = new MenuItem(AppI18n.get("rename"), new FontIcon("mdal-360"));
refresh.setOnAction(event -> {

View file

@ -71,6 +71,7 @@ public class CustomFormRenderer extends PreferencesFxFormRenderer {
for (int i = 0; i < elements.size(); i++) {
// add to GridPane
Element element = elements.get(i);
var offset = preferencesGroup.getTitle() != null ? 15 : 0;
if (element instanceof Field f) {
SimpleControl c = (SimpleControl) f.getRenderer();
c.setField(f);
@ -126,8 +127,6 @@ public class CustomFormRenderer extends PreferencesFxFormRenderer {
styleClass.append("-last");
}
var offset = preferencesGroup.getTitle() != null ? 15 : 0;
GridPane.setMargin(descriptionLabel, new Insets(SPACING, 0, 0, offset));
GridPane.setMargin(node, new Insets(SPACING, 0, 0, offset));
@ -144,6 +143,7 @@ public class CustomFormRenderer extends PreferencesFxFormRenderer {
if (element instanceof LazyNodeElement<?> nodeElement) {
var node = nodeElement.getNode();
grid.add(node, 0, i + rowAmount);
GridPane.setMargin(node, new Insets(SPACING, 0, 0, offset));
}
}
}

View file

@ -1,7 +1,6 @@
package io.xpipe.app.prefs;
import atlantafx.base.theme.Styles;
import com.dlsc.formsfx.model.structure.Element;
import com.dlsc.preferencesfx.model.Category;
import com.dlsc.preferencesfx.model.Group;
import com.dlsc.preferencesfx.model.Setting;
@ -10,10 +9,9 @@ import io.xpipe.app.fxcomps.impl.HorizontalComp;
import io.xpipe.app.fxcomps.impl.TextFieldComp;
import io.xpipe.app.util.TerminalHelper;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.process.CommandControl;
import io.xpipe.core.process.ShellDialects;
import javafx.beans.property.Property;
import io.xpipe.core.store.LocalStore;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@ -30,9 +28,6 @@ public class PasswordCategory extends AppPrefsCategory {
@SneakyThrows
public Category create() {
var ctr = Setting.class.getDeclaredConstructor(String.class, Element.class, Property.class);
ctr.setAccessible(true);
var testPasswordManagerValue = new SimpleStringProperty();
Runnable test = () -> {
prefs.save();

View file

@ -9,16 +9,18 @@ import com.dlsc.preferencesfx.model.Group;
import com.dlsc.preferencesfx.model.Setting;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.DesktopHelper;
import io.xpipe.app.util.LockChangeAlert;
import io.xpipe.app.util.OptionsBuilder;
import io.xpipe.core.util.XPipeInstallation;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import lombok.SneakyThrows;
import java.util.List;
import static io.xpipe.app.prefs.AppPrefs.group;
public class VaultCategory extends AppPrefsCategory {
@ -57,22 +59,25 @@ public class VaultCategory extends AppPrefsCategory {
@SneakyThrows
public Category create() {
var pro = true;
BooleanField enable = BooleanField.ofBooleanType(prefs.enableGitStorage)
.editable(pro)
.render(() -> {
return new CustomToggleControl();
});
StringField remote = StringField.ofStringType(prefs.storageGitRemote)
.editable(pro)
.render(() -> {
var c = new SimpleTextControl();
c.setPrefWidth(1000);
return c;
});
if (!pro) {
prefs.getProRequiredSettings().addAll(List.of(enable, remote));
}
var openDataDir = lazyNode(
"openDataDir", new OptionsBuilder().name("openDataDir").description("openDataDirDescription").addComp(
new ButtonComp(AppI18n.observable("openDataDirButton"), () -> {
DesktopHelper.browsePath(DataStorage.get().getDataDir());
})
).buildComp().padding(new Insets(25, 0, 0, 0)),
null);
return Category.of(
"vault",
group(
@ -84,7 +89,8 @@ public class VaultCategory extends AppPrefsCategory {
Setting.of(
"storageGitRemote",
remote,
prefs.storageGitRemote)),
prefs.storageGitRemote),
openDataDir),
group(
"storage",
STORAGE_DIR_FIXED

View file

@ -113,6 +113,10 @@ public abstract class DataStorage {
return dir.resolve("stores");
}
public Path getDataDir() {
return dir.resolve("data");
}
protected Path getStreamsDir() {
return dir.resolve("streams");
}
@ -174,6 +178,10 @@ public abstract class DataStorage {
}
public void updateCategory(DataStoreEntry entry, DataStoreCategory newCategory) {
if (getStoreCategoryIfPresent(entry.getUuid()).map(category -> category.equals(newCategory)).orElse(false)) {
return;
}
var children = getDeepStoreChildren(entry);
var toRemove = Stream.concat(Stream.of(entry), children.stream()).toArray(DataStoreEntry[]::new);
listeners.forEach(storageListener -> storageListener.onStoreRemove(toRemove));
@ -518,6 +526,7 @@ public abstract class DataStorage {
private List<DataStoreEntry> getHierarchy(DataStoreEntry entry) {
var es = new ArrayList<DataStoreEntry>();
es.add(entry);
DataStoreEntry current = entry;
while ((current = getDisplayParent(current).orElse(null)) != null) {

View file

@ -0,0 +1,41 @@
package io.xpipe.app.storage;
import io.xpipe.core.process.OsType;
import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.Value;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
@Value
@AllArgsConstructor
public class LocalFileReference {
public static LocalFileReference of(String s) {
if (s == null) {
return null;
}
var replaced = s.trim().replace("<DATA>", DataStorage.get().getDataDir().toString());
try {
return new LocalFileReference(Path.of(replaced).toString());
} catch (InvalidPathException ex) {
return new LocalFileReference(replaced);
}
}
@NonNull
String path;
public String serialize() {
var start = DataStorage.get().getDataDir();
try {
if (Path.of(path).startsWith(start)) {
return "<DATA>" + OsType.getLocal().getFileSystemSeparator() + start.relativize(Path.of(path));
}
} catch (InvalidPathException ignored) {}
return path.toString();
}
}

View file

@ -19,10 +19,28 @@ public class StorageJacksonModule extends SimpleModule {
public void setupModule(SetupContext context) {
addSerializer(DataStoreEntryRef.class, new DataStoreEntryRefSerializer());
addDeserializer(DataStoreEntryRef.class, new DataStoreEntryRefDeserializer());
addSerializer(LocalFileReference.class, new LocalFileReferenceSerializer());
addDeserializer(LocalFileReference.class, new LocalFileReferenceDeserializer());
context.addSerializers(_serializers);
context.addDeserializers(_deserializers);
}
public static class LocalFileReferenceSerializer extends JsonSerializer<LocalFileReference> {
@Override
public void serialize(LocalFileReference value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString(value.serialize());
}
}
public static class LocalFileReferenceDeserializer extends JsonDeserializer<LocalFileReference> {
@Override
public LocalFileReference deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return LocalFileReference.of(p.getValueAsString());
}
}
@SuppressWarnings("all")
public static class DataStoreEntryRefSerializer extends JsonSerializer<DataStoreEntryRef> {

View file

@ -5,6 +5,7 @@ import io.xpipe.core.store.LocalStore;
import io.xpipe.core.process.OsType;
import java.awt.*;
import java.nio.file.Files;
import java.nio.file.Path;
public class DesktopHelper {
@ -30,6 +31,10 @@ public class DesktopHelper {
return;
}
if (!Files.exists(file)) {
return;
}
ThreadHelper.runAsync(() -> {
try {
Desktop.getDesktop().open(file.toFile());

View file

@ -12,6 +12,9 @@ editorProgramDescription=The default text editor to use when editing any kind of
windowOpacity=Window opacity
windowOpacityDescription=Changes the window opacity to keep track of what is happening in the background.
useSystemFont=Use system font
openDataDir=Vault data directory
openDataDirButton=Open data directory
openDataDirDescription=If you want to sync additional files, such as SSH keys, across systems with your git repository, you can put them into the storage data directory. Any files referenced there will have their file paths automatically adapted on any synced system.
updates=Updates
passwordKey=Password key
selectAll=Select all

View file

@ -11,3 +11,7 @@ You can then use this repository in all XPipe application instances the same way
## Connection list
%s
## Additional data
You can sync arbitrary files between systems by putting them into the data subdirectory at `%%USERPROFILE%%\.xpipe\storage\data` or `~/.xpipe/storage/data`.

View file

@ -24,6 +24,10 @@ import java.util.List;
public class ScriptGroupStoreProvider implements DataStoreProvider {
public boolean isShareable() {
return true;
}
@Override
public Comp<?> customEntryComp(StoreSection sec, boolean preferLarge) {
ScriptGroupStore s = sec.getWrapper().getEntry().getStore().asNeeded();