From 68f488658a96820e0075b33a74fd57a190195df8 Mon Sep 17 00:00:00 2001 From: kozorukov Date: Wed, 13 Mar 2024 17:05:10 +0300 Subject: [PATCH] UI --- .../client/feign/dialog/DialogClient.java | 16 ++++ .../client/feign/dialog/DialogMessageDto.java | 10 +++ .../ui/controller/DialogSceneController.java | 80 +++++++++++++++++++ .../SendMessageSceneController.java | 8 ++ .../ui/dialog/DialogWithOtherUserDialog.java | 30 +++++++ .../desktop/service/dialog/DialogMessage.java | 22 +++++ .../desktop/service/dialog/DialogService.java | 10 +++ .../service/dialog/DialogServiceImpl.java | 73 +++++++++++++++++ .../infrastructure/ui/DialogScene.fxml | 36 +++++++++ .../infrastructure/ui/SendMessageScene.fxml | 5 ++ 10 files changed, 290 insertions(+) create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogClient.java create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogMessageDto.java create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/DialogSceneController.java create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/dialog/DialogWithOtherUserDialog.java create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogMessage.java create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogService.java create mode 100644 crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogServiceImpl.java create mode 100644 crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/DialogScene.fxml diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogClient.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogClient.java new file mode 100644 index 0000000..2d5b654 --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogClient.java @@ -0,0 +1,16 @@ +package cryptomessenger.desktop.infrastructure.client.feign.dialog; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.UUID; + +@FeignClient(name = "dialog-client", url = "${app.server-base-url}") +public interface DialogClient { + + @GetMapping("/dialogs/{myId}/{otherUserId}") + Page getDialog(@PathVariable UUID myId, @PathVariable UUID otherUserId, Pageable pageable); +} diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogMessageDto.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogMessageDto.java new file mode 100644 index 0000000..1c6fbd0 --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/feign/dialog/DialogMessageDto.java @@ -0,0 +1,10 @@ +package cryptomessenger.desktop.infrastructure.client.feign.dialog; + +import cryptomessenger.desktop.infrastructure.client.feign.message.MessageDto; +import lombok.Data; + +@Data +public class DialogMessageDto { + private final MessageDto message; + private final boolean isMine; +} diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/DialogSceneController.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/DialogSceneController.java new file mode 100644 index 0000000..5fc261b --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/DialogSceneController.java @@ -0,0 +1,80 @@ +package cryptomessenger.desktop.infrastructure.ui.controller; + +import cryptomessenger.desktop.infrastructure.ui.Refreshable; +import cryptomessenger.desktop.service.dialog.DialogMessage; +import cryptomessenger.desktop.service.dialog.DialogService; +import cryptomessenger.desktop.service.message.MessageService; +import cryptomessenger.desktop.utility.ThreadFactories; +import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.event.ActionEvent; +import javafx.scene.control.Pagination; +import javafx.scene.control.TableView; +import javafx.scene.control.TextArea; +import javafx.scene.control.cell.PropertyValueFactory; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +import java.net.URL; +import java.util.ResourceBundle; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Component +@RequiredArgsConstructor +public class DialogSceneController implements Refreshable { + + private final MessageService messageService; + private final DialogService dialogService; + + private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, ThreadFactories.daemon()); + + public TextArea messageField; + + public String username; + public TableView messagesTable; + public Pagination messagesTablePagination; + + @Override + public void initialize(URL location, ResourceBundle resources) { + username = resources.getString("otherUsername"); + refresh(); + configureAutoRefresh(); + } + + private void configureAutoRefresh() { + executor.scheduleWithFixedDelay(() -> Platform.runLater(this::refreshMessagesTable), 1, 1, TimeUnit.SECONDS); + } + + @Override + public void refresh() { + refreshMessagesTable(); + } + + private void refreshMessagesTable() { + configureTable(); + } + + public void onSend(ActionEvent actionEvent) { + messageService.send(username, messageField.getText()); + refreshMessagesTable(); + } + + private void configureTable() { + configureColumns(); + var messages = dialogService.getMessages(username, Pageable.ofSize(25).withPage(messagesTablePagination.getCurrentPageIndex())); + messagesTablePagination.setPageCount(messages.getTotalPages()); + messagesTablePagination.setVisible(messages.getTotalPages() > 0); + + messagesTable.setItems(FXCollections.observableArrayList(messages.getContent())); + } + + private void configureColumns() { + var columns = messagesTable.getColumns(); + columns.get(0).setCellValueFactory(new PropertyValueFactory<>("sentAt")); + columns.get(1).setCellValueFactory(new PropertyValueFactory<>("inbox")); + columns.get(2).setCellValueFactory(new PropertyValueFactory<>("outbox")); + } +} diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/SendMessageSceneController.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/SendMessageSceneController.java index ae791c7..9ac085c 100644 --- a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/SendMessageSceneController.java +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/SendMessageSceneController.java @@ -1,6 +1,7 @@ package cryptomessenger.desktop.infrastructure.ui.controller; import cryptomessenger.desktop.infrastructure.localstorage.LocalStorage; +import cryptomessenger.desktop.infrastructure.ui.dialog.DialogWithOtherUserDialog; import cryptomessenger.desktop.service.LocalStorageKeys; import cryptomessenger.desktop.service.message.MessageService; import javafx.event.ActionEvent; @@ -21,6 +22,7 @@ public class SendMessageSceneController implements Initializable { private final MessageService messageService; private final LocalStorage localStorage; + private final DialogWithOtherUserDialog dialogWithOtherUserDialog; public ComboBox receiverSelector; public TextArea messageField; @@ -49,4 +51,10 @@ public void onSend(ActionEvent actionEvent) { public void onCancel(ActionEvent actionEvent) { onCancel.run(); } + + public void onOpenDialog(ActionEvent actionEvent) { + var otherUser = receiverSelector.getValue(); + dialogWithOtherUserDialog.show(otherUser); + onCancel.run(); + } } diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/dialog/DialogWithOtherUserDialog.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/dialog/DialogWithOtherUserDialog.java new file mode 100644 index 0000000..31481a2 --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/dialog/DialogWithOtherUserDialog.java @@ -0,0 +1,30 @@ +package cryptomessenger.desktop.infrastructure.ui.dialog; + +import cryptomessenger.desktop.infrastructure.ui.SceneLoader; +import cryptomessenger.desktop.infrastructure.ui.SceneProperties; +import javafx.scene.image.Image; +import javafx.stage.Modality; +import javafx.stage.Stage; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import static java.util.Map.entry; + +@Component +@RequiredArgsConstructor +public class DialogWithOtherUserDialog { + + private final SceneLoader sceneLoader; + + public void show(String username) { + var stage = new Stage(); + stage.setTitle("Dialog with " + username); + stage.getIcons().add(new Image(getClass().getResourceAsStream("/graphics/lock.png"))); + stage.initModality(Modality.APPLICATION_MODAL); + var sceneProperties = SceneProperties.of( + entry("otherUsername", username == null ? "" : username) + ); + stage.setScene(sceneLoader.load("DialogScene", sceneProperties)); + stage.show(); + } +} diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogMessage.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogMessage.java new file mode 100644 index 0000000..6351186 --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogMessage.java @@ -0,0 +1,22 @@ +package cryptomessenger.desktop.service.dialog; + +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; +import java.time.format.FormatStyle; + +import static java.time.format.DateTimeFormatter.ofLocalizedDateTime; + +@Data +@Builder +public class DialogMessage { + + private final LocalDateTime sentAt; + private String inbox; + private String outbox; + + public String getSentAt() { + return sentAt.format(ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)); + } +} diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogService.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogService.java new file mode 100644 index 0000000..b5828d2 --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogService.java @@ -0,0 +1,10 @@ +package cryptomessenger.desktop.service.dialog; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.UUID; + +public interface DialogService { + Page getMessages(String username, Pageable pageable); +} diff --git a/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogServiceImpl.java b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogServiceImpl.java new file mode 100644 index 0000000..391f854 --- /dev/null +++ b/crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/dialog/DialogServiceImpl.java @@ -0,0 +1,73 @@ +package cryptomessenger.desktop.service.dialog; + +import cryptomessenger.coder.Coder; +import cryptomessenger.desktop.infrastructure.client.feign.dialog.DialogClient; +import cryptomessenger.desktop.infrastructure.client.feign.dialog.DialogMessageDto; +import cryptomessenger.desktop.infrastructure.client.feign.message.MessageDto; +import cryptomessenger.desktop.infrastructure.client.feign.user.UserClient; +import cryptomessenger.desktop.infrastructure.client.feign.user.UserDto; +import cryptomessenger.desktop.infrastructure.localstorage.LocalStorage; +import cryptomessenger.desktop.service.LocalStorageKeys; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.time.ZoneId; + +import static java.time.ZoneOffset.UTC; + +@Service +@RequiredArgsConstructor +public class DialogServiceImpl implements DialogService { + + private final LocalStorage localStorage; + private final UserClient userClient; + private final DialogClient dialogClient; + private final Coder coder; + + @Override + public Page getMessages(String username, Pageable pageable) { + var currentUser = getCurrentUser(); + var receiver = userClient.getByUsername(username); + try { + return dialogClient.getDialog(currentUser.getId(), receiver.getId(), pageable) + .map(this::convertDialogMessage); + } catch (Exception e) { + return Page.empty(); + } + } + + private UserDto getCurrentUser() { + var currentUsername = localStorage.getString(LocalStorageKeys.CURRENT_USERNAME); + return userClient.getByUsername(currentUsername); + } + + private DialogMessage convertDialogMessage(DialogMessageDto dto) { + var sentAt = dto.getMessage() + .getSentAt() + .atZone(UTC) + .withZoneSameInstant(ZoneId.systemDefault()) + .toLocalDateTime(); + var isMine = dto.isMine(); + return DialogMessage.builder() + .sentAt(sentAt) + .outbox(isMine ? decodeText(dto.getMessage()) : "") + .inbox(!isMine ? decodeText(dto.getMessage()) : "") + .build(); + } + + private String decodeText(MessageDto dto) { + var currentUser = getCurrentUser(); + var isCurrentUserSender = currentUser.getId().equals(dto.getSenderId()); + var privateKey = localStorage.getBytes(LocalStorageKeys.PRIVATE_KEY); + try { + return coder.decode( + () -> isCurrentUserSender ? dto.getContentForSender() : dto.getContentForReceiver(), + () -> privateKey + ).getText(); + } catch (Exception e) { + return "[Unable to decode]"; + } + } +} diff --git a/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/DialogScene.fxml b/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/DialogScene.fxml new file mode 100644 index 0000000..5cf0a90 --- /dev/null +++ b/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/DialogScene.fxml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/SendMessageScene.fxml b/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/SendMessageScene.fxml index f7e277d..09a4c06 100644 --- a/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/SendMessageScene.fxml +++ b/crypto-messenger-desktop/src/main/resources/cryptomessenger/desktop/infrastructure/ui/SendMessageScene.fxml @@ -37,6 +37,11 @@ +