mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-21 23:20:23 +00:00
Improve git storage functionality
This commit is contained in:
parent
f5eebdae73
commit
aa00cd6521
11 changed files with 124 additions and 37 deletions
|
@ -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 -> {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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> {
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue