Code Sketch
yiiii
Category: Programming
import javax.swing._
import java.awt._
import java.awt.event._
import java.io._
import java.security.MessageDigest
import scala.collection.mutable.ArrayBuffer
import scala.jdk.CollectionConverters._
val frame = new JFrame("WhatsApp Live Chat - Kojo Edition")
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setSize(850, 650)
frame.setLocationRelativeTo(null)
case class Message(sender: String, text: String, time: String)
case class UserAccount(username: String, passwordHash: String)
val accountsFile =
new File(System.getProperty("user.home"), ".whatsapp_kojo_accounts.dat")
val messagesFile =
new File(System.getProperty("user.home"), ".whatsapp_kojo_messages.dat")
var users = ArrayBuffer.empty[UserAccount]
var messages = ArrayBuffer.empty[Message]
var loggedInUser: Option[String] = None
def hashPassword(password: String): String = {
val md = MessageDigest.getInstance("SHA-256")
md.digest(password.getBytes("UTF-8"))
.map(b => f"$b%02x")
.mkString
}
def loadData(): Unit = {
// Load accounts
try {
if (accountsFile.exists()) {
val ois = new ObjectInputStream(
new FileInputStream(accountsFile)
)
try {
val list =
ois.readObject().asInstanceOf[java.util.ArrayList[(String, String)]]
users.clear()
for ((u, p) <- list.asScala) {
users += UserAccount(u, p)
}
} finally {
ois.close()
}
}
} catch {
case _: Throwable =>
// Ignore corrupted/missing account data
}
// Load messages
try {
if (messagesFile.exists()) {
val ois = new ObjectInputStream(
new FileInputStream(messagesFile)
)
try {
val list =
ois.readObject()
.asInstanceOf[java.util.ArrayList[(String, String, String)]]
messages.clear()
for ((s, t, tm) <- list.asScala) {
messages += Message(s, t, tm)
}
} finally {
ois.close()
}
}
} catch {
case _: Throwable =>
// Ignore corrupted/missing message data
}
}
def saveData(): Unit = {
try {
val oosAcc =
new ObjectOutputStream(new FileOutputStream(accountsFile))
try {
val accList =
new java.util.ArrayList[(String, String)]()
for (u <- users) {
accList.add((u.username, u.passwordHash))
}
oosAcc.writeObject(accList)
} finally {
oosAcc.close()
}
val oosMsg =
new ObjectOutputStream(new FileOutputStream(messagesFile))
try {
val msgList =
new java.util.ArrayList[(String, String, String)]()
for (m <- messages) {
msgList.add((m.sender, m.text, m.time))
}
oosMsg.writeObject(msgList)
} finally {
oosMsg.close()
}
} catch {
case ex: Throwable =>
ex.printStackTrace()
}
}
// ------------------------------------------------------------
// Main UI
// ------------------------------------------------------------
val cardLayout = new CardLayout()
val mainPanel = new JPanel(cardLayout)
// ------------------------------------------------------------
// Login panel
// ------------------------------------------------------------
val loginPanel = new JPanel(new GridBagLayout())
loginPanel.setBackground(new Color(0, 150, 136))
val gbc = new GridBagConstraints()
gbc.insets = new Insets(10, 10, 10, 10)
gbc.gridx = 0
gbc.gridy = 0
gbc.gridwidth = 2
val titleLabel = new JLabel("WhatsApp Live Chat")
titleLabel.setFont(new Font("Arial", Font.BOLD, 26))
titleLabel.setForeground(Color.WHITE)
loginPanel.add(titleLabel, gbc)
gbc.gridwidth = 1
gbc.gridy = 1
val userLabel = new JLabel("Username:")
userLabel.setForeground(Color.WHITE)
loginPanel.add(userLabel, gbc)
gbc.gridx = 1
val userText = new JTextField(15)
loginPanel.add(userText, gbc)
gbc.gridx = 0
gbc.gridy = 2
val passLabel = new JLabel("Password:")
passLabel.setForeground(Color.WHITE)
loginPanel.add(passLabel, gbc)
gbc.gridx = 1
val passText = new JPasswordField(15)
loginPanel.add(passText, gbc)
gbc.gridx = 0
gbc.gridy = 3
val loginButton = new JButton("Login / Register")
loginPanel.add(loginButton, gbc)
gbc.gridx = 1
val changePassButton = new JButton("Change Password")
loginPanel.add(changePassButton, gbc)
val statusLabel = new JLabel("")
statusLabel.setForeground(Color.YELLOW)
gbc.gridx = 0
gbc.gridy = 4
gbc.gridwidth = 2
loginPanel.add(statusLabel, gbc)
// ------------------------------------------------------------
// Chat panel
// ------------------------------------------------------------
val chatPanel = new JPanel(new BorderLayout())
val topPanel = new JPanel(
new FlowLayout(FlowLayout.LEFT)
)
topPanel.setBackground(new Color(0, 128, 105))
val headerLabel =
new JLabel("WhatsApp Live Chat Room")
headerLabel.setFont(
new Font("Arial", Font.BOLD, 18)
)
headerLabel.setForeground(Color.WHITE)
topPanel.add(headerLabel)
val logoutButton = new JButton("Logout")
topPanel.add(logoutButton)
chatPanel.add(topPanel, BorderLayout.NORTH)
val chatArea = new JTextArea()
chatArea.setEditable(false)
chatArea.setFont(
new Font("Arial", Font.PLAIN, 14)
)
val scrollPane = new JScrollPane(chatArea)
chatPanel.add(scrollPane, BorderLayout.CENTER)
val bottomPanel = new JPanel(
new BorderLayout()
)
val messageText = new JTextField()
val sendButton = new JButton("Send")
bottomPanel.add(
messageText,
BorderLayout.CENTER
)
bottomPanel.add(
sendButton,
BorderLayout.EAST
)
chatPanel.add(
bottomPanel,
BorderLayout.SOUTH
)
// ------------------------------------------------------------
// Message display
// ------------------------------------------------------------
def refreshMessages(): Unit = {
val sb = new StringBuilder()
for (m <- messages) {
sb.append(
s"[${m.time}] ${m.sender}: ${m.text}\n"
)
}
chatArea.setText(sb.toString())
chatArea.setCaretPosition(
chatArea.getDocument.getLength
)
}
// ------------------------------------------------------------
// Login / Register
// ------------------------------------------------------------
loginButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val u = userText.getText.trim
val p = new String(passText.getPassword)
if (u.isEmpty || p.isEmpty) {
statusLabel.setText(
"Please enter username and password."
)
} else {
val hashed = hashPassword(p)
val existing =
users.find(_.username == u)
existing match {
case Some(acc) =>
if (acc.passwordHash == hashed) {
loggedInUser = Some(u)
statusLabel.setText(
"Login successful!"
)
cardLayout.show(
mainPanel,
"CHAT"
)
refreshMessages()
} else {
statusLabel.setText(
"Incorrect password."
)
}
case None =>
users += UserAccount(
u,
hashed
)
saveData()
loggedInUser = Some(u)
statusLabel.setText(
"Account created & logged in!"
)
cardLayout.show(
mainPanel,
"CHAT"
)
refreshMessages()
}
}
}
}
)
// ------------------------------------------------------------
// Change password
// ------------------------------------------------------------
changePassButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val u = userText.getText.trim
val p = new String(passText.getPassword)
if (u.isEmpty || p.isEmpty) {
statusLabel.setText(
"Enter username and new password to change."
)
} else {
val hashed = hashPassword(p)
val idx =
users.indexWhere(_.username == u)
if (idx >= 0) {
users(idx) =
UserAccount(u, hashed)
saveData()
statusLabel.setText(
"Password changed successfully!"
)
} else {
statusLabel.setText(
"User not found to change password."
)
}
}
}
}
)
// ------------------------------------------------------------
// Send message
// ------------------------------------------------------------
sendButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
val txt =
messageText.getText.trim
if (txt.nonEmpty) {
loggedInUser match {
case Some(usr) =>
val timeStr =
java.time.LocalTime
.now()
.toString
.take(8)
messages += Message(
usr,
txt,
timeStr
)
saveData()
messageText.setText("")
refreshMessages()
case None =>
// User is not logged in
}
}
}
}
)
// Press Enter to send
messageText.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
sendButton.doClick()
}
}
)
// ------------------------------------------------------------
// Logout
// ------------------------------------------------------------
logoutButton.addActionListener(
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
loggedInUser = None
cardLayout.show(
mainPanel,
"LOGIN"
)
userText.requestFocusInWindow()
}
}
)
// ------------------------------------------------------------
// Refresh messages every second
// ------------------------------------------------------------
val timer =
new javax.swing.Timer(
1000,
new ActionListener {
override def actionPerformed(
e: ActionEvent
): Unit = {
loadData()
refreshMessages()
}
}
)
timer.start()
// ------------------------------------------------------------
// Finish setup
// ------------------------------------------------------------
mainPanel.add(
loginPanel,
"LOGIN"
)
mainPanel.add(
chatPanel,
"CHAT"
)
frame.setContentPane(mainPanel)
cardLayout.show(
mainPanel,
"LOGIN"
)
loadData()
frame.setVisible(true)
SwingUtilities.invokeLater(
new Runnable {
override def run(): Unit =
userText.requestFocusInWindow()
}
)