A bunch of Packet changes
5
.idea/compiler.xml
generated
@ -11,10 +11,5 @@
|
|||||||
<module name="Lib" />
|
<module name="Lib" />
|
||||||
</profile>
|
</profile>
|
||||||
</annotationProcessing>
|
</annotationProcessing>
|
||||||
<bytecodeTargetLevel>
|
|
||||||
<module name="Lib" target="17" />
|
|
||||||
<module name="Client" target="17" />
|
|
||||||
<module name="Server" target="17" />
|
|
||||||
</bytecodeTargetLevel>
|
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@ -45,6 +45,12 @@
|
|||||||
<version>${junit.version}</version>
|
<version>${junit.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains</groupId>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<version>16.0.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -1,24 +1,37 @@
|
|||||||
package dev.wiing.gossip.client;
|
package dev.wiing.gossip.client;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.lib.PacketHandler;
|
||||||
import dev.wiing.gossip.lib.PacketManager;
|
import dev.wiing.gossip.lib.PacketManager;
|
||||||
import dev.wiing.gossip.lib.models.SecretUser;
|
import dev.wiing.gossip.lib.models.SecretUser;
|
||||||
|
import dev.wiing.gossip.lib.models.User;
|
||||||
|
import dev.wiing.gossip.lib.packets.FetchUserPacket;
|
||||||
import dev.wiing.gossip.lib.packets.Packet;
|
import dev.wiing.gossip.lib.packets.Packet;
|
||||||
|
import dev.wiing.gossip.lib.packets.UserDataPacket;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class Connection {
|
public class Connection {
|
||||||
|
|
||||||
private static final Connection instance = new Connection();
|
private static final Connection instance = new Connection();
|
||||||
|
|
||||||
private final PacketManager packetManager;
|
private final PacketManager packetManager = new PacketManager();
|
||||||
|
|
||||||
|
private final PacketHandler packetHandler = new PacketHandler();
|
||||||
|
|
||||||
|
private Thread handlerThread;
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
|
|
||||||
private SecretUser self;
|
private SecretUser self;
|
||||||
|
|
||||||
|
private final List<Packet> queuedPackets = Collections.synchronizedList(new LinkedList<>());
|
||||||
|
|
||||||
private Connection() {
|
private Connection() {
|
||||||
packetManager = new PacketManager();
|
|
||||||
packetManager.registerPackets();
|
packetManager.registerPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +43,35 @@ public class Connection {
|
|||||||
return packetManager;
|
return packetManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketHandler getPacketHandler() {
|
||||||
|
return packetHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PacketHandlerRunnable implements Runnable {
|
||||||
|
private final Connection connection;
|
||||||
|
|
||||||
|
public PacketHandlerRunnable(Connection connection) {
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Packet packet = connection.nextPacket();
|
||||||
|
connection.getPacketHandler().runPacket(packet);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginHandlingPackets() {
|
||||||
|
handlerThread = new Thread(new PacketHandlerRunnable(this));
|
||||||
|
handlerThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
public Socket getSocket() {
|
public Socket getSocket() {
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
@ -46,20 +88,81 @@ public class Connection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Packet nextPacket() {
|
public Packet nextPacket(boolean useQueue) throws SocketException {
|
||||||
|
if (!queuedPackets.isEmpty()) {
|
||||||
|
return queuedPackets.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return getPacketManager().readPacket(getSocket().getInputStream());
|
return getPacketManager().readPacket(getSocket().getInputStream());
|
||||||
|
} catch (SocketException e) {
|
||||||
|
throw e;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Packet nextPacket() throws SocketException {
|
||||||
|
return nextPacket(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Packet nextPacket(short packetType) throws SocketException {
|
||||||
|
while (true) {
|
||||||
|
Packet packet = nextPacket(false);
|
||||||
|
|
||||||
|
if (packet.getType() != packetType) {
|
||||||
|
queuedPackets.add(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SecretUser getSelf() {
|
public SecretUser getSelf() {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelf(SecretUser self) {
|
public void setSelf(SecretUser self) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
|
|
||||||
|
userCache.put(self.getUserID(), new User(
|
||||||
|
self.getUsername(),
|
||||||
|
self.getAvatarID(),
|
||||||
|
self.getUserID()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<Long, User> userCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public User getUser(long userID) {
|
||||||
|
User result;
|
||||||
|
|
||||||
|
if ((result = userCache.getOrDefault(userID, null)) != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchUserPacket fetch = new FetchUserPacket();
|
||||||
|
fetch.setUserID(userID);
|
||||||
|
Connection.getInstance().sendPacket(fetch);
|
||||||
|
|
||||||
|
Packet resp;
|
||||||
|
try {
|
||||||
|
resp = Connection.getInstance().nextPacket(UserDataPacket.TYPE);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (resp.getType() == UserDataPacket.TYPE) {
|
||||||
|
UserDataPacket userData = (UserDataPacket)resp;
|
||||||
|
result = new User(
|
||||||
|
userData.getUsername(),
|
||||||
|
userData.getAvatarID(),
|
||||||
|
userData.getUserID()
|
||||||
|
);
|
||||||
|
userCache.put(result.getUserID(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package dev.wiing.gossip.client.controllers;
|
|||||||
|
|
||||||
|
|
||||||
import dev.wiing.gossip.client.Connection;
|
import dev.wiing.gossip.client.Connection;
|
||||||
import dev.wiing.gossip.client.data.UserIcon;
|
import dev.wiing.gossip.client.data.UserAvatar;
|
||||||
import dev.wiing.gossip.lib.models.SecretUser;
|
import dev.wiing.gossip.lib.models.SecretUser;
|
||||||
import dev.wiing.gossip.lib.packets.CredentialsPacket;
|
import dev.wiing.gossip.lib.packets.CredentialsPacket;
|
||||||
import dev.wiing.gossip.lib.packets.Packet;
|
import dev.wiing.gossip.lib.packets.Packet;
|
||||||
@ -12,12 +12,15 @@ import javafx.event.ActionEvent;
|
|||||||
import javafx.event.Event;
|
import javafx.event.Event;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
import java.net.SocketException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@ -34,15 +37,7 @@ public class LoginController implements Initializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
paneIcon.setBackground(new Background(
|
UserAvatar.MORNING.applyToRegionBackground(paneIcon, false);
|
||||||
new BackgroundImage(
|
|
||||||
UserIcon.MORNING.getImage(),
|
|
||||||
BackgroundRepeat.NO_REPEAT,
|
|
||||||
BackgroundRepeat.NO_REPEAT,
|
|
||||||
BackgroundPosition.CENTER,
|
|
||||||
new BackgroundSize(100.0, 100.0, true, true, false, true)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
|
|
||||||
Circle circle = new Circle(paneIcon.getMinWidth() * 0.5);
|
Circle circle = new Circle(paneIcon.getMinWidth() * 0.5);
|
||||||
paneIcon.setShape(circle);
|
paneIcon.setShape(circle);
|
||||||
@ -57,17 +52,27 @@ public class LoginController implements Initializable {
|
|||||||
@FXML
|
@FXML
|
||||||
public void onLogin(ActionEvent event) {
|
public void onLogin(ActionEvent event) {
|
||||||
RegisterPacket packet = new RegisterPacket();
|
RegisterPacket packet = new RegisterPacket();
|
||||||
packet.setIconID((char)0);
|
packet.setAvatarID((byte)0);
|
||||||
packet.setUsername(txtUsername.getText());
|
packet.setUsername(txtUsername.getText());
|
||||||
|
|
||||||
Connection.getInstance().sendPacket(packet);
|
Connection.getInstance().sendPacket(packet);
|
||||||
|
|
||||||
Packet result = Connection.getInstance().nextPacket();
|
Packet result = null;
|
||||||
|
try {
|
||||||
|
result = Connection.getInstance().nextPacket();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
if (result != null && result.getType() == CredentialsPacket.TYPE) {
|
if (result != null && result.getType() == CredentialsPacket.TYPE) {
|
||||||
CredentialsPacket creds = (CredentialsPacket)result;
|
CredentialsPacket creds = (CredentialsPacket)result;
|
||||||
|
|
||||||
SecretUser user = new SecretUser(packet.getUsername(), packet.getIconID(), creds.getUID(), creds.getSecret());
|
SecretUser user = new SecretUser(packet.getUsername(), packet.getAvatarID(), creds.getUID(), creds.getSecret());
|
||||||
Connection.getInstance().setSelf(user);
|
Connection.getInstance().setSelf(user);
|
||||||
|
|
||||||
|
var pair = MainController.createInstance();
|
||||||
|
|
||||||
|
((Stage)Stage.getWindows().get(0)).setScene(new Scene(pair.first()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,114 @@
|
|||||||
|
package dev.wiing.gossip.client.controllers;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.client.Connection;
|
||||||
|
import dev.wiing.gossip.client.generic.Pair;
|
||||||
|
import dev.wiing.gossip.client.utils.Utils;
|
||||||
|
import dev.wiing.gossip.lib.models.Topic;
|
||||||
|
import dev.wiing.gossip.lib.models.User;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class MainChatController {
|
||||||
|
|
||||||
|
private Topic topic;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblBabblersCount;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblTopicName;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblTyping;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblVisitBabblerCount;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblVisitHostName;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblVisitTopicDescription;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblVisitTopicName;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextArea txtCompose;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox vboxChatPage;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox vboxMessages;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox vboxVisitPage;
|
||||||
|
|
||||||
|
public void setTopic(Topic topic) {
|
||||||
|
this.topic = topic;
|
||||||
|
|
||||||
|
boolean participating = topic.hasUser(Connection.getInstance().getSelf());
|
||||||
|
togglePages(participating);
|
||||||
|
|
||||||
|
setTopicName(topic.getName());
|
||||||
|
lblVisitTopicDescription.setText(topic.getDescription());
|
||||||
|
setBabblerCount(topic.getUsersReadOnly().size());
|
||||||
|
lblVisitHostName.setText(topic.getHost().getUsernameDisplay());
|
||||||
|
|
||||||
|
this.topic.addChangeListener(evt -> {
|
||||||
|
if (evt.getPropertyName().equals("userAdd") || evt.getPropertyName().equals("userRemove")) {
|
||||||
|
setBabblerCount(topic.getUsersReadOnly().size());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTopicName(String name) {
|
||||||
|
lblTopicName.setText(name);
|
||||||
|
lblVisitTopicName.setText(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBabblerCount(int count) {
|
||||||
|
String res = "No Babblers";
|
||||||
|
if (count == 1) {
|
||||||
|
res = "1 Babbler";
|
||||||
|
} else if (count > 1) {
|
||||||
|
res = Integer.toString(count) + " Babblers";
|
||||||
|
}
|
||||||
|
|
||||||
|
lblBabblersCount.setText(res);
|
||||||
|
lblVisitBabblerCount.setText(res + " active");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void togglePages(boolean participating) {
|
||||||
|
vboxChatPage.setVisible(participating);
|
||||||
|
vboxVisitPage.setVisible(!participating);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void onJoinTopic(ActionEvent event) {
|
||||||
|
topic.addUser(Connection.getInstance().getSelf());
|
||||||
|
|
||||||
|
togglePages(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void onSend(MouseEvent event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pair<Parent, MainChatController> createInstance() {
|
||||||
|
return Utils.createInstance("views/main-chat-view.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,140 @@
|
|||||||
|
package dev.wiing.gossip.client.controllers;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.client.Connection;
|
||||||
|
import dev.wiing.gossip.client.controllers.item.MainTopicItemController;
|
||||||
|
import dev.wiing.gossip.client.data.UserAvatar;
|
||||||
|
import dev.wiing.gossip.client.generic.Pair;
|
||||||
|
import dev.wiing.gossip.client.utils.Utils;
|
||||||
|
import dev.wiing.gossip.lib.models.Topic;
|
||||||
|
import dev.wiing.gossip.lib.models.User;
|
||||||
|
import dev.wiing.gossip.lib.packets.*;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class MainController implements Initializable {
|
||||||
|
|
||||||
|
Topic activeTopic = null;
|
||||||
|
Parent activeTopicElement = null;
|
||||||
|
private final Map<Long, Parent> topicContentMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox vboxRoot;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private VBox vboxTopics;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblUsername;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane paneIcon;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private AnchorPane paneContent;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblJoinMessage;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
User user = Connection.getInstance().getSelf();
|
||||||
|
lblUsername.setText(user.getUsernameDisplay());
|
||||||
|
|
||||||
|
UserAvatar.getAvatar(user.getAvatarID()).applyToRegionBackground(paneIcon, true);
|
||||||
|
|
||||||
|
Connection.getInstance().getPacketHandler().addListener(TopicAddedPacket.class, packet -> {
|
||||||
|
User host = Connection.getInstance().getUser(packet.getHostID());
|
||||||
|
|
||||||
|
Topic topic = new Topic(
|
||||||
|
packet.getTopicID(),
|
||||||
|
packet.getTopicName(),
|
||||||
|
packet.getTopicDescription(),
|
||||||
|
host,
|
||||||
|
packet.getTopicColor()
|
||||||
|
);
|
||||||
|
|
||||||
|
var pair = MainTopicItemController.createInstance();
|
||||||
|
pair.second().setTopic(topic);
|
||||||
|
|
||||||
|
var contentPair = MainChatController.createInstance();
|
||||||
|
contentPair.second().setTopic(topic);
|
||||||
|
|
||||||
|
topicContentMap.put(topic.getId(), contentPair.first());
|
||||||
|
|
||||||
|
AnchorPane.setLeftAnchor(contentPair.first(), 0.0);
|
||||||
|
AnchorPane.setBottomAnchor(contentPair.first(), 0.0);
|
||||||
|
AnchorPane.setTopAnchor(contentPair.first(), 0.0);
|
||||||
|
AnchorPane.setRightAnchor(contentPair.first(), 0.0);
|
||||||
|
contentPair.first().setVisible(false);
|
||||||
|
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
paneContent.getChildren().add(contentPair.first());
|
||||||
|
|
||||||
|
vboxTopics.getChildren().add(pair.first());
|
||||||
|
});
|
||||||
|
|
||||||
|
pair.second().setOnMouseClicked(event -> {
|
||||||
|
setActiveTopic(topic, pair.first());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Connection.getInstance().beginHandlingPackets();
|
||||||
|
|
||||||
|
vboxTopics.getChildren().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveTopic(Topic topic, Parent element) {
|
||||||
|
if (activeTopic != null) {
|
||||||
|
activeTopicElement.getStyleClass().remove("tag-min");
|
||||||
|
topicContentMap.get(activeTopic.getId()).setVisible(false);
|
||||||
|
} else {
|
||||||
|
lblJoinMessage.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic == null) {
|
||||||
|
activeTopic = null;
|
||||||
|
activeTopicElement = null;
|
||||||
|
|
||||||
|
lblJoinMessage.setVisible(true);
|
||||||
|
|
||||||
|
vboxRoot.setStyle("-accent: hsb(250, 10%, 50%);");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vboxRoot.setStyle("-accent: hsb(" + topic.getColor() + ", 70%, 100%);");
|
||||||
|
|
||||||
|
element.getStyleClass().add("tag-min");
|
||||||
|
|
||||||
|
activeTopic = topic;
|
||||||
|
activeTopicElement = element;
|
||||||
|
|
||||||
|
topicContentMap.get(activeTopic.getId()).setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void onCreateTopic(ActionEvent event) {
|
||||||
|
CreateTopicPacket packet = new CreateTopicPacket();
|
||||||
|
packet.setUserSecret(Connection.getInstance().getSelf().getUserSecret());
|
||||||
|
packet.setTopicName("Point Nemo");
|
||||||
|
packet.setTopicDescription("We are so gone XDD");
|
||||||
|
|
||||||
|
Connection.getInstance().sendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pair<Parent, MainController> createInstance() {
|
||||||
|
return Utils.createInstance("views/main-view.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package dev.wiing.gossip.client.controllers.item;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.client.generic.Pair;
|
||||||
|
import dev.wiing.gossip.client.utils.Utils;
|
||||||
|
import dev.wiing.gossip.lib.models.Topic;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
|
public class MainTopicItemController {
|
||||||
|
|
||||||
|
private Topic topic;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private HBox hboxParent;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label lblTopicName;
|
||||||
|
|
||||||
|
public void setTopic(Topic topic) {
|
||||||
|
this.topic = topic;
|
||||||
|
this.lblTopicName.setText(topic.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Topic getTopic() {
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Pair<Parent, MainTopicItemController> createInstance() {
|
||||||
|
return Utils.createInstance("views/main-topic-item.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnMouseClicked(EventHandler<MouseEvent> eventHandler) {
|
||||||
|
hboxParent.setOnMouseClicked(eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,102 @@
|
|||||||
|
package dev.wiing.gossip.client.data;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.client.Program;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class UserAvatar {
|
||||||
|
|
||||||
|
public static final UserAvatar MORNING = new UserAvatar("Morning", "Eggs and Bacon", 0, "avatars/avatar-0.png");
|
||||||
|
public static final UserAvatar VINTAGE = new UserAvatar("Vintage", "Red Car", 1, "avatars/avatar-1.png");
|
||||||
|
public static final UserAvatar ROUTINE = new UserAvatar("Routine", "Coffee Cup", 2, "avatars/avatar-2.png");
|
||||||
|
public static final UserAvatar SILLY = new UserAvatar("Silly", "Ghost Toy", 3, "avatars/avatar-3.png");
|
||||||
|
public static final UserAvatar ADVENTURE = new UserAvatar("Adventure", "Slide with a Plant", 4, "avatars/avatar-4.png");
|
||||||
|
public static final UserAvatar REBELLION = new UserAvatar("Rebellion", "Potted Cactus", 5, "avatars/avatar-5.png");
|
||||||
|
public static final UserAvatar DIGITAL = new UserAvatar("Digital", "Game Controller", 6, "avatars/avatar-6.png");
|
||||||
|
public static final UserAvatar MYSTERY = new UserAvatar("Mystery", "Floating Sphere", 7, "avatars/avatar-7.png");
|
||||||
|
public static final UserAvatar VACATION = new UserAvatar("Vacation", "Cat under an Umbrella", 8, "avatars/avatar-8.png");
|
||||||
|
public static final UserAvatar SIMPLE = new UserAvatar("Simple", "Potted Succulent", 9, "avatars/avatar-9.png");
|
||||||
|
|
||||||
|
public static UserAvatar getAvatar(int avatarID) {
|
||||||
|
return Arrays.stream(getAvatars())
|
||||||
|
.skip(avatarID)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UserAvatar[] getAvatars() {
|
||||||
|
return new UserAvatar[]{
|
||||||
|
MORNING,
|
||||||
|
VINTAGE,
|
||||||
|
ROUTINE,
|
||||||
|
SILLY,
|
||||||
|
ADVENTURE,
|
||||||
|
REBELLION,
|
||||||
|
DIGITAL,
|
||||||
|
MYSTERY,
|
||||||
|
VACATION,
|
||||||
|
SIMPLE
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String description;
|
||||||
|
private final int id;
|
||||||
|
private final URL url;
|
||||||
|
|
||||||
|
private UserAvatar(String name, String description, int id, String path) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.id = id;
|
||||||
|
this.url = Program.class.getResource(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image getImage() {
|
||||||
|
try {
|
||||||
|
return new Image(url.openStream());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyToRegionBackground(Region region, boolean applyShape) {
|
||||||
|
if (getImage() == null) return;
|
||||||
|
|
||||||
|
region.setBackground(new Background(
|
||||||
|
new BackgroundImage(
|
||||||
|
getImage(),
|
||||||
|
BackgroundRepeat.NO_REPEAT,
|
||||||
|
BackgroundRepeat.NO_REPEAT,
|
||||||
|
BackgroundPosition.CENTER,
|
||||||
|
new BackgroundSize(100.0, 100.0, true, true, false, true)
|
||||||
|
)
|
||||||
|
));
|
||||||
|
region.setStyle("");
|
||||||
|
|
||||||
|
if (applyShape) {
|
||||||
|
Circle circle = new Circle(region.getMinWidth() * 0.5);
|
||||||
|
region.setShape(circle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,72 +0,0 @@
|
|||||||
package dev.wiing.gossip.client.data;
|
|
||||||
|
|
||||||
import dev.wiing.gossip.client.Program;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
public class UserIcon {
|
|
||||||
|
|
||||||
public static final UserIcon MORNING = new UserIcon("Morning", "Eggs and Bacon", 0, "icons/icon_0.png");
|
|
||||||
public static final UserIcon VINTAGE = new UserIcon("Vintage", "Red Car", 1, "icons/icon_1.png");
|
|
||||||
public static final UserIcon ROUTINE = new UserIcon("Routine", "Coffee Cup", 2, "icons/icon_2.png");
|
|
||||||
public static final UserIcon SILLY = new UserIcon("Silly", "Ghost Toy", 3, "icons/icon_3.png");
|
|
||||||
public static final UserIcon ADVENTURE = new UserIcon("Adventure", "Slide with a Plant", 4, "icons/icon_4.png");
|
|
||||||
public static final UserIcon REBELLION = new UserIcon("Rebellion", "Potted Cactus", 5, "icons/icon_5.png");
|
|
||||||
public static final UserIcon DIGITAL = new UserIcon("Digital", "Game Controller", 6, "icons/icon_6.png");
|
|
||||||
public static final UserIcon MYSTERY = new UserIcon("Mystery", "Floating Sphere", 7, "icons/icon_7.png");
|
|
||||||
public static final UserIcon VACATION = new UserIcon("Vacation", "Cat under an Umbrella", 8, "icons/icon_8.png");
|
|
||||||
public static final UserIcon SIMPLE = new UserIcon("Simple", "Potted Succulent", 9, "icons/icon_9.png");
|
|
||||||
|
|
||||||
public static UserIcon[] getIcons() {
|
|
||||||
return new UserIcon[]{
|
|
||||||
MORNING,
|
|
||||||
VINTAGE,
|
|
||||||
ROUTINE,
|
|
||||||
SILLY,
|
|
||||||
ADVENTURE,
|
|
||||||
REBELLION,
|
|
||||||
DIGITAL,
|
|
||||||
MYSTERY,
|
|
||||||
VACATION,
|
|
||||||
SIMPLE
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final String description;
|
|
||||||
private final int id;
|
|
||||||
private final URL url;
|
|
||||||
|
|
||||||
private UserIcon(String name, String description, int id, String path) {
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
this.id = id;
|
|
||||||
this.url = Program.class.getResource(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public URL getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Image getImage() {
|
|
||||||
try {
|
|
||||||
return new Image(url.openStream());
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
package dev.wiing.gossip.client.generic;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record Pair<U, V>(@NotNull U first, @NotNull V second) {
|
||||||
|
|
||||||
|
public static <X, Y> Pair<X, Y> cast(Pair<? extends X, ? extends Y> second) {
|
||||||
|
return new Pair<>(second.first, second.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Pair[" +
|
||||||
|
"first=" + first + ", " +
|
||||||
|
"second=" + second + ']';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
package dev.wiing.gossip.client.utils;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.client.Program;
|
||||||
|
import dev.wiing.gossip.client.generic.Pair;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
public static <T> Pair<Parent, T> createInstance(String res) {
|
||||||
|
FXMLLoader loader = new FXMLLoader(Program.class.getResource(res));
|
||||||
|
|
||||||
|
Parent parent;
|
||||||
|
try {
|
||||||
|
parent = loader.load();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
T controller = loader.getController();
|
||||||
|
|
||||||
|
return new Pair<>(parent, controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openParentAsWindow(Parent parent, String title) {
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setTitle(title);
|
||||||
|
stage.setScene(new Scene(parent));
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateLabelColorOnText(Label label, TextField... texts) {
|
||||||
|
for (TextField text : texts) {
|
||||||
|
text.setOnKeyTyped(keyEvent -> updateLabelColorOnTextCallback(label, texts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateLabelColorOnTextCallback(Label label, TextField... texts) {
|
||||||
|
boolean hadItems = label.getStyleClass().contains("accent");
|
||||||
|
|
||||||
|
if (Arrays.stream(texts).allMatch(textField -> textField.getText().isEmpty())) {
|
||||||
|
label.getStyleClass().remove("accent");
|
||||||
|
}
|
||||||
|
else if (Arrays.stream(texts).allMatch(textField -> !textField.getText().isEmpty() && !hadItems)) {
|
||||||
|
label.getStyleClass().add("accent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -2,8 +2,11 @@ module dev.wiing.gossip.client {
|
|||||||
requires javafx.controls;
|
requires javafx.controls;
|
||||||
requires javafx.fxml;
|
requires javafx.fxml;
|
||||||
requires dev.wiing.gossip.lib;
|
requires dev.wiing.gossip.lib;
|
||||||
|
requires annotations;
|
||||||
|
requires java.desktop;
|
||||||
|
|
||||||
exports dev.wiing.gossip.client;
|
exports dev.wiing.gossip.client;
|
||||||
opens dev.wiing.gossip.client to javafx.fxml;
|
opens dev.wiing.gossip.client to javafx.fxml;
|
||||||
opens dev.wiing.gossip.client.controllers to javafx.fxml;
|
opens dev.wiing.gossip.client.controllers to javafx.fxml;
|
||||||
|
opens dev.wiing.gossip.client.controllers.item to javafx.fxml;
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
11
Client/src/main/resources/dev/wiing/gossip/client/custom.css
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
.gradient {
|
||||||
|
-fx-background-color: radial-gradient(radius 130%, ladder(#eee, -accent, #111), #111);
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-button.clickable:hover {
|
||||||
|
-fx-background-color: #fff1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic-button.clickable.tag-min:hover {
|
||||||
|
-fx-background-color: ladder(hsb(0, 0%, 50%), -color, #0000);
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
@ -19,10 +19,10 @@
|
|||||||
-fx-background-radius: 16px;
|
-fx-background-radius: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list.container .viewport,
|
.list.container > .viewport,
|
||||||
.section,
|
.section,
|
||||||
.pane.list.container {
|
.pane.list.container {
|
||||||
-fx-background-color: #19191d;
|
-fx-background-color: #88f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list.container.dim .viewport {
|
.list.container.dim .viewport {
|
||||||
@ -34,12 +34,12 @@ SplitPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SplitPane .split-pane-divider {
|
SplitPane .split-pane-divider {
|
||||||
-fx-background-color: #19191d;
|
-fx-background-color: #88f1;
|
||||||
-fx-padding: 0.0 5.0 0.0 0.0;
|
-fx-padding: 0.0 5.0 0.0 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.elevated {
|
.elevated {
|
||||||
-fx-background-color: #25252c;
|
-fx-background-color: #88f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-radius {
|
.border-radius {
|
||||||
@ -52,18 +52,29 @@ SplitPane .split-pane-divider {
|
|||||||
|
|
||||||
Label,
|
Label,
|
||||||
RadioButton,
|
RadioButton,
|
||||||
TextField {
|
TextField,
|
||||||
|
TextArea {
|
||||||
-fx-text-fill: #fff;
|
-fx-text-fill: #fff;
|
||||||
-fx-fill: #fff;
|
-fx-fill: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextArea,
|
||||||
|
TextArea .scroll-pane {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextArea .scroll-pane .content {
|
||||||
|
-fx-background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
ChoiceBox,
|
ChoiceBox,
|
||||||
TextField {
|
TextField,
|
||||||
-fx-background-color: #19191d;
|
TextArea .scroll-pane .content {
|
||||||
|
-fx-background-color: #88f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorPicker {
|
ColorPicker {
|
||||||
-fx-background-color: #29292F;
|
-fx-background-color: #88f1;
|
||||||
-fx-padding: 4px 14px;
|
-fx-padding: 4px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,6 +88,12 @@ ColorPicker {
|
|||||||
-fx-fill: #556;
|
-fx-fill: #556;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.transparent,
|
||||||
|
.transparent.list.container .viewport,
|
||||||
|
TextArea.transparent .scroll-pane .content {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.accent {
|
.accent {
|
||||||
-fx-text-fill: -accent;
|
-fx-text-fill: -accent;
|
||||||
}
|
}
|
||||||
@ -85,15 +102,10 @@ Separator {
|
|||||||
-fx-opacity: 0.1;
|
-fx-opacity: 0.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button.secondary {
|
||||||
-fx-background-color: -accent;
|
|
||||||
-fx-background-insets: 0;
|
|
||||||
-fx-text-fill: white;
|
|
||||||
-fx-padding: 4px 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
Button.gray {
|
|
||||||
-fx-background-color: #4a4a55;
|
-fx-background-color: #4a4a55;
|
||||||
|
-fx-border-width: 0px;
|
||||||
|
-fx-text-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
Button,
|
Button,
|
||||||
@ -101,6 +113,17 @@ Button,
|
|||||||
-fx-cursor: hand;
|
-fx-cursor: hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button:hover {
|
||||||
|
-fx-background-color: ladder(hsb(0, 0%, 40%), -color, #0000);
|
||||||
|
-fx-text-fill: ladder(hsb(0, 0%, 50%), -color, #fff);
|
||||||
|
-fx-border-color: ladder(hsb(0, 0%, 50%), -color, #fff);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button.secondary:hover {
|
||||||
|
-fx-background-color: #6c6c80;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
ChoiceBox {
|
ChoiceBox {
|
||||||
-fx-background-insets: 0;
|
-fx-background-insets: 0;
|
||||||
}
|
}
|
||||||
@ -109,7 +132,9 @@ ChoiceBox {
|
|||||||
-fx-font-family: "AXIS Extra Bold";
|
-fx-font-family: "AXIS Extra Bold";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Button,
|
||||||
Label.tag {
|
Label.tag {
|
||||||
|
-color: -accent;
|
||||||
-fx-background-color: ladder(hsb(0, 0%, 70%), -color, #0000);
|
-fx-background-color: ladder(hsb(0, 0%, 70%), -color, #0000);
|
||||||
-fx-background-radius: 8px;
|
-fx-background-radius: 8px;
|
||||||
-fx-border-radius: 8px;
|
-fx-border-radius: 8px;
|
||||||
@ -119,6 +144,21 @@ Label.tag {
|
|||||||
-fx-padding: 6px 14px;
|
-fx-padding: 6px 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tag-min {
|
||||||
|
-color: -accent;
|
||||||
|
-fx-background-color: ladder(hsb(0, 0%, 60%), -color, #0000);
|
||||||
|
-fx-text-fill: -color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Label.tag.small {
|
||||||
|
-fx-background-color: -color;
|
||||||
|
-fx-background-radius: 4px;
|
||||||
|
-fx-border-width: 0;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
-fx-font-size: 10px;
|
||||||
|
-fx-padding: 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
CheckBox .box {
|
CheckBox .box {
|
||||||
-fx-background-color: #25252c;
|
-fx-background-color: #25252c;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,10 +12,11 @@
|
|||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="16.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.wiing.gossip.client.controllers.LoginController">
|
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" spacing="16.0" styleClass="gradient" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.wiing.gossip.client.controllers.LoginController">
|
||||||
<stylesheets>
|
<stylesheets>
|
||||||
<URL value="@../styling.css" />
|
<URL value="@../styling.css" />
|
||||||
<URL value="@../scroll.css" />
|
<URL value="@../scroll.css" />
|
||||||
|
<URL value="@../custom.css" />
|
||||||
</stylesheets>
|
</stylesheets>
|
||||||
<children>
|
<children>
|
||||||
<ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
|
<ImageView fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
|
||||||
@ -31,13 +32,13 @@
|
|||||||
</Label>
|
</Label>
|
||||||
<HBox alignment="CENTER" spacing="16.0" VBox.vgrow="ALWAYS">
|
<HBox alignment="CENTER" spacing="16.0" VBox.vgrow="ALWAYS">
|
||||||
<children>
|
<children>
|
||||||
<AnchorPane fx:id="paneIcon" maxHeight="64.0" maxWidth="64.0" minHeight="64.0" minWidth="64.0" onMouseClicked="#onChangeIcon" onMouseEntered="#onIconMouseEnter" onMouseExited="#onIconMouseExit">
|
<AnchorPane fx:id="paneIcon" maxHeight="64.0" maxWidth="64.0" minHeight="64.0" minWidth="64.0" onMouseClicked="#onChangeIcon" onMouseEntered="#onIconMouseEnter" onMouseExited="#onIconMouseExit" style="-fx-background-color: white;">
|
||||||
<children>
|
<children>
|
||||||
<HBox fx:id="hboxEditOverlay" alignment="CENTER" mouseTransparent="true" opacity="0.0" style="-fx-background-color: #0006;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<HBox fx:id="hboxEditOverlay" alignment="CENTER" mouseTransparent="true" opacity="0.0" style="-fx-background-color: #0006;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<children>
|
||||||
<ImageView blendMode="HARD_LIGHT" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true">
|
<ImageView blendMode="HARD_LIGHT" fitHeight="32.0" fitWidth="32.0" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../edit.png" />
|
<Image url="@../icons/icon-edit.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</children>
|
</children>
|
||||||
|
|||||||
@ -0,0 +1,165 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.String?>
|
||||||
|
<?import java.net.URL?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.control.Separator?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
|
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.wiing.gossip.client.controllers.MainChatController">
|
||||||
|
<children>
|
||||||
|
<VBox fx:id="vboxVisitPage" alignment="CENTER" layoutX="10.0" layoutY="10.0" spacing="8.0" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<AnchorPane maxHeight="64.0" maxWidth="64.0" minHeight="64.0" minWidth="64.0">
|
||||||
|
<children>
|
||||||
|
<Circle blendMode="OVERLAY" fill="WHITE" opacity="0.7" radius="32.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||||
|
<ImageView fitHeight="48.0" fitWidth="48.0" layoutX="8.0" layoutY="8.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="8.0" AnchorPane.leftAnchor="8.0" AnchorPane.rightAnchor="8.0" AnchorPane.topAnchor="8.0">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
<Label fx:id="lblVisitTopicName" style="-fx-font-size: 20;" styleClass="axis" text="Topic name" />
|
||||||
|
<Label fx:id="lblVisitTopicDescription" text="Topic Description" />
|
||||||
|
<Label fx:id="lblVisitHostName" styleClass="accent" text="\@ Username" />
|
||||||
|
<Separator orientation="VERTICAL" prefHeight="20.0" visible="false" />
|
||||||
|
<Button mnemonicParsing="false" onAction="#onJoinTopic" text="Join">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="secondary" />
|
||||||
|
</styleClass>
|
||||||
|
</Button>
|
||||||
|
<Label fx:id="lblVisitBabblerCount" styleClass="faint" text="2 babblers active" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
<VBox fx:id="vboxChatPage" spacing="16.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="4.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fitHeight="24.0" fitWidth="24.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label fx:id="lblTopicName" style="-fx-font-size: 16;" styleClass="axis" text="Label" />
|
||||||
|
<Label fx:id="lblBabblersCount" alignment="CENTER_RIGHT" maxWidth="1.7976931348623157E308" style="-fx-font-size: 12;" styleClass="faint" text="2 babblers" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
|
||||||
|
<content>
|
||||||
|
<VBox fx:id="vboxMessages" spacing="16.0">
|
||||||
|
<children>
|
||||||
|
<HBox spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Pane maxHeight="40.0" maxWidth="40.0" minHeight="40.0" minWidth="40.0" style="-fx-background-color: white;" />
|
||||||
|
<VBox alignment="CENTER_LEFT" minHeight="40.0" spacing="4.0" HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="BOTTOM_LEFT" spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Label style="-fx-font-size: 14;" text="\@ Username">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="accent" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
<Label text="You">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="tag" />
|
||||||
|
<String fx:value="small" />
|
||||||
|
<String fx:value="axis" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
<Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" maxWidth="1.7976931348623157E308" style="-fx-font-size: 10;" styleClass="faint" text="10 minutes ago" textAlignment="RIGHT" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<VBox spacing="4.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Message contents" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<HBox alignment="CENTER_LEFT" layoutX="26.0" layoutY="26.0" spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fitHeight="20.0" fitWidth="20.0" opacity="0.5" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/door-enter.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label style="-fx-font-size: 14;" styleClass="axis" text="\@ Username" />
|
||||||
|
<Label layoutX="46.0" layoutY="19.0" style="-fx-font-size: 12;" text="joined the Topic" />
|
||||||
|
<Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" maxWidth="1.7976931348623157E308" style="-fx-font-size: 10;" styleClass="faint" text="10 minutes ago" textAlignment="RIGHT" HBox.hgrow="ALWAYS">
|
||||||
|
<HBox.margin>
|
||||||
|
<Insets />
|
||||||
|
</HBox.margin>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="16.0" right="16.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="elevated" />
|
||||||
|
<String fx:value="border-radius-small" />
|
||||||
|
</styleClass>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="container" />
|
||||||
|
<String fx:value="list" />
|
||||||
|
</styleClass>
|
||||||
|
</ScrollPane>
|
||||||
|
<VBox spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="lblTyping" style="-fx-font-size: 11;" styleClass="faint" text="Username, Username is typing ...">
|
||||||
|
<padding>
|
||||||
|
<Insets left="16.0" right="16.0" />
|
||||||
|
</padding>
|
||||||
|
</Label>
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="8.0">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="container" />
|
||||||
|
<String fx:value="pane" />
|
||||||
|
<String fx:value="list" />
|
||||||
|
</styleClass>
|
||||||
|
<children>
|
||||||
|
<TextArea fx:id="txtCompose" maxHeight="1.7976931348623157E308" minHeight="1.0" prefHeight="24.0" prefRowCount="1" promptText="Compose..." styleClass="transparent" wrapText="true" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fitHeight="20.0" fitWidth="20.0" onMouseClicked="#onSend" opacity="0.5" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-send-2.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
<stylesheets>
|
||||||
|
<URL value="@../styling.css" />
|
||||||
|
<URL value="@../scroll.css" />
|
||||||
|
<URL value="@../custom.css" />
|
||||||
|
</stylesheets>
|
||||||
|
</AnchorPane>
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.String?>
|
||||||
|
<?import java.net.URL?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
|
||||||
|
<HBox fx:id="hboxParent" opacity="0.8" spacing="4.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.wiing.gossip.client.controllers.item.MainTopicItemController">
|
||||||
|
<children>
|
||||||
|
<ImageView blendMode="OVERLAY" fitHeight="16.0" fitWidth="16.0" opacity="0.4" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label fx:id="lblTopicName" text="Label" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="border-radius-small" />
|
||||||
|
<String fx:value="clickable" />
|
||||||
|
<String fx:value="topic-button" />
|
||||||
|
</styleClass>
|
||||||
|
<stylesheets>
|
||||||
|
<URL value="@../styling.css" />
|
||||||
|
<URL value="@../custom.css" />
|
||||||
|
</stylesheets>
|
||||||
|
</HBox>
|
||||||
@ -3,26 +3,63 @@
|
|||||||
<?import java.lang.String?>
|
<?import java.lang.String?>
|
||||||
<?import java.net.URL?>
|
<?import java.net.URL?>
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ScrollPane?>
|
<?import javafx.scene.control.ScrollPane?>
|
||||||
|
<?import javafx.scene.control.Separator?>
|
||||||
<?import javafx.scene.control.SplitPane?>
|
<?import javafx.scene.control.SplitPane?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
<?import javafx.scene.image.Image?>
|
<?import javafx.scene.image.Image?>
|
||||||
<?import javafx.scene.image.ImageView?>
|
<?import javafx.scene.image.ImageView?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Pane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
|
|
||||||
|
<VBox fx:id="vboxRoot" alignment="TOP_RIGHT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="486.0" prefWidth="750.0" spacing="16.0" style="-accent: hsb(0, 0%, 50%);;" styleClass="gradient" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="dev.wiing.gossip.client.controllers.MainController">
|
||||||
<VBox alignment="TOP_RIGHT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="594.0" prefWidth="948.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
|
|
||||||
<stylesheets>
|
<stylesheets>
|
||||||
<URL value="@../styling.css" />
|
<URL value="@../styling.css" />
|
||||||
<URL value="@../scroll.css" />
|
<URL value="@../scroll.css" />
|
||||||
|
<URL value="@../custom.css" />
|
||||||
</stylesheets>
|
</stylesheets>
|
||||||
<children>
|
<children>
|
||||||
<ImageView fitHeight="30.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
|
<HBox>
|
||||||
|
<children>
|
||||||
|
<ImageView blendMode="OVERLAY" fitHeight="30.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true">
|
||||||
<image>
|
<image>
|
||||||
<Image url="@../logo.png" />
|
<Image url="@../logo.png" />
|
||||||
</image>
|
</image>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
<SplitPane dividerPositions="0.29797979797979796" prefHeight="160.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
|
<Separator maxWidth="1.7976931348623157E308" visible="false" HBox.hgrow="ALWAYS" />
|
||||||
|
<HBox spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<VBox alignment="CENTER_RIGHT" minHeight="40.0" spacing="4.0" HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="BOTTOM_LEFT" spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="lblUsername" style="-fx-font-size: 14;" text="\@ Username">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="accent" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<Label style="-fx-font-size: 12;" text="Log out">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="faint" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
<Pane fx:id="paneIcon" maxHeight="40.0" maxWidth="40.0" minHeight="40.0" minWidth="40.0" style="-fx-background-color: white;" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<SplitPane dividerPositions="0.29797979797979796" prefHeight="160.0" prefWidth="200.0" styleClass="transparent" VBox.vgrow="ALWAYS">
|
||||||
<items>
|
<items>
|
||||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
|
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
|
||||||
<styleClass>
|
<styleClass>
|
||||||
@ -30,13 +67,57 @@
|
|||||||
<String fx:value="pane" />
|
<String fx:value="pane" />
|
||||||
</styleClass>
|
</styleClass>
|
||||||
<children>
|
<children>
|
||||||
<ScrollPane prefHeight="200.0" prefWidth="200.0" vbarPolicy="NEVER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<VBox alignment="TOP_CENTER" spacing="16.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<content>
|
|
||||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="200.0" prefWidth="200.0">
|
|
||||||
<children>
|
<children>
|
||||||
<VBox prefHeight="200.0" prefWidth="100.0" />
|
<ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" VBox.vgrow="ALWAYS">
|
||||||
|
<content>
|
||||||
|
<VBox fx:id="vboxTopics" maxHeight="1.7976931348623157E308" spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<HBox spacing="4.0">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="tag-min" />
|
||||||
|
<String fx:value="border-radius-small" />
|
||||||
|
</styleClass>
|
||||||
|
<children>
|
||||||
|
<ImageView blendMode="OVERLAY" fitHeight="16.0" fitWidth="16.0" opacity="0.4" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Label" />
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
<HBox layoutX="10.0" layoutY="10.0" opacity="0.8" spacing="4.0" styleClass="border-radius-small">
|
||||||
|
<children>
|
||||||
|
<ImageView blendMode="OVERLAY" fitHeight="16.0" fitWidth="16.0" opacity="0.4" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Label" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
<HBox layoutX="10.0" layoutY="50.0" opacity="0.8" spacing="4.0" styleClass="border-radius-small">
|
||||||
|
<children>
|
||||||
|
<ImageView blendMode="OVERLAY" fitHeight="16.0" fitWidth="16.0" opacity="0.4" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label text="Label" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
</content>
|
</content>
|
||||||
<padding>
|
<padding>
|
||||||
<Insets right="4.0" />
|
<Insets right="4.0" />
|
||||||
@ -44,12 +125,152 @@
|
|||||||
<styleClass>
|
<styleClass>
|
||||||
<String fx:value="container" />
|
<String fx:value="container" />
|
||||||
<String fx:value="list" />
|
<String fx:value="list" />
|
||||||
<String fx:value="dim" />
|
<String fx:value="transparent" />
|
||||||
</styleClass>
|
</styleClass>
|
||||||
</ScrollPane>
|
</ScrollPane>
|
||||||
|
<Button mnemonicParsing="false" onAction="#onCreateTopic" text="New TOPIC">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="tag" />
|
||||||
|
</styleClass>
|
||||||
|
</Button>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets right="16.0" />
|
||||||
|
</padding>
|
||||||
|
</AnchorPane>
|
||||||
|
<AnchorPane fx:id="paneContent" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<Label fx:id="lblJoinMessage" alignment="CENTER" style="-fx-font-size: 20;" text="Start or Join a Topic!" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="accent" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
<AnchorPane layoutX="16.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<VBox alignment="CENTER" spacing="8.0" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<AnchorPane maxHeight="64.0" maxWidth="64.0" minHeight="64.0" minWidth="64.0">
|
||||||
|
<children>
|
||||||
|
<Circle blendMode="OVERLAY" fill="WHITE" opacity="0.7" radius="32.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
|
||||||
|
<ImageView fitHeight="48.0" fitWidth="48.0" layoutX="8.0" layoutY="8.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="8.0" AnchorPane.leftAnchor="8.0" AnchorPane.rightAnchor="8.0" AnchorPane.topAnchor="8.0">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
</children>
|
</children>
|
||||||
</AnchorPane>
|
</AnchorPane>
|
||||||
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
|
<Label style="-fx-font-size: 20;" styleClass="axis" text="Topic name" />
|
||||||
|
<Label text="Topic Description" />
|
||||||
|
<Label styleClass="accent" text="\@ Username" />
|
||||||
|
<Separator orientation="VERTICAL" prefHeight="20.0" visible="false" />
|
||||||
|
<Button mnemonicParsing="false" text="Join">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="secondary" />
|
||||||
|
</styleClass>
|
||||||
|
</Button>
|
||||||
|
<Label styleClass="faint" text="2 babblers active" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0" spacing="16.0" visible="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="4.0">
|
||||||
|
<children>
|
||||||
|
<ImageView fitHeight="24.0" fitWidth="24.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-hash.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<Label style="-fx-font-size: 16;" styleClass="axis" text="Label" />
|
||||||
|
<Label alignment="CENTER_RIGHT" maxWidth="1.7976931348623157E308" style="-fx-font-size: 12;" styleClass="faint" text="2 babblers" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
|
||||||
|
<content>
|
||||||
|
<VBox spacing="16.0">
|
||||||
|
<children>
|
||||||
|
<HBox spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Pane maxHeight="40.0" maxWidth="40.0" minHeight="40.0" minWidth="40.0" style="-fx-background-color: white;" />
|
||||||
|
<VBox alignment="CENTER_LEFT" minHeight="40.0" spacing="4.0" HBox.hgrow="ALWAYS">
|
||||||
|
<children>
|
||||||
|
<HBox alignment="BOTTOM_LEFT" spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Label style="-fx-font-size: 14;" text="\@ Username">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="axis" />
|
||||||
|
<String fx:value="accent" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
<Label text="You">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="tag" />
|
||||||
|
<String fx:value="small" />
|
||||||
|
<String fx:value="axis" />
|
||||||
|
</styleClass>
|
||||||
|
</Label>
|
||||||
|
<Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" maxWidth="1.7976931348623157E308" style="-fx-font-size: 10;" styleClass="faint" text="10 minutes ago" textAlignment="RIGHT" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
<VBox spacing="4.0">
|
||||||
|
<children>
|
||||||
|
<Label text="Message contents" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="16.0" left="16.0" right="16.0" top="16.0" />
|
||||||
|
</padding>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="container" />
|
||||||
|
<String fx:value="list" />
|
||||||
|
</styleClass>
|
||||||
|
</ScrollPane>
|
||||||
|
<VBox spacing="8.0">
|
||||||
|
<children>
|
||||||
|
<Label style="-fx-font-size: 11;" styleClass="faint" text="Username, Username is typing ...">
|
||||||
|
<padding>
|
||||||
|
<Insets left="16.0" right="16.0" />
|
||||||
|
</padding>
|
||||||
|
</Label>
|
||||||
|
<HBox alignment="CENTER_LEFT" spacing="8.0">
|
||||||
|
<styleClass>
|
||||||
|
<String fx:value="container" />
|
||||||
|
<String fx:value="pane" />
|
||||||
|
<String fx:value="list" />
|
||||||
|
</styleClass>
|
||||||
|
<children>
|
||||||
|
<TextArea maxHeight="1.7976931348623157E308" minHeight="1.0" prefHeight="24.0" prefRowCount="1" promptText="Compose..." styleClass="transparent" wrapText="true" HBox.hgrow="ALWAYS" />
|
||||||
|
<ImageView fitHeight="20.0" fitWidth="20.0" opacity="0.5" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../icons/icon-send-2.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="8.0" left="8.0" right="8.0" top="8.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets left="16.0" />
|
||||||
|
</padding></AnchorPane>
|
||||||
</items>
|
</items>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</children>
|
</children>
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package dev.wiing.gossip.lib;
|
package dev.wiing.gossip.lib;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.lib.packets.CreateTopicPacket;
|
||||||
import dev.wiing.gossip.lib.packets.Packet;
|
import dev.wiing.gossip.lib.packets.Packet;
|
||||||
|
import dev.wiing.gossip.lib.packets.TopicAddedPacket;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@ -36,6 +38,14 @@ public class PacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void runPacket(Packet packet) {
|
||||||
|
switch (packet.getType()) {
|
||||||
|
case TopicAddedPacket.TYPE:
|
||||||
|
runPacket(TopicAddedPacket.class, (TopicAddedPacket)packet);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public <T extends Packet> Set<PacketListener<T>> getListeners(Class<T> packetClass) {
|
public <T extends Packet> Set<PacketListener<T>> getListeners(Class<T> packetClass) {
|
||||||
if (!listeners.containsKey(packetClass))
|
if (!listeners.containsKey(packetClass))
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
package dev.wiing.gossip.lib;
|
package dev.wiing.gossip.lib;
|
||||||
|
|
||||||
import dev.wiing.gossip.lib.packets.CredentialsPacket;
|
import dev.wiing.gossip.lib.packets.*;
|
||||||
import dev.wiing.gossip.lib.packets.RegisterPacket;
|
|
||||||
import dev.wiing.gossip.lib.packets.Packet;
|
|
||||||
import dev.wiing.gossip.lib.packets.TestPacket;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -20,6 +17,12 @@ public class PacketManager {
|
|||||||
|
|
||||||
add(new RegisterPacket());
|
add(new RegisterPacket());
|
||||||
add(new CredentialsPacket());
|
add(new CredentialsPacket());
|
||||||
|
|
||||||
|
add(new CreateTopicPacket());
|
||||||
|
add(new TopicAddedPacket());
|
||||||
|
|
||||||
|
add(new FetchUserPacket());
|
||||||
|
add(new UserDataPacket());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -33,6 +36,9 @@ public class PacketManager {
|
|||||||
packetMap.clear();
|
packetMap.clear();
|
||||||
|
|
||||||
for (Packet packet : packetList) {
|
for (Packet packet : packetList) {
|
||||||
|
if (packetMap.containsKey(packet.getType())) {
|
||||||
|
throw new RuntimeException("Packet type already exists");
|
||||||
|
}
|
||||||
packetMap.put(packet.getType(), packet);
|
packetMap.put(packet.getType(), packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,6 +46,8 @@ public class PacketManager {
|
|||||||
public Packet readPacket(BufferedInputStream stream) throws IOException {
|
public Packet readPacket(BufferedInputStream stream) throws IOException {
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(stream.readNBytes(6));
|
ByteBuffer buffer = ByteBuffer.wrap(stream.readNBytes(6));
|
||||||
|
|
||||||
|
if (buffer.capacity() < 6) return null;
|
||||||
|
|
||||||
short type = buffer.getShort();
|
short type = buffer.getShort();
|
||||||
int size = buffer.getInt();
|
int size = buffer.getInt();
|
||||||
|
|
||||||
@ -71,4 +79,10 @@ public class PacketManager {
|
|||||||
writePacket(outputStream, packet);
|
writePacket(outputStream, packet);
|
||||||
outputStream.flush();
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void replyPacket(Packet source, Packet reply) throws IOException {
|
||||||
|
BufferedOutputStream outputStream = new BufferedOutputStream(source.getSource().getOutputStream());
|
||||||
|
writePacket(outputStream, reply);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
public class StringType implements DataType<String> {
|
public class StringType implements DataType<String> {
|
||||||
|
|
||||||
private String value;
|
private String value = "";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
|
|||||||
17
Lib/src/main/java/dev/wiing/gossip/lib/models/Message.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package dev.wiing.gossip.lib.models;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public class Message {
|
||||||
|
private User author;
|
||||||
|
private String message;
|
||||||
|
private LocalDateTime postTime;
|
||||||
|
|
||||||
|
public Message(User author, String message, LocalDateTime postTime) {
|
||||||
|
this.author = author;
|
||||||
|
this.message = message;
|
||||||
|
this.postTime = postTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
96
Lib/src/main/java/dev/wiing/gossip/lib/models/Topic.java
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package dev.wiing.gossip.lib.models;
|
||||||
|
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.beans.PropertyChangeSupport;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class Topic {
|
||||||
|
private PropertyChangeSupport changeSupport;
|
||||||
|
|
||||||
|
private final long id;
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private final User host;
|
||||||
|
private final Map<Long, User> users = new ConcurrentHashMap<>();
|
||||||
|
private short color;
|
||||||
|
|
||||||
|
public Topic(long id, String name, String description, User host, short color) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.host = host;
|
||||||
|
this.color = color;
|
||||||
|
|
||||||
|
this.changeSupport = new PropertyChangeSupport(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
String prev = this.name;
|
||||||
|
this.name = name;
|
||||||
|
this.changeSupport.firePropertyChange("name", prev, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
String prev = this.description;
|
||||||
|
this.description = description;
|
||||||
|
this.changeSupport.firePropertyChange("description", prev, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getColor() {
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(short color) {
|
||||||
|
short prev = this.color;
|
||||||
|
this.color = color;
|
||||||
|
this.changeSupport.firePropertyChange("color", prev, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Long, User> getUsersReadOnly() {
|
||||||
|
return Collections.unmodifiableMap(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUser(User user) {
|
||||||
|
if (this.users.containsKey(user.getUserID())) return;
|
||||||
|
|
||||||
|
this.users.put(user.getUserID(), user);
|
||||||
|
this.changeSupport.firePropertyChange("userAdd", null, getUsersReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeUser(User user) {
|
||||||
|
if (!this.users.containsKey(user.getUserID())) return;
|
||||||
|
|
||||||
|
this.users.remove(user.getUserID());
|
||||||
|
this.changeSupport.firePropertyChange("userRemove", null, getUsersReadOnly());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUser(User user) {
|
||||||
|
return this.users.containsKey(user.getUserID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChangeListener(PropertyChangeListener listener) {
|
||||||
|
this.changeSupport.addPropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeChangeListener(PropertyChangeListener listener) {
|
||||||
|
this.changeSupport.removePropertyChangeListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,12 +7,12 @@ public class User {
|
|||||||
private final PropertyChangeSupport change;
|
private final PropertyChangeSupport change;
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
private int iconID;
|
private int avatarID;
|
||||||
private long userID;
|
private long userID;
|
||||||
|
|
||||||
public User(String username, int iconID, long userID) {
|
public User(String username, int avatarID, long userID) {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.iconID = iconID;
|
this.avatarID = avatarID;
|
||||||
this.userID = userID;
|
this.userID = userID;
|
||||||
|
|
||||||
this.change = new PropertyChangeSupport(this);
|
this.change = new PropertyChangeSupport(this);
|
||||||
@ -22,20 +22,24 @@ public class User {
|
|||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUsernameDisplay() {
|
||||||
|
return "@ " + getUsername();
|
||||||
|
}
|
||||||
|
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
String previous = this.username;
|
String previous = this.username;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
change.firePropertyChange("username", previous, username);
|
change.firePropertyChange("username", previous, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIconID() {
|
public int getAvatarID() {
|
||||||
return iconID;
|
return avatarID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIconID(int iconID) {
|
public void setAvatarID(int avatarID) {
|
||||||
int previous = this.iconID;
|
int previous = this.avatarID;
|
||||||
this.iconID = iconID;
|
this.avatarID = avatarID;
|
||||||
change.firePropertyChange("iconID", previous, iconID);
|
change.firePropertyChange("iconID", previous, avatarID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getUserID() {
|
public long getUserID() {
|
||||||
|
|||||||
@ -0,0 +1,72 @@
|
|||||||
|
package dev.wiing.gossip.lib.packets;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.lib.data.StringType;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class CreateTopicPacket extends Packet {
|
||||||
|
|
||||||
|
public static final short TYPE = 0x11;
|
||||||
|
public static final int LENGTH = 0x022;
|
||||||
|
|
||||||
|
private byte[] userSecret = new byte[32];
|
||||||
|
private final StringType topicName = new StringType();
|
||||||
|
private final StringType topicDescription = new StringType();
|
||||||
|
|
||||||
|
public CreateTopicPacket() {
|
||||||
|
super(TYPE, LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLength() {
|
||||||
|
setLength(
|
||||||
|
32 + // User secret
|
||||||
|
topicName.getLength() +
|
||||||
|
topicDescription.getLength()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getUserSecret() {
|
||||||
|
return userSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserSecret(byte[] userSecret) {
|
||||||
|
this.userSecret = userSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopicName() {
|
||||||
|
return topicName.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopicDescription() {
|
||||||
|
return topicDescription.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicName(String topicName) {
|
||||||
|
this.topicName.setValue(topicName);
|
||||||
|
updateLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicDescription(String topicDescription) {
|
||||||
|
this.topicDescription.setValue(topicDescription);
|
||||||
|
updateLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||||
|
CreateTopicPacket packet = new CreateTopicPacket();
|
||||||
|
packet.setLength(size);
|
||||||
|
|
||||||
|
buffer.get(packet.userSecret);
|
||||||
|
packet.topicName.setBytes(buffer);
|
||||||
|
packet.topicDescription.setBytes(buffer);
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBytes(ByteBuffer buffer) {
|
||||||
|
buffer.put(userSecret);
|
||||||
|
buffer.put(topicName.getBytes());
|
||||||
|
buffer.put(topicDescription.getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package dev.wiing.gossip.lib.packets;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class FetchUserPacket extends Packet {
|
||||||
|
|
||||||
|
public static final short TYPE = 0x21;
|
||||||
|
public static final int LENGTH = 0x008;
|
||||||
|
|
||||||
|
private long userID;
|
||||||
|
|
||||||
|
public FetchUserPacket() {
|
||||||
|
super(TYPE, LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getUserID() {
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserID(long userID) {
|
||||||
|
this.userID = userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||||
|
FetchUserPacket packet = new FetchUserPacket();
|
||||||
|
|
||||||
|
packet.userID = buffer.getLong();
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBytes(ByteBuffer buffer) {
|
||||||
|
buffer.putLong(userID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,10 +7,10 @@ import java.nio.ByteBuffer;
|
|||||||
public class RegisterPacket extends Packet {
|
public class RegisterPacket extends Packet {
|
||||||
|
|
||||||
public static final short TYPE = 0x02;
|
public static final short TYPE = 0x02;
|
||||||
public static final int SIZE = 0x0001;
|
public static final int SIZE = 0x0005;
|
||||||
|
|
||||||
private final StringType username = new StringType();
|
private final StringType username = new StringType();
|
||||||
private char iconID;
|
private byte avatarID;
|
||||||
|
|
||||||
public RegisterPacket() {
|
public RegisterPacket() {
|
||||||
super(TYPE, SIZE);
|
super(TYPE, SIZE);
|
||||||
@ -22,6 +22,7 @@ public class RegisterPacket extends Packet {
|
|||||||
packet.setLength(size);
|
packet.setLength(size);
|
||||||
|
|
||||||
packet.username.setBytes(buffer);
|
packet.username.setBytes(buffer);
|
||||||
|
packet.avatarID = buffer.get();
|
||||||
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
@ -29,24 +30,23 @@ public class RegisterPacket extends Packet {
|
|||||||
@Override
|
@Override
|
||||||
public void writeBytes(ByteBuffer buffer) {
|
public void writeBytes(ByteBuffer buffer) {
|
||||||
buffer.put(username.getBytes());
|
buffer.put(username.getBytes());
|
||||||
|
buffer.put(avatarID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username.getValue();
|
return username.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegisterPacket setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.username.setValue(username);
|
this.username.setValue(username);
|
||||||
this.setLength(1 + this.username.getLength());
|
this.setLength(1 + this.username.getLength());
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public char getIconID() {
|
public byte getAvatarID() {
|
||||||
return iconID;
|
return avatarID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegisterPacket setIconID(char iconID) {
|
public void setAvatarID(byte avatarID) {
|
||||||
this.iconID = iconID;
|
this.avatarID = avatarID;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,96 @@
|
|||||||
|
package dev.wiing.gossip.lib.packets;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.lib.data.StringType;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class TopicAddedPacket extends Packet {
|
||||||
|
|
||||||
|
public static final short TYPE = 0x12;
|
||||||
|
public static final int LENGTH = 0x014;
|
||||||
|
|
||||||
|
private long topicID;
|
||||||
|
private long hostID;
|
||||||
|
private final StringType topicName = new StringType();
|
||||||
|
private final StringType topicDescription = new StringType();
|
||||||
|
private short topicColor;
|
||||||
|
|
||||||
|
public TopicAddedPacket() {
|
||||||
|
super(TYPE, LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLength() {
|
||||||
|
setLength(
|
||||||
|
8 + // topicID
|
||||||
|
8 + // hostID
|
||||||
|
topicName.getLength() +
|
||||||
|
topicDescription.getLength() +
|
||||||
|
2 // topicColor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTopicID() {
|
||||||
|
return topicID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicID(long topicID) {
|
||||||
|
this.topicID = topicID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getHostID() {
|
||||||
|
return hostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHostID(long hostID) {
|
||||||
|
this.hostID = hostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopicName() {
|
||||||
|
return topicName.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicName(String topicName) {
|
||||||
|
this.topicName.setValue(topicName);
|
||||||
|
updateLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTopicDescription() {
|
||||||
|
return topicDescription.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicDescription(String topicDescription) {
|
||||||
|
this.topicDescription.setValue(topicDescription);
|
||||||
|
updateLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getTopicColor() {
|
||||||
|
return topicColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTopicColor(short topicColor) {
|
||||||
|
this.topicColor = topicColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||||
|
TopicAddedPacket packet = new TopicAddedPacket();
|
||||||
|
setLength(size);
|
||||||
|
|
||||||
|
packet.topicID = buffer.getLong();
|
||||||
|
packet.hostID = buffer.getLong();
|
||||||
|
packet.topicName.setBytes(buffer);
|
||||||
|
packet.topicDescription.setBytes(buffer);
|
||||||
|
packet.topicColor = buffer.getShort();
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBytes(ByteBuffer buffer) {
|
||||||
|
buffer.putLong(topicID);
|
||||||
|
buffer.putLong(hostID);
|
||||||
|
buffer.put(topicName.getBytes());
|
||||||
|
buffer.put(topicDescription.getBytes());
|
||||||
|
buffer.putShort(topicColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
package dev.wiing.gossip.lib.packets;
|
||||||
|
|
||||||
|
import dev.wiing.gossip.lib.data.StringType;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class UserDataPacket extends Packet {
|
||||||
|
|
||||||
|
public static final short TYPE = 0x22;
|
||||||
|
public static final int LENGTH = 0x00a;
|
||||||
|
|
||||||
|
private long userID;
|
||||||
|
private final StringType username = new StringType();
|
||||||
|
private byte avatarID;
|
||||||
|
|
||||||
|
public UserDataPacket() {
|
||||||
|
super(TYPE, LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getUserID() {
|
||||||
|
return userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserID(long userID) {
|
||||||
|
this.userID = userID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username.setValue(username);
|
||||||
|
setLength(9 + this.username.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getAvatarID() {
|
||||||
|
return avatarID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarID(byte avatarID) {
|
||||||
|
this.avatarID = avatarID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Packet readBytes(ByteBuffer buffer, int size) {
|
||||||
|
UserDataPacket packet = new UserDataPacket();
|
||||||
|
packet.setLength(size);
|
||||||
|
|
||||||
|
packet.userID = buffer.getLong();
|
||||||
|
packet.username.setBytes(buffer);
|
||||||
|
packet.avatarID = buffer.get();
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeBytes(ByteBuffer buffer) {
|
||||||
|
buffer.putLong(userID);
|
||||||
|
buffer.put(username.getBytes());
|
||||||
|
buffer.put(avatarID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,16 +1,23 @@
|
|||||||
package dev.wiing.gossip.server;
|
package dev.wiing.gossip.server;
|
||||||
|
|
||||||
import dev.wiing.gossip.lib.models.SecretUser;
|
import dev.wiing.gossip.lib.models.SecretUser;
|
||||||
|
import dev.wiing.gossip.lib.models.Topic;
|
||||||
import dev.wiing.gossip.lib.models.User;
|
import dev.wiing.gossip.lib.models.User;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Database {
|
public class Database {
|
||||||
|
private int userIdCounter = 1;
|
||||||
private final Map<Long, User> users = Collections.synchronizedMap(new HashMap<>());
|
private final Map<Long, User> users = Collections.synchronizedMap(new HashMap<>());
|
||||||
private final Map<byte[], User> usersBySecret = Collections.synchronizedMap(new HashMap<>());
|
private final Map<String, User> usersBySecret = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
private final Map<Long, Socket> userSockets = Collections.synchronizedMap(new HashMap<>());
|
||||||
private final Set<String> usedUsernames = Collections.synchronizedSet(new HashSet<>());
|
private final Set<String> usedUsernames = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
private int topicIdCounter = 1;
|
||||||
|
private final Map<Long, Topic> topics = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
private Database() {
|
private Database() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -21,18 +28,19 @@ public class Database {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretUser registerUser(String username, char iconID) {
|
public SecretUser registerUser(String username, byte iconID, Socket socket) {
|
||||||
SecureRandom random = new SecureRandom();
|
SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
byte[] secret = new byte[32];
|
byte[] secret = new byte[32];
|
||||||
random.nextBytes(secret);
|
random.nextBytes(secret);
|
||||||
|
|
||||||
long userID = random.nextLong();
|
long userID = userIdCounter++;
|
||||||
|
|
||||||
User user = new User(username, iconID, userID);
|
User user = new User(username, iconID, userID);
|
||||||
|
|
||||||
users.put(userID, user);
|
users.put(userID, user);
|
||||||
usersBySecret.put(secret, user);
|
userSockets.put(userID, socket);
|
||||||
|
usersBySecret.put(new String(secret), user);
|
||||||
usedUsernames.add(username);
|
usedUsernames.add(username);
|
||||||
|
|
||||||
System.out.println("Created user: " + username + " #" + Long.toUnsignedString(userID));
|
System.out.println("Created user: " + username + " #" + Long.toUnsignedString(userID));
|
||||||
@ -55,6 +63,23 @@ public class Database {
|
|||||||
return usersBySecret.getOrDefault(secret, null);
|
return usersBySecret.getOrDefault(secret, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<User> getUsers() {
|
||||||
|
return Collections.unmodifiableCollection(users.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket getUserSocket(long uID) {
|
||||||
|
return userSockets.getOrDefault(uID, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getUserOfSocket(Socket socket) {
|
||||||
|
return userSockets.entrySet().stream()
|
||||||
|
.filter(entry -> entry.getValue().equals(socket))
|
||||||
|
.findFirst()
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.map(this::getUserByID)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
public void removeUser(User user) {
|
public void removeUser(User user) {
|
||||||
users.remove(user.getUserID());
|
users.remove(user.getUserID());
|
||||||
|
|
||||||
@ -64,6 +89,36 @@ public class Database {
|
|||||||
.findFirst()
|
.findFirst()
|
||||||
.ifPresent(usersBySecret::remove);
|
.ifPresent(usersBySecret::remove);
|
||||||
|
|
||||||
|
userSockets.remove(user.getUserID());
|
||||||
|
|
||||||
usedUsernames.remove(user.getUsername());
|
usedUsernames.remove(user.getUsername());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Topic createTopic(byte[] userSecret, String topicName, String topicDescription) {
|
||||||
|
if (!usersBySecret.containsKey(new String(userSecret))) return null;
|
||||||
|
|
||||||
|
User user = usersBySecret.get(new String(userSecret));
|
||||||
|
|
||||||
|
short colorHue = (short)Math.abs((new Random().nextInt(360)));
|
||||||
|
|
||||||
|
Topic topic = new Topic(
|
||||||
|
topicIdCounter++,
|
||||||
|
topicName,
|
||||||
|
topicDescription,
|
||||||
|
user,
|
||||||
|
colorHue
|
||||||
|
);
|
||||||
|
|
||||||
|
topics.put(topic.getId(), topic);
|
||||||
|
|
||||||
|
return topic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Topic getTopic(long topicID) {
|
||||||
|
return topics.getOrDefault(topicID, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTopic(long topicID) {
|
||||||
|
topics.remove(topicID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
package dev.wiing.gossip.server;
|
package dev.wiing.gossip.server;
|
||||||
|
|
||||||
import dev.wiing.gossip.lib.models.SecretUser;
|
import dev.wiing.gossip.lib.models.SecretUser;
|
||||||
|
import dev.wiing.gossip.lib.models.Topic;
|
||||||
import dev.wiing.gossip.lib.models.User;
|
import dev.wiing.gossip.lib.models.User;
|
||||||
import dev.wiing.gossip.lib.packets.CredentialsPacket;
|
import dev.wiing.gossip.lib.packets.*;
|
||||||
import dev.wiing.gossip.lib.packets.RegisterPacket;
|
|
||||||
import dev.wiing.gossip.lib.packets.Packet;
|
|
||||||
import dev.wiing.gossip.lib.packets.TestPacket;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -31,6 +29,14 @@ public record UserSocket(Socket socket) implements Runnable {
|
|||||||
case RegisterPacket.TYPE:
|
case RegisterPacket.TYPE:
|
||||||
onRegisterUser((RegisterPacket) packet);
|
onRegisterUser((RegisterPacket) packet);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CreateTopicPacket.TYPE:
|
||||||
|
onCreateTopic((CreateTopicPacket) packet);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FetchUserPacket.TYPE:
|
||||||
|
onFetchUser((FetchUserPacket) packet);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (SocketException e) {
|
} catch (SocketException e) {
|
||||||
break;
|
break;
|
||||||
@ -38,17 +44,59 @@ public record UserSocket(Socket socket) implements Runnable {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connection lost
|
||||||
|
User user = Database.getInstance().getUserOfSocket(socket);
|
||||||
|
Database.getInstance().removeUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRegisterUser(RegisterPacket packet) {
|
private void onRegisterUser(RegisterPacket packet) {
|
||||||
SecretUser user = Database.getInstance().registerUser(packet.getUsername(), packet.getIconID());
|
SecretUser user = Database.getInstance().registerUser(packet.getUsername(), packet.getAvatarID(), packet.getSource());
|
||||||
|
|
||||||
CredentialsPacket creds = new CredentialsPacket();
|
CredentialsPacket creds = new CredentialsPacket();
|
||||||
creds.setSecret(user.getUserSecret());
|
creds.setSecret(user.getUserSecret());
|
||||||
creds.setUID(user.getUserID());
|
creds.setUID(user.getUserID());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Globals.getPacketManager().writePacket(packet.getSource().getOutputStream(), creds);
|
Globals.getPacketManager().replyPacket(packet, creds);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onCreateTopic(CreateTopicPacket packet) {
|
||||||
|
Topic topic = Database.getInstance().createTopic(packet.getUserSecret(), packet.getTopicName(), packet.getTopicDescription());
|
||||||
|
|
||||||
|
if (topic == null) return;
|
||||||
|
|
||||||
|
TopicAddedPacket added = new TopicAddedPacket();
|
||||||
|
added.setTopicID(topic.getId());
|
||||||
|
added.setHostID(topic.getHost().getUserID());
|
||||||
|
added.setTopicName(topic.getName());
|
||||||
|
added.setTopicDescription(topic.getDescription());
|
||||||
|
added.setTopicColor(topic.getColor());
|
||||||
|
|
||||||
|
for (User user : Database.getInstance().getUsers()) {
|
||||||
|
Socket socket = Database.getInstance().getUserSocket(user.getUserID());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Globals.getPacketManager().writePacket(socket.getOutputStream(), added);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onFetchUser(FetchUserPacket packet) {
|
||||||
|
User user = Database.getInstance().getUserByID(packet.getUserID());
|
||||||
|
|
||||||
|
UserDataPacket resp = new UserDataPacket();
|
||||||
|
resp.setUserID(user.getUserID());
|
||||||
|
resp.setUsername(user.getUsername());
|
||||||
|
resp.setAvatarID((byte) user.getAvatarID());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Globals.getPacketManager().replyPacket(packet, resp);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||