mirror of
https://github.com/xpipe-io/xpipe.git
synced 2024-11-25 00:50:31 +00:00
Various fixes
This commit is contained in:
parent
6bf21edc68
commit
d9db3b1ef6
14 changed files with 138 additions and 43 deletions
|
@ -83,6 +83,9 @@ public class BrowserModel {
|
|||
savedState.save();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all files
|
||||
localTransfersStage.clear();
|
||||
}
|
||||
|
||||
public void finishChooser() {
|
||||
|
|
|
@ -186,7 +186,8 @@ public class BrowserTransferComp extends SimpleComp {
|
|||
return;
|
||||
}
|
||||
|
||||
model.clear();
|
||||
// Don't clear, it might be more convenient to keep the contents
|
||||
// model.clear();
|
||||
event.consume();
|
||||
});
|
||||
}),
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.xpipe.app.browser;
|
|||
|
||||
import io.xpipe.app.issue.ErrorEvent;
|
||||
import io.xpipe.app.util.BooleanScope;
|
||||
import io.xpipe.app.util.ShellTemp;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import io.xpipe.core.store.FileSystem;
|
||||
import javafx.beans.binding.Bindings;
|
||||
|
@ -27,8 +28,7 @@ import java.util.concurrent.Executors;
|
|||
@Value
|
||||
public class BrowserTransferModel {
|
||||
|
||||
private static final Path TEMP =
|
||||
FileUtils.getTempDirectory().toPath().resolve("xpipe").resolve("download");
|
||||
private static final Path TEMP = ShellTemp.getLocalTempDataDirectory("download");
|
||||
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = Executors.defaultThreadFactory().newThread(r);
|
||||
|
@ -41,15 +41,19 @@ public class BrowserTransferModel {
|
|||
BooleanProperty downloading = new SimpleBooleanProperty();
|
||||
BooleanProperty allDownloaded = new SimpleBooleanProperty();
|
||||
|
||||
public void clear() {
|
||||
private void cleanDirectory() {
|
||||
try (var ls = Files.list(TEMP)) {
|
||||
var list = ls.toList();
|
||||
for (Path path : list) {
|
||||
Files.delete(path);
|
||||
FileUtils.forceDelete(path.toFile());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ErrorEvent.fromThrowable(e).handle();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
cleanDirectory();
|
||||
items.clear();
|
||||
}
|
||||
|
||||
|
@ -116,7 +120,7 @@ public class BrowserTransferModel {
|
|||
try {
|
||||
try (var b = new BooleanScope(downloading).start()) {
|
||||
FileSystemHelper.dropFilesInto(
|
||||
FileSystemHelper.getLocal(TEMP), List.of(item.getFileEntry()), true, progress -> {
|
||||
FileSystemHelper.getLocal(TEMP), List.of(item.getFileEntry()), true, false, progress -> {
|
||||
item.getProgress().setValue(progress);
|
||||
item.getOpenFileSystemModel().getProgress().setValue(progress);
|
||||
});
|
||||
|
|
|
@ -153,7 +153,7 @@ public class FileSystemHelper {
|
|||
}
|
||||
|
||||
public static void dropLocalFilesInto(
|
||||
FileSystem.FileEntry entry, List<Path> files, Consumer<BrowserTransferProgress> progress) throws Exception {
|
||||
FileSystem.FileEntry entry, List<Path> files, Consumer<BrowserTransferProgress> progress, boolean checkConflicts) throws Exception {
|
||||
var entries = files.stream()
|
||||
.map(path -> {
|
||||
try {
|
||||
|
@ -163,7 +163,7 @@ public class FileSystemHelper {
|
|||
}
|
||||
})
|
||||
.toList();
|
||||
dropFilesInto(entry, entries, false, progress);
|
||||
dropFilesInto(entry, entries, false, checkConflicts, progress);
|
||||
}
|
||||
|
||||
public static void delete(List<FileSystem.FileEntry> files) {
|
||||
|
@ -184,6 +184,7 @@ public class FileSystemHelper {
|
|||
FileSystem.FileEntry target,
|
||||
List<FileSystem.FileEntry> files,
|
||||
boolean explicitCopy,
|
||||
boolean checkConflicts,
|
||||
Consumer<BrowserTransferProgress> progress)
|
||||
throws Exception {
|
||||
if (files.isEmpty()) {
|
||||
|
@ -201,10 +202,10 @@ public class FileSystemHelper {
|
|||
AtomicReference<BrowserAlerts.FileConflictChoice> lastConflictChoice = new AtomicReference<>();
|
||||
for (var file : files) {
|
||||
if (file.getFileSystem().equals(target.getFileSystem())) {
|
||||
dropFileAcrossSameFileSystem(target, file, explicitCopy, lastConflictChoice, files.size() > 1);
|
||||
dropFileAcrossSameFileSystem(target, file, explicitCopy, lastConflictChoice, files.size() > 1, checkConflicts);
|
||||
progress.accept(BrowserTransferProgress.finished(file.getName(), file.getSize()));
|
||||
} else {
|
||||
dropFileAcrossFileSystems(target, file, progress, lastConflictChoice, files.size() > 1);
|
||||
dropFileAcrossFileSystems(target, file, progress, lastConflictChoice, files.size() > 1, checkConflicts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +215,8 @@ public class FileSystemHelper {
|
|||
FileSystem.FileEntry source,
|
||||
boolean explicitCopy,
|
||||
AtomicReference<BrowserAlerts.FileConflictChoice> lastConflictChoice,
|
||||
boolean multiple)
|
||||
boolean multiple,
|
||||
boolean checkConflicts)
|
||||
throws Exception {
|
||||
// Prevent dropping directory into itself
|
||||
if (source.getPath().equals(target.getPath())) {
|
||||
|
@ -233,7 +235,7 @@ public class FileSystemHelper {
|
|||
new IllegalArgumentException("Target directory " + targetFile + " does already exist"));
|
||||
}
|
||||
|
||||
if (!handleChoice(lastConflictChoice, target.getFileSystem(), targetFile, multiple)) {
|
||||
if (checkConflicts && !handleChoice(lastConflictChoice, target.getFileSystem(), targetFile, multiple)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -249,7 +251,8 @@ public class FileSystemHelper {
|
|||
FileSystem.FileEntry source,
|
||||
Consumer<BrowserTransferProgress> progress,
|
||||
AtomicReference<BrowserAlerts.FileConflictChoice> lastConflictChoice,
|
||||
boolean multiple)
|
||||
boolean multiple,
|
||||
boolean checkConflicts)
|
||||
throws Exception {
|
||||
if (target.getKind() != FileKind.DIRECTORY) {
|
||||
throw new IllegalStateException("Target " + target.getPath() + " is not a directory");
|
||||
|
@ -272,7 +275,9 @@ public class FileSystemHelper {
|
|||
List<FileSystem.FileEntry> list = source.getFileSystem().listFilesRecursively(source.getPath());
|
||||
list.forEach(fileEntry -> {
|
||||
flatFiles.put(fileEntry, FileNames.toUnix(FileNames.relativize(baseRelative, fileEntry.getPath())));
|
||||
totalSize.addAndGet(fileEntry.getSize());
|
||||
if (fileEntry.getKind() == FileKind.FILE) {
|
||||
totalSize.addAndGet(fileEntry.getSize());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
flatFiles.put(source, FileNames.getFileName(source.getPath()));
|
||||
|
@ -290,7 +295,7 @@ public class FileSystemHelper {
|
|||
if (sourceFile.getKind() == FileKind.DIRECTORY) {
|
||||
target.getFileSystem().mkdirs(targetFile);
|
||||
} else if (sourceFile.getKind() == FileKind.FILE) {
|
||||
if (!handleChoice(
|
||||
if (checkConflicts && !handleChoice(
|
||||
lastConflictChoice, target.getFileSystem(), targetFile, multiple || flatFiles.size() > 1)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -299,22 +304,29 @@ public class FileSystemHelper {
|
|||
OutputStream outputStream = null;
|
||||
try {
|
||||
inputStream = sourceFile.getFileSystem().openInput(sourceFile.getPath());
|
||||
outputStream = target.getFileSystem().openOutput(targetFile, source.getSize());
|
||||
transfer(source, inputStream, outputStream, transferred, totalSize, progress);
|
||||
outputStream = target.getFileSystem().openOutput(targetFile, sourceFile.getSize());
|
||||
transferFile(sourceFile, inputStream, outputStream, transferred, totalSize, progress);
|
||||
inputStream.transferTo(OutputStream.nullOutputStream());
|
||||
} catch (Exception ex) {
|
||||
// Mark progress as finished to reset any progress display
|
||||
progress.accept(BrowserTransferProgress.finished(sourceFile.getName(), transferred.get()));
|
||||
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (Exception om) {
|
||||
ErrorEvent.fromThrowable(om).handle();
|
||||
// This is expected as the process control has to be killed
|
||||
// When calling close, it will throw an exception when it has to kill
|
||||
// ErrorEvent.fromThrowable(om).handle();
|
||||
}
|
||||
}
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (Exception om) {
|
||||
ErrorEvent.fromThrowable(om).handle();
|
||||
// This is expected as the process control has to be killed
|
||||
// When calling close, it will throw an exception when it has to kill
|
||||
// ErrorEvent.fromThrowable(om).handle();
|
||||
}
|
||||
}
|
||||
throw ex;
|
||||
|
@ -385,21 +397,24 @@ public class FileSystemHelper {
|
|||
return true;
|
||||
}
|
||||
|
||||
private static void transfer(
|
||||
FileSystem.FileEntry source,
|
||||
private static void transferFile(
|
||||
FileSystem.FileEntry sourceFile,
|
||||
InputStream inputStream,
|
||||
OutputStream outputStream,
|
||||
AtomicLong transferred,
|
||||
AtomicLong total,
|
||||
Consumer<BrowserTransferProgress> progress)
|
||||
throws IOException {
|
||||
var bs = (int) Math.min(DEFAULT_BUFFER_SIZE, source.getSize());
|
||||
// Initialize progress immediately prior to reading anything
|
||||
progress.accept(new BrowserTransferProgress(sourceFile.getName(), transferred.get(), total.get()));
|
||||
|
||||
var bs = (int) Math.min(DEFAULT_BUFFER_SIZE, sourceFile.getSize());
|
||||
byte[] buffer = new byte[bs];
|
||||
int read;
|
||||
while ((read = inputStream.read(buffer, 0, bs)) >= 0) {
|
||||
while ((read = inputStream.read(buffer, 0, bs)) > 0) {
|
||||
outputStream.write(buffer, 0, read);
|
||||
transferred.addAndGet(read);
|
||||
progress.accept(new BrowserTransferProgress(source.getName(), transferred.get(), total.get()));
|
||||
progress.accept(new BrowserTransferProgress(sourceFile.getName(), transferred.get(), total.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,7 +269,7 @@ public final class OpenFileSystemModel {
|
|||
}
|
||||
|
||||
startIfNeeded();
|
||||
FileSystemHelper.dropLocalFilesInto(entry, files, progress::setValue);
|
||||
FileSystemHelper.dropLocalFilesInto(entry, files, progress::setValue, true);
|
||||
refreshSync();
|
||||
});
|
||||
});
|
||||
|
@ -289,7 +289,7 @@ public final class OpenFileSystemModel {
|
|||
}
|
||||
|
||||
startIfNeeded();
|
||||
FileSystemHelper.dropFilesInto(target, files, explicitCopy, browserTransferProgress -> {
|
||||
FileSystemHelper.dropFilesInto(target, files, explicitCopy, true, browserTransferProgress -> {
|
||||
progress.setValue(browserTransferProgress);
|
||||
});
|
||||
refreshSync();
|
||||
|
|
|
@ -23,8 +23,7 @@ import java.util.function.Consumer;
|
|||
|
||||
public class FileBridge {
|
||||
|
||||
private static final Path TEMP =
|
||||
FileUtils.getTempDirectory().toPath().resolve("xpipe").resolve("bridge");
|
||||
private static final Path TEMP = ShellTemp.getLocalTempDataDirectory("bridge");
|
||||
private static FileBridge INSTANCE;
|
||||
private final Set<Entry> openEntries = new HashSet<>();
|
||||
|
||||
|
|
73
app/src/main/java/io/xpipe/app/util/ShellTemp.java
Normal file
73
app/src/main/java/io/xpipe/app/util/ShellTemp.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
package io.xpipe.app.util;
|
||||
|
||||
import io.xpipe.core.process.OsType;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
import io.xpipe.core.store.FileNames;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ShellTemp {
|
||||
|
||||
public static Path getLocalTempDataDirectory(String sub) {
|
||||
var temp = FileUtils.getTempDirectory().toPath().resolve("xpipe");
|
||||
if (OsType.getLocal().equals(OsType.LINUX)) {
|
||||
var user = System.getenv("USER");
|
||||
temp = temp.resolve(user != null ? user : "user");
|
||||
}
|
||||
return temp.resolve(sub);
|
||||
}
|
||||
|
||||
public static String getUserSpecificTempDataDirectory(ShellControl proc, String sub) {
|
||||
if (OsType.getLocal().equals(OsType.LINUX) || OsType.getLocal().equals(OsType.MACOS)) {
|
||||
var user = System.getenv("USER");
|
||||
return FileNames.join("/tmp", "xpipe", user, sub);
|
||||
}
|
||||
var temp = proc.getSystemTemporaryDirectory();
|
||||
return FileNames.join(temp, "xpipe", sub);
|
||||
}
|
||||
|
||||
public static void checkTempDirectory(ShellControl proc) throws Exception {
|
||||
var d = proc.getShellDialect();
|
||||
|
||||
var systemTemp = proc.getOsType().getTempDirectory(proc);
|
||||
if (!d.directoryExists(proc, systemTemp).executeAndCheck() || !checkDirectoryPermissions(proc, systemTemp)) {
|
||||
throw new IOException("No permissions to access %s".formatted(systemTemp));
|
||||
}
|
||||
|
||||
var home = proc.getOsType().getHomeDirectory(proc);
|
||||
if (!d.directoryExists(proc, home).executeAndCheck() || !checkDirectoryPermissions(proc, home)) {
|
||||
throw new IOException("No permissions to access %s".formatted(home));
|
||||
}
|
||||
|
||||
// Always delete legacy directory and do not care whether it partially fails
|
||||
// This system xpipe temp directory might contain other files on the local machine, so only clear the exec
|
||||
d.deleteFileOrDirectory(proc, FileNames.join(systemTemp, "xpipe", "exec")).executeAndCheck();
|
||||
d.deleteFileOrDirectory(proc, FileNames.join(home, ".xpipe", "temp")).executeAndCheck();
|
||||
d.deleteFileOrDirectory(proc, FileNames.join(home, ".xpipe", "system_id")).executeAndCheck();
|
||||
}
|
||||
|
||||
private static boolean checkDirectoryPermissions(ShellControl proc, String dir) throws Exception {
|
||||
if (proc.getOsType().equals(OsType.WINDOWS)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var d = proc.getShellDialect();
|
||||
return proc.executeSimpleBooleanCommand("test -r %s && test -w %s && test -x %s"
|
||||
.formatted(d.fileArgument(dir), d.fileArgument(dir), d.fileArgument(dir)));
|
||||
}
|
||||
|
||||
public static String getSubDirectory(ShellControl proc, String... sub) throws Exception {
|
||||
var base = proc.getSystemTemporaryDirectory();
|
||||
var arr = Stream.concat(Stream.of(base), Arrays.stream(sub)).toArray(String[]::new);
|
||||
var dir = FileNames.join(arr);
|
||||
|
||||
// We assume that this directory does not exist yet and therefore don't perform any checks
|
||||
proc.getShellDialect().prepareUserTempDirectory(proc, dir).execute();
|
||||
|
||||
return dir;
|
||||
}
|
||||
}
|
10
dist/changelogs/8.0.md
vendored
10
dist/changelogs/8.0.md
vendored
|
@ -1,10 +1,8 @@
|
|||
This is update is primarily focused on internal reworks. It includes many changes that are necessary going forward to allow for many future features to come. These new implementations can take into account everything learned so far and are more robust, especially when considering the long-term timeline.
|
||||
This update includes many changes that are necessary going forward to allow for many future features to come. These new implementations can take into account everything learned so far and are more robust, especially when considering the long-term timeline.
|
||||
|
||||
The versioning scheme has also been changed to simplify version numbers. So we are going straight from 1.7 to 8.0!
|
||||
|
||||
If you're interested, make sure to check out the public test build repository at https://github.com/xpipe-io/xpipe-ptb to download an early version. The regular releases and PTB releases are designed to not interfere with each other and can therefore be installed and used side by side. They work on separate configuration data.
|
||||
|
||||
You can help the development effort by testing the PTB version and reporting any issues that you can find.
|
||||
Note that on Windows the automatic updater still has a few issues if you are upgrading from 1.7.16. If the update fails, you can still install 8.0 manually by downloading and installing it.
|
||||
|
||||
## New terminal launcher
|
||||
|
||||
|
@ -22,7 +20,7 @@ The git installation on Windows comes with its own posix environment, which some
|
|||
|
||||
## File browser improvements
|
||||
|
||||
The file browser has been reworked in terms of performance and reliability. Transferring many files should now be faster. Any errors that can occur are now handled better.
|
||||
The file browser has been reworked in terms of performance and reliability. Any errors that can occur are now handled better.
|
||||
|
||||
In terms of the interface, there is also now a progress indicator for files being transferred. For any file conflicts, there is now a new dialog to choose how to resolve any conflict when copying or moving files.
|
||||
|
||||
|
@ -41,6 +39,8 @@ Furthermore, you can also choose to use any namespace you want. This is useful i
|
|||
|
||||
This update comes with a complete rework of the settings menu. Many options have been added and existing ones have been improved, with a focus on providing more control over security settings. Make sure to give them a read to discover new options.
|
||||
|
||||
There has been a big focus on providing finer-grained control over security settings, which can be especially useful in enterprise contexts.
|
||||
|
||||
## Per-Vault settings
|
||||
|
||||
Previously all settings were stored on a per-system basis. This caused some problems with git vaults, as all relevant settings that should persist across systems were not synced. From now on, all options that should be the same on all synced systems are automatically included in the git vault.
|
||||
|
|
|
@ -4,6 +4,7 @@ import io.xpipe.app.issue.ErrorEvent;
|
|||
import io.xpipe.app.storage.DataStorage;
|
||||
import io.xpipe.app.storage.DataStoreEntry;
|
||||
import io.xpipe.app.storage.DataStoreEntryRef;
|
||||
import io.xpipe.app.util.ShellTemp;
|
||||
import io.xpipe.app.util.Validators;
|
||||
import io.xpipe.core.process.ScriptSnippet;
|
||||
import io.xpipe.core.process.ShellControl;
|
||||
|
@ -113,8 +114,7 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
|
|||
.mapToInt(value ->
|
||||
value.get().getName().hashCode() + value.getStore().hashCode())
|
||||
.sum();
|
||||
var targetDir =
|
||||
FileNames.join(proc.getSystemTemporaryDirectory(), "xpipe", "scripts", proc.getShellDialect().getId());
|
||||
var targetDir = FileNames.join(ShellTemp.getUserSpecificTempDataDirectory(proc,"scripts"), proc.getShellDialect().getId());
|
||||
var hashFile = FileNames.join(targetDir, "hash");
|
||||
var d = proc.getShellDialect();
|
||||
if (d.createFileExistsCommand(proc, hashFile).executeAndCheck()) {
|
||||
|
|
|
@ -3,7 +3,7 @@ IF %ERRORLEVEL%==0 (
|
|||
exit /b 0
|
||||
)
|
||||
|
||||
SET "PATH=%PATH%;%USERPROFILE%\.xpipe\scriptdata\clink"
|
||||
SET "PATH=%PATH%;%TEMP%\xpipe\scriptdata\clink"
|
||||
WHERE clink >NUL 2>NUL
|
||||
IF %ERRORLEVEL%==0 (
|
||||
exit /b 0
|
||||
|
@ -16,4 +16,4 @@ if ($defaultCreds) {^
|
|||
$downloader.Credentials = $defaultCreds^
|
||||
}^
|
||||
$downloader.DownloadFile("https://github.com/chrisant996/clink/releases/download/v1.5.13/clink.1.5.13.290610.zip", "$env:TEMP\clink.zip");^
|
||||
Expand-Archive -Force -LiteralPath "$env:TEMP\clink.zip" -DestinationPath "$env:USERPROFILE\.xpipe\scriptdata\clink"; | powershell -NoLogo >NUL
|
||||
Expand-Archive -Force -LiteralPath "$env:TEMP\clink.zip" -DestinationPath "$env:TEMP\xpipe\scriptdata\clink"; | powershell -NoLogo >NUL
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
dir=~/.xpipe/scriptdata/starship
|
||||
dir="/tmp/xpipe/$USER/scriptdata/starship"
|
||||
export PATH="$PATH:$dir"
|
||||
which starship > /dev/null 2>&1
|
||||
if [ "$?" != 0 ]; then
|
||||
|
|
|
@ -4,6 +4,6 @@ IF NOT %ERRORLEVEL%==0 (
|
|||
SET "PATH=%PATH%;C:\\Program Files\\starship\\bin"
|
||||
)
|
||||
|
||||
MKDIR "%USERPROFILE%\\.xpipe\\scriptdata\\starship" >NUL 2>NUL
|
||||
echo load(io.popen('starship init cmd'):read("*a"))() > "%USERPROFILE%\\.xpipe\\scriptdata\\starship\\starship.lua"
|
||||
clink inject --quiet --profile "%USERPROFILE%\\.xpipe\\scriptdata\\starship"
|
||||
MKDIR "%TEMP%\\xpipe\\scriptdata\\starship" >NUL 2>NUL
|
||||
echo load(io.popen('starship init cmd'):read("*a"))() > "%TEMP%\\xpipe\\scriptdata\\starship\\starship.lua"
|
||||
clink inject --quiet --profile "%TEMP%\\xpipe\\scriptdata\\starship"
|
|
@ -1,4 +1,4 @@
|
|||
set dir ~/.xpipe/scriptdata/starship
|
||||
set dir "/tmp/xpipe/$USER/scriptdata/starship"
|
||||
export PATH="$PATH:$dir"
|
||||
which starship > /dev/null 2>&1
|
||||
if [ $status != 0 ]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
dir=~/.xpipe/scriptdata/starship
|
||||
dir="/tmp/xpipe/$USER/scriptdata/starship"
|
||||
export PATH="$PATH:$dir"
|
||||
which starship > /dev/null 2>&1
|
||||
if [ "$?" != 0 ]; then
|
||||
|
|
Loading…
Reference in a new issue