Skip to content

Commit c6d0c69

Browse files
committed
misc
1 parent fe5770e commit c6d0c69

File tree

16 files changed

+195
-46
lines changed

16 files changed

+195
-46
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.idea/
22
target/
3-
local-storage/
3+
CryptoMessengerStorage/
44
*.iml
55
.DS_Store

crypto-messenger-desktop/src/main/java/cryptomessenger/Desktop.java

+2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.cache.annotation.EnableCaching;
56
import org.springframework.cloud.openfeign.EnableFeignClients;
67

78
@SpringBootApplication
89
@EnableFeignClients
10+
@EnableCaching
911
public class Desktop {
1012

1113
public static void main(String[] args) {

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/client/user/UserClient.java

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cryptomessenger.desktop.infrastructure.client.user;
22

3+
import org.springframework.cache.annotation.Cacheable;
34
import org.springframework.cloud.openfeign.FeignClient;
45
import org.springframework.web.bind.annotation.*;
56

@@ -12,8 +13,10 @@ public interface UserClient {
1213
UserDto register(@RequestBody UserRegistrationDto registration);
1314

1415
@GetMapping("/users")
16+
@Cacheable("users")
1517
UserDto getByUsername(@RequestParam String username);
1618

1719
@GetMapping("/users/{id}")
20+
@Cacheable("users")
1821
UserDto getById(@PathVariable UUID id);
1922
}

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/localstorage/LocalStorage.java

+11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package cryptomessenger.desktop.infrastructure.localstorage;
22

3+
import java.util.Arrays;
4+
import java.util.List;
5+
36
public interface LocalStorage {
47

58
void save(String key, byte[] value);
@@ -8,9 +11,17 @@ default void save(String key, String value) {
811
save(key, value.getBytes());
912
}
1013

14+
default void save(String key, List<String> values) {
15+
save(key, String.join(";", values));
16+
}
17+
1118
byte[] getBytes(String key);
1219

1320
default String getString(String key) {
1421
return new String(getBytes(key));
1522
}
23+
24+
default List<String> getStrings(String key) {
25+
return Arrays.asList(getString(key).split(";"));
26+
}
1627
}

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/localstorage/LocalStorageImpl.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
@Component
1212
public class LocalStorageImpl implements LocalStorage {
1313

14-
private final Path basePath = Path.of("./local-storage");
14+
private final Path basePath = Path.of("CryptoMessengerStorage");
1515
private final ReadWriteLock lock = new ReentrantReadWriteLock();
1616

1717
@SneakyThrows

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/JavaFxApplication.java

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public void start(Stage primaryStage) {
2525
primaryStage.setTitle("CryptoMessenger");
2626
primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("/graphics/lock.png")));
2727
primaryStage.setScene(sceneLoader.load("MainScene"));
28+
primaryStage.setMinWidth(700);
29+
primaryStage.setMinHeight(700);
2830
primaryStage.show();
2931
}
3032
}

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/MainSceneController.java

+57-15
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@
88
import cryptomessenger.desktop.utility.ThreadFactories;
99
import javafx.collections.FXCollections;
1010
import javafx.event.ActionEvent;
11+
import javafx.scene.control.Button;
12+
import javafx.scene.control.Pagination;
1113
import javafx.scene.control.TableView;
1214
import javafx.scene.control.TextField;
1315
import javafx.scene.control.cell.PropertyValueFactory;
16+
import lombok.Builder;
1417
import lombok.RequiredArgsConstructor;
18+
import org.springframework.data.domain.Page;
1519
import org.springframework.data.domain.Pageable;
1620
import org.springframework.stereotype.Component;
1721

1822
import java.util.concurrent.Executors;
1923
import java.util.concurrent.ScheduledExecutorService;
2024
import java.util.concurrent.TimeUnit;
25+
import java.util.function.Function;
2126

2227
@Component
2328
@RequiredArgsConstructor
@@ -28,37 +33,43 @@ public class MainSceneController implements Refreshable {
2833
private final SendMessageDialog sendMessageDialog;
2934

3035
public TextField usernameField;
36+
public Button registerButton;
3137
public TableView<Message> inboxTable;
3238
public TableView<Message> outboxTable;
39+
public Pagination inboxTablePagination;
40+
public Pagination outboxTablePagination;
3341

3442
private ScheduledExecutorService executor;
3543

3644
@Override
3745
public void refresh() {
38-
usernameField.setText(userService.getCurrentUsername());
46+
var username = userService.getCurrentUsername();
47+
usernameField.setText(username);
48+
usernameField.setDisable(!username.isEmpty());
49+
registerButton.setVisible(username.isEmpty());
3950
refreshInboxTable();
4051
refreshOutboxTable();
4152
configureAutoRefresh();
4253
}
4354

4455
private void refreshInboxTable() {
45-
var columns = inboxTable.getColumns();
46-
columns.get(0).setCellValueFactory(new PropertyValueFactory<>("senderUsername"));
47-
columns.get(1).setCellValueFactory(new PropertyValueFactory<>("sentAt"));
48-
columns.get(2).setCellValueFactory(new PropertyValueFactory<>("text"));
49-
inboxTable.setItems(FXCollections.observableArrayList(
50-
messageService.getInbox(Pageable.ofSize(100).withPage(0)).getContent()
51-
));
56+
TableConfigurer.builder()
57+
.table(inboxTable)
58+
.pagination(inboxTablePagination)
59+
.recipientField("senderUsername")
60+
.messagesProvider(messageService::getInbox)
61+
.build()
62+
.configure();
5263
}
5364

5465
private void refreshOutboxTable() {
55-
var columns = outboxTable.getColumns();
56-
columns.get(0).setCellValueFactory(new PropertyValueFactory<>("receiverUsername"));
57-
columns.get(1).setCellValueFactory(new PropertyValueFactory<>("sentAt"));
58-
columns.get(2).setCellValueFactory(new PropertyValueFactory<>("text"));
59-
outboxTable.setItems(FXCollections.observableArrayList(
60-
messageService.getOutbox(Pageable.ofSize(100).withPage(0)).getContent()
61-
));
66+
TableConfigurer.builder()
67+
.table(outboxTable)
68+
.pagination(outboxTablePagination)
69+
.recipientField("receiverUsername")
70+
.messagesProvider(messageService::getOutbox)
71+
.build()
72+
.configure();
6273
}
6374

6475
private void configureAutoRefresh() {
@@ -80,4 +91,35 @@ public void onSendMessage(ActionEvent actionEvent) {
8091
public void onRefresh(ActionEvent actionEvent) {
8192
refresh();
8293
}
94+
95+
public void onReply(ActionEvent actionEvent) {
96+
var selectedMessage = inboxTable.getSelectionModel().getSelectedItem();
97+
var receiverUsername = selectedMessage == null ? null : selectedMessage.getSenderUsername();
98+
sendMessageDialog.show(receiverUsername, this::refresh);
99+
}
100+
101+
@Builder
102+
private static class TableConfigurer {
103+
104+
private final TableView<Message> table;
105+
private final Pagination pagination;
106+
private final String recipientField;
107+
private final Function<Pageable, Page<Message>> messagesProvider;
108+
109+
public void configure() {
110+
configureColumns();
111+
var messages = messagesProvider.apply(Pageable.ofSize(25).withPage(pagination.getCurrentPageIndex()));
112+
pagination.setPageCount(messages.getTotalPages());
113+
pagination.setVisible(messages.getTotalPages() > 0);
114+
table.setItems(FXCollections.observableArrayList(messages.getContent()));
115+
}
116+
117+
private void configureColumns() {
118+
var columns = table.getColumns();
119+
columns.get(0).setCellValueFactory(new PropertyValueFactory<>(recipientField));
120+
columns.get(1).setCellValueFactory(new PropertyValueFactory<>("sentAt"));
121+
columns.get(2).setCellValueFactory(new PropertyValueFactory<>("text"));
122+
columns.get(2).prefWidthProperty().bind(table.widthProperty().subtract(420));
123+
}
124+
}
83125
}

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/controller/SendMessageSceneController.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,48 @@
11
package cryptomessenger.desktop.infrastructure.ui.controller;
22

3+
import cryptomessenger.desktop.infrastructure.localstorage.LocalStorage;
4+
import cryptomessenger.desktop.service.LocalStorageKeys;
35
import cryptomessenger.desktop.service.message.MessageService;
46
import javafx.event.ActionEvent;
57
import javafx.fxml.Initializable;
8+
import javafx.scene.control.ComboBox;
69
import javafx.scene.control.TextArea;
7-
import javafx.scene.control.TextField;
810
import lombok.RequiredArgsConstructor;
911
import org.springframework.stereotype.Component;
1012

1113
import java.net.URL;
1214
import java.util.ResourceBundle;
1315

16+
import static javafx.collections.FXCollections.observableArrayList;
17+
1418
@Component
1519
@RequiredArgsConstructor
1620
public class SendMessageSceneController implements Initializable {
1721

1822
private final MessageService messageService;
23+
private final LocalStorage localStorage;
1924

20-
public TextField receiverField;
25+
public ComboBox<String> receiverSelector;
2126
public TextArea messageField;
2227

2328
private Runnable onSent;
2429
private Runnable onCancel;
2530

2631
@Override
2732
public void initialize(URL location, ResourceBundle resources) {
33+
receiverSelector.setItems(observableArrayList(localStorage.getStrings(LocalStorageKeys.CONTACTS)));
34+
var receiverUsername = resources.getString("receiverUsername");
35+
if (!receiverUsername.isEmpty()) {
36+
receiverSelector.setValue(receiverUsername);
37+
} else if (!receiverSelector.getItems().isEmpty()) {
38+
receiverSelector.setValue(receiverSelector.getItems().get(0));
39+
}
2840
onSent = (Runnable) resources.getObject("onSent");
2941
onCancel = (Runnable) resources.getObject("onCancel");
3042
}
3143

3244
public void onSend(ActionEvent actionEvent) {
33-
messageService.send(receiverField.getText(), messageField.getText());
45+
messageService.send(receiverSelector.getValue(), messageField.getText());
3446
onSent.run();
3547
}
3648

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/infrastructure/ui/dialog/SendMessageDialog.java

+5
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,16 @@ public class SendMessageDialog {
1717
private final SceneLoader sceneLoader;
1818

1919
public void show(Runnable onSuccess) {
20+
show(null, onSuccess);
21+
}
22+
23+
public void show(String receiverUsername, Runnable onSuccess) {
2024
var stage = new Stage();
2125
stage.setTitle("Send Message");
2226
stage.getIcons().add(new Image(getClass().getResourceAsStream("/graphics/lock.png")));
2327
stage.initModality(Modality.APPLICATION_MODAL);
2428
var sceneProperties = SceneProperties.of(
29+
entry("receiverUsername", receiverUsername == null ? "" : receiverUsername),
2530
entry("onSent", (Runnable) () -> {
2631
stage.close();
2732
onSuccess.run();
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package cryptomessenger.desktop.service;
22

33
public class LocalStorageKeys {
4-
public static final String CURRENT_USERNAME = "current-username";
4+
public static final String CURRENT_USERNAME = "user";
55
public static final String PRIVATE_KEY = "private-key";
6-
public static final String PUBLIC_KEY = "public-key";
6+
public static final String CONTACTS = "contacts";
77
}

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/message/Message.java

+8
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44
import lombok.Data;
55

66
import java.time.LocalDateTime;
7+
import java.time.format.FormatStyle;
8+
9+
import static java.time.format.DateTimeFormatter.ofLocalizedDateTime;
710

811
@Data
912
@Builder
1013
public class Message {
14+
1115
private final LocalDateTime sentAt;
1216
private final String senderUsername;
1317
private final String receiverUsername;
1418
private final String text;
19+
20+
public String getSentAt() {
21+
return sentAt.format(ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT));
22+
}
1523
}

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/message/MessageService.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package cryptomessenger.desktop.service.message;
22

3+
import jakarta.validation.constraints.NotBlank;
34
import org.springframework.data.domain.Page;
45
import org.springframework.data.domain.Pageable;
6+
import org.springframework.validation.annotation.Validated;
57

8+
@Validated
69
public interface MessageService {
710

8-
void send(String receiverUsername, String text);
11+
void send(String receiverUsername, @NotBlank String text);
912

1013
Page<Message> getInbox(Pageable pageable);
1114

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/message/MessageServiceImpl.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
import org.springframework.data.domain.Pageable;
1414
import org.springframework.stereotype.Service;
1515

16+
import java.time.ZoneId;
17+
18+
import static java.time.ZoneOffset.UTC;
19+
import static java.util.function.Predicate.not;
20+
import static java.util.stream.Collectors.toList;
21+
1622
@Service
1723
@RequiredArgsConstructor
1824
public class MessageServiceImpl implements MessageService {
@@ -34,13 +40,23 @@ public void send(String receiverUsername, String text) {
3440
.contentForReceiver(coder.encode(text::getBytes, receiver::getPublicKey).getBytes())
3541
.build()
3642
);
43+
addContact(receiverUsername);
3744
}
3845

3946
private UserDto getCurrentUser() {
4047
var currentUsername = localStorage.getString(LocalStorageKeys.CURRENT_USERNAME);
4148
return userClient.getByUsername(currentUsername);
4249
}
4350

51+
private void addContact(String newContact) {
52+
var contacts = localStorage.getStrings(LocalStorageKeys.CONTACTS).stream()
53+
.filter(not(String::isEmpty))
54+
.filter(not(contact -> contact.equals(newContact)))
55+
.collect(toList());
56+
contacts.add(0, newContact);
57+
localStorage.save(LocalStorageKeys.CONTACTS, contacts);
58+
}
59+
4460
@Override
4561
public Page<Message> getInbox(Pageable pageable) {
4662
try {
@@ -65,7 +81,7 @@ private Message convertMessage(MessageDto dto) {
6581
var sender = userClient.getById(dto.getSenderId());
6682
var receiver = userClient.getById(dto.getReceiverId());
6783
return Message.builder()
68-
.sentAt(dto.getSentAt())
84+
.sentAt(dto.getSentAt().atZone(UTC).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime())
6985
.senderUsername(sender.getUsername())
7086
.receiverUsername(receiver.getUsername())
7187
.text(decodeText(dto))

crypto-messenger-desktop/src/main/java/cryptomessenger/desktop/service/user/UserServiceImpl.java

-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,5 @@ public void register(String username) {
3232
);
3333
localStorage.save(LocalStorageKeys.CURRENT_USERNAME, username);
3434
localStorage.save(LocalStorageKeys.PRIVATE_KEY, keyPair.getPrivateKey().getBytes());
35-
localStorage.save(LocalStorageKeys.PUBLIC_KEY, keyPair.getPublicKey().getBytes());
3635
}
3736
}

0 commit comments

Comments
 (0)