diff --git a/app/src/main/java/io/xeres/app/api/controller/notification/NotificationController.java b/app/src/main/java/io/xeres/app/api/controller/notification/NotificationController.java index 4bc6ffd6b..62107d7b7 100644 --- a/app/src/main/java/io/xeres/app/api/controller/notification/NotificationController.java +++ b/app/src/main/java/io/xeres/app/api/controller/notification/NotificationController.java @@ -23,6 +23,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import io.xeres.app.service.notification.file.FileNotificationService; import io.xeres.app.service.notification.forum.ForumNotificationService; import io.xeres.app.service.notification.status.StatusNotificationService; import org.springframework.http.MediaType; @@ -40,11 +41,13 @@ public class NotificationController { private final StatusNotificationService statusNotificationService; private final ForumNotificationService forumNotificationService; + private final FileNotificationService fileNotificationService; - public NotificationController(StatusNotificationService statusNotificationService, ForumNotificationService forumNotificationService) + public NotificationController(StatusNotificationService statusNotificationService, ForumNotificationService forumNotificationService, FileNotificationService fileNotificationService) { this.statusNotificationService = statusNotificationService; this.forumNotificationService = forumNotificationService; + this.fileNotificationService = fileNotificationService; } @GetMapping("/status") @@ -62,4 +65,12 @@ public SseEmitter setupForumNotification() { return forumNotificationService.addClient(); } + + @GetMapping("/file") + @Operation(summary = "Subscribe to file notifications") + @ApiResponse(responseCode = "200", description = "Request completed successfully") + public SseEmitter setupFileNotification() + { + return fileNotificationService.addClient(); + } } diff --git a/app/src/main/java/io/xeres/app/service/file/FileService.java b/app/src/main/java/io/xeres/app/service/file/FileService.java index d1263c766..c42ff17ab 100644 --- a/app/src/main/java/io/xeres/app/service/file/FileService.java +++ b/app/src/main/java/io/xeres/app/service/file/FileService.java @@ -108,7 +108,7 @@ public void checkForSharesToScan() log.debug("Scanning: {}", share); share.setLastScanned(now); shareRepository.save(share); - scanShare(share.getFile()); + scanShare(share); }); } @@ -170,10 +170,12 @@ private List getFullPath(File file) return tree; } - void scanShare(File directory) + void scanShare(Share share) { try { + fileNotificationService.startScanning(share); + File directory = share.getFile(); var directoryPath = getFilePath(directory); Files.walkFileTree(directoryPath, new TrackingFileVisitor(fileRepository, directory) { @@ -247,6 +249,10 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) { throw new RuntimeException(e); } + finally + { + fileNotificationService.stopScanning(); + } } private Path getFilePath(File file) @@ -314,6 +320,7 @@ Sha1Sum calculateFileHash(Path path) log.debug("Calculating file hash of file {}", path); try (var fc = FileChannel.open(path, StandardOpenOption.READ)) // ExtendedOpenOption.DIRECT is useless for memory mapped files { + fileNotificationService.setScanningFile(path); var md = new Sha1MessageDigest(); var size = fc.size(); @@ -339,6 +346,7 @@ Sha1Sum calculateFileHash(Path path) catch (IOException e) { log.warn("Error while trying to compute hash of file " + path, e); + fileNotificationService.setScanningFile(null); return null; } } diff --git a/app/src/main/java/io/xeres/app/service/notification/file/FileNotificationService.java b/app/src/main/java/io/xeres/app/service/notification/file/FileNotificationService.java index d439d2557..edf2cd59c 100644 --- a/app/src/main/java/io/xeres/app/service/notification/file/FileNotificationService.java +++ b/app/src/main/java/io/xeres/app/service/notification/file/FileNotificationService.java @@ -19,16 +19,42 @@ package io.xeres.app.service.notification.file; +import io.xeres.app.database.model.share.Share; import io.xeres.app.service.notification.NotificationService; import io.xeres.common.rest.notification.Notification; +import io.xeres.common.rest.notification.file.FileNotification; import org.springframework.stereotype.Service; +import java.nio.file.Path; + @Service public class FileNotificationService extends NotificationService { + private String shareName; + private String scannedFile; + @Override protected Notification createNotification() { - return null; // XXX + return new FileNotification(shareName, scannedFile); + } + + public void startScanning(Share share) + { + this.shareName = share.getName(); + sendNotification(); + } + + public void setScanningFile(Path scannedFile) + { + this.scannedFile = scannedFile != null ? scannedFile.toString() : null; + sendNotification(); + } + + public void stopScanning() + { + this.shareName = null; + this.scannedFile = null; + sendNotification(); } } diff --git a/common/src/main/java/io/xeres/common/rest/notification/file/FileNotification.java b/common/src/main/java/io/xeres/common/rest/notification/file/FileNotification.java new file mode 100644 index 000000000..581698410 --- /dev/null +++ b/common/src/main/java/io/xeres/common/rest/notification/file/FileNotification.java @@ -0,0 +1,7 @@ +package io.xeres.common.rest.notification.file; + +import io.xeres.common.rest.notification.Notification; + +public record FileNotification(String shareName, String scannedFile) implements Notification +{ +} diff --git a/ui/src/main/java/io/xeres/ui/client/NotificationClient.java b/ui/src/main/java/io/xeres/ui/client/NotificationClient.java index 9ba0632cf..cb9268720 100644 --- a/ui/src/main/java/io/xeres/ui/client/NotificationClient.java +++ b/ui/src/main/java/io/xeres/ui/client/NotificationClient.java @@ -19,6 +19,7 @@ package io.xeres.ui.client; +import io.xeres.common.rest.notification.file.FileNotification; import io.xeres.common.rest.notification.forum.ForumNotification; import io.xeres.common.rest.notification.status.StatusNotification; import io.xeres.ui.JavaFxApplication; @@ -70,4 +71,14 @@ public Flux> getForumNotifications() { }); } + + public Flux> getFileNotifications() + { + return webClient.get() + .uri("/file") + .retrieve() + .bodyToFlux(new ParameterizedTypeReference<>() + { + }); + } } diff --git a/ui/src/main/java/io/xeres/ui/controller/MainWindowController.java b/ui/src/main/java/io/xeres/ui/controller/MainWindowController.java index d10577fee..e8f103651 100644 --- a/ui/src/main/java/io/xeres/ui/controller/MainWindowController.java +++ b/ui/src/main/java/io/xeres/ui/controller/MainWindowController.java @@ -39,10 +39,7 @@ import io.xeres.ui.support.window.WindowManager; import javafx.application.Platform; import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.Menu; -import javafx.scene.control.MenuItem; +import javafx.scene.control.*; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.FileChooser; @@ -150,19 +147,22 @@ public class MainWindowController implements WindowController private Button showQrCodeButton; @FXML - public Button addFriendButton; + private Button addFriendButton; @FXML - public Button webHelpButton; + private Button webHelpButton; @FXML - public Label numberOfConnections; + private Label numberOfConnections; @FXML - public LedControl natStatus; + private LedControl natStatus; @FXML - public LedControl dhtStatus; + private LedControl dhtStatus; + + @FXML + private ProgressIndicator hashingStatus; private final ChatViewController chatViewController; @@ -177,7 +177,8 @@ public class MainWindowController implements WindowController private int currentUsers; private int totalUsers; - private Disposable notificationDisposable; + private Disposable statusNotificationDisposable; + private Disposable fileNotificationDisposable; public MainWindowController(ChatViewController chatViewController, LocationClient locationClient, TrayService trayService, WindowManager windowManager, Environment environment, IdentityClient identityClient, ConfigClient configClient, NotificationClient notificationClient, ResourceBundle bundle) { @@ -265,7 +266,7 @@ public void initialize() Platform.exit(); }); - setupStatusNotifications(); + setupNotifications(); trayService.addSystemTray(); @@ -307,14 +308,13 @@ private void openUrl(String url) JavaFxApplication.openUrl(url); } - private void setupStatusNotifications() + private void setupNotifications() { // Apparently the LED is not happy if we don't turn it on first here. natStatus.setState(true); dhtStatus.setState(true); - notificationDisposable = notificationClient.getStatusNotifications() - .doOnComplete(() -> log.debug("Notification connection closed")) + statusNotificationDisposable = notificationClient.getStatusNotifications() .doOnError(UiUtils::showAlertError) .doOnNext(sse -> Platform.runLater(() -> { if (sse.data() != null) @@ -325,6 +325,24 @@ private void setupStatusNotifications() } })) .subscribe(); + + fileNotificationDisposable = notificationClient.getFileNotifications() + .doOnError(UiUtils::showAlertError) + .doOnNext(sse -> Platform.runLater(() -> { + if (sse.data() != null) + { + hashingStatus.setVisible(sse.data().shareName() != null); + if (sse.data().scannedFile() != null) + { + TooltipUtils.install(hashingStatus, sse.data().shareName() + ": hashing " + sse.data().scannedFile() + "..."); + } + else + { + TooltipUtils.install(hashingStatus, null); + } + } + })) + .subscribe(); } private void setUserCount(Integer newCurrentUsers, Integer newTotalUsers) @@ -400,9 +418,14 @@ private void setDhtInfo(DhtInfo newDhtInfo) @EventListener public void onApplicationEvent(ContextClosedEvent ignored) { - if (notificationDisposable != null && !notificationDisposable.isDisposed()) + if (statusNotificationDisposable != null && !statusNotificationDisposable.isDisposed()) + { + statusNotificationDisposable.dispose(); + } + + if (fileNotificationDisposable != null && !fileNotificationDisposable.isDisposed()) { - notificationDisposable.dispose(); + fileNotificationDisposable.dispose(); } } } diff --git a/ui/src/main/resources/view/main.fxml b/ui/src/main/resources/view/main.fxml index 20ea79425..b3bc4b1d0 100644 --- a/ui/src/main/resources/view/main.fxml +++ b/ui/src/main/resources/view/main.fxml @@ -19,6 +19,7 @@ ~ along with Xeres. If not, see . --> + @@ -244,5 +245,7 @@ + +