Code Sketch
sniper_0718
Category: Art
import javax.swing._
import java.awt._
import java.awt.event._
import java.io._
import java.net._
import javax.imageio.ImageIO
import javax.swing.filechooser.FileNameExtensionFilter
import scala.collection.mutable
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import java.awt.image.BufferedImage
import javax.imageio.ImageIO
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.util.Base64
// ==========================================
// SOCKET SERVER CLASS (All Features Synced)
// ==========================================
object KojoServer {
val registeredUsers = mutable.Map[String, String]("bhimraj" -> "praxnya", "user1" -> "123", "user2" -> "456")
val onlineUsers = mutable.Set[String]()
val clientWriters = mutable.Map[String, PrintWriter]()
val chatHistories = mutable.Map[(String, String), mutable.ListBuffer[(String, String)]]()
val friendRequests = mutable.Map[String, mutable.Set[String]]()
val friendsMap = mutable.Map[String, mutable.Set[String]]()
val userPersonalPosts = mutable.Map[String, mutable.ListBuffer[String]]()
val profileImages = mutable.Map[String, String]() // Base64 encoded string sathi
def startServer(port: Int = 5000): Unit = {
Future {
try {
val serverSocket = new ServerSocket(port)
println("Kojo Server started on port " + port + "...")
while (true) {
val socket = serverSocket.accept()
new ClientHandler(socket).start()
}
} catch {
case e: Exception => println("Server already running or port busy: " + e.getMessage)
}
}
}
class ClientHandler(socket: Socket) extends Thread {
val reader = new BufferedReader(new InputStreamReader(socket.getInputStream))
val writer = new PrintWriter(socket.getOutputStream, true)
var loggedInUser: String = ""
override def run(): Unit = {
try {
var line: String = null
while ({ line = reader.readLine(); line != null }) {
val parts = line.split(":", 2)
val cmd = parts(0)
val data = if (parts.length > 1) parts(1) else ""
cmd match {
case "LOGIN" =>
val creds = data.split(",")
val id = creds(0)
val pass = creds(1)
if (registeredUsers.contains(id) && registeredUsers(id) != pass) {
writer.println("ERROR:Incorrect Password")
} else {
registeredUsers(id) = pass
loggedInUser = id
onlineUsers += id
clientWriters(id) = writer
writer.println("LOGIN_SUCCESS")
broadcastOnlineUsers()
}
case "CHAT" =>
val chatParts = data.split(",", 3)
val from = chatParts(0)
val to = chatParts(1)
val msg = chatParts(2)
val chatKey = if (from < to) (from, to) else (to, from)
chatHistories.getOrElseUpdate(chatKey, mutable.ListBuffer()) += ((from, msg))
clientWriters.get(to).foreach(_.println("CHAT_MSG:" + from + "," + to + "," + msg))
writer.println("CHAT_MSG:" + from + "," + to + "," + msg)
case "REQ" =>
val reqParts = data.split(",", 2)
val from = reqParts(0)
val to = reqParts(1)
friendRequests.getOrElseUpdate(to, mutable.Set()) += from
clientWriters.get(to).foreach(_.println("NOTIFY:" + from + " sent you a friend request!"))
clientWriters.get(to).foreach(_.println("REFRESH_DATA"))
writer.println("REQ_SENT")
case "ACCEPT_REQ" =>
val accParts = data.split(",", 2)
val me = accParts(0)
val requester = accParts(1)
friendRequests.getOrElse(me, mutable.Set()) -= requester
friendsMap.getOrElseUpdate(me, mutable.Set()) += requester
friendsMap.getOrElseUpdate(requester, mutable.Set()) += me
clientWriters.get(requester).foreach(_.println("NOTIFY:" + me + " accepted your friend request!"))
clientWriters.get(requester).foreach(_.println("REFRESH_DATA"))
writer.println("REFRESH_DATA")
case "REJECT_REQ" =>
val rejParts = data.split(",", 2)
val me = rejParts(0)
val requester = rejParts(1)
friendRequests.getOrElse(me, mutable.Set()) -= requester
clientWriters.get(requester).foreach(_.println("NOTIFY:" + me + " rejected your friend request."))
clientWriters.get(requester).foreach(_.println("REFRESH_DATA"))
writer.println("REFRESH_DATA")
case "POST" =>
val postParts = data.split(",", 2)
val user = postParts(0)
val postTxt = postParts(1)
userPersonalPosts.getOrElseUpdate(user, mutable.ListBuffer()) += ("- " + postTxt)
broadcastRefresh()
case "UPLOAD_PHOTO" =>
val photoParts = data.split(",", 2)
val user = photoParts(0)
val b64Img = photoParts(1)
profileImages(user) = b64Img
broadcastRefresh()
case "GET_ALL_DATA" =>
sendUserData(writer)
case _ =>
}
}
} catch {
case _: Throwable =>
} finally {
if (loggedInUser.nonEmpty) {
onlineUsers -= loggedInUser
clientWriters -= loggedInUser
broadcastOnlineUsers()
}
socket.close()
}
}
def broadcastOnlineUsers(): Unit = {
val usersList = onlineUsers.mkString(",")
for ((_, w) <- clientWriters) {
w.println("ONLINE_USERS:" + usersList)
}
}
def broadcastRefresh(): Unit = {
for ((_, w) <- clientWriters) {
w.println("REFRESH_DATA")
}
}
def sendUserData(w: PrintWriter): Unit = {
// Send friend requests and posts info if needed
w.println("REFRESH_DATA")
}
}
}
// ==========================================
// NETWORK CLIENT CLASS
// ==========================================
class NetworkClient(dashboard: Dashboard, serverIp: String, port: Int) {
var socket: Socket = _
var reader: BufferedReader = _
var writer: PrintWriter = _
def connect(id: String, pass: String): Boolean = {
try {
socket = new Socket(serverIp, port)
reader = new BufferedReader(new InputStreamReader(socket.getInputStream))
writer = new PrintWriter(socket.getOutputStream, true)
writer.println("LOGIN:" + id + "," + pass)
val resp = reader.readLine()
if (resp == "LOGIN_SUCCESS") {
startListening()
true
} else {
false
}
} catch {
case _: Throwable => false
}
}
def startListening(): Unit = {
Future {
try {
var line: String = null
while ({ line = reader.readLine(); line != null }) {
if (line.startsWith("ONLINE_USERS:")) {
val users = line.substring(13).split(",").filter(_.nonEmpty).toSet
SwingUtilities.invokeLater(() => dashboard.updateOnlineUsers(users))
} else if (line.startsWith("CHAT_MSG:")) {
val parts = line.substring(9).split(",", 3)
dashboard.appendChatMessage(parts(0), parts(1), parts(2))
} else if (line.startsWith("NOTIFY:")) {
val msg = line.substring(7)
SwingUtilities.invokeLater(() => JOptionPane.showMessageDialog(dashboard, msg, "Notification", JOptionPane.INFORMATION_MESSAGE))
} else if (line == "REFRESH_DATA") {
SwingUtilities.invokeLater(() => dashboard.setupRightPanel())
}
}
} catch {
case _: Throwable =>
}
}
}
def sendChat(from: String, to: String, msg: String): Unit = {
if (writer != null) writer.println("CHAT:" + from + "," + to + "," + msg)
}
def sendFriendReq(from: String, to: String): Unit = {
if (writer != null) writer.println("REQ:" + from + "," + to)
}
def acceptReq(me: String, requester: String): Unit = {
if (writer != null) writer.println("ACCEPT_REQ:" + me + "," + requester)
}
def rejectReq(me: String, requester: String): Unit = {
if (writer != null) writer.println("REJECT_REQ:" + me + "," + requester)
}
def sendPost(user: String, post: String): Unit = {
if (writer != null) writer.println("POST:" + user + "," + post)
}
def sendPhoto(user: String, b64: String): Unit = {
if (writer != null) writer.println("UPLOAD_PHOTO:" + user + "," + b64)
}
}
// ==========================================
// LOGIN FRAME
// ==========================================
class LoginFrame extends JFrame {
setTitle("Kojo Social - Login")
setResizable(false)
setSize(400, 280)
setLocationRelativeTo(null)
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
val ipField = new JTextField("127.0.0.1", 15)
val idField = new JTextField("bhimraj", 15)
val passField = new JPasswordField("praxnya", 15)
val loginButton = new JButton("Login / Register & Start Server")
val errorLabel = new JLabel("")
errorLabel.setForeground(Color.RED)
val panel = new JPanel()
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS))
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20))
val ipPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
ipPanel.add(new JLabel("Server IP:"))
ipPanel.add(ipField)
val idPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
idPanel.add(new JLabel("ID: "))
idPanel.add(idField)
val passPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
passPanel.add(new JLabel("Pass: "))
passPanel.add(passField)
panel.add(ipPanel)
panel.add(idPanel)
panel.add(passPanel)
panel.add(Box.createVerticalStrut(15))
panel.add(loginButton)
panel.add(Box.createVerticalStrut(10))
panel.add(errorLabel)
add(panel)
loginButton.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
val ip = ipField.getText.trim
val id = idField.getText.trim
val pass = new String(passField.getPassword).trim
try {
KojoServer.startServer(5000)
} catch {
case _: Throwable =>
}
Thread.sleep(300)
val dashboard = new Dashboard(id, ip)
if (dashboard.netClient.connect(id, pass)) {
dashboard.setVisible(true)
dispose()
} else {
errorLabel.setText("Connection Failed / Wrong Password!")
}
}
})
}
// ==========================================
// DASHBOARD FRAME
// ==========================================
class Dashboard(val currentUserId: String, serverIp: String) extends JFrame {
setTitle("Kojo Social Dashboard - Logged in as: " + currentUserId)
setSize(1150, 680)
setLocationRelativeTo(null)
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
setLayout(new BorderLayout())
var selectedTargetUser: String = ""
val netClient = new NetworkClient(this, serverIp, 5000)
var onlineUsersSet: Set[String] = Set()
val localChatHistories = mutable.Map[(String, String), mutable.ListBuffer[(String, String)]]()
val usersListPanel = new JPanel()
usersListPanel.setLayout(new BoxLayout(usersListPanel, BoxLayout.Y_AXIS))
val usersScroll = new JScrollPane(usersListPanel)
usersScroll.setPreferredSize(new Dimension(240, 600))
val centerPanel = new JPanel()
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS))
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
val chatTitleLbl = new JLabel("Select a user from left to Start Live Chat")
chatTitleLbl.setFont(new Font("Arial", Font.BOLD, 13))
val chatArea = new JTextArea(16, 30)
chatArea.setEditable(false)
val chatScroll = new JScrollPane(chatArea)
val msgField = new JTextField(20)
val sendMsgBtn = new JButton("Send Message")
val msgPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
msgPanel.add(msgField)
msgPanel.add(sendMsgBtn)
msgPanel.setVisible(false)
centerPanel.add(chatTitleLbl)
centerPanel.add(Box.createVerticalStrut(10))
centerPanel.add(chatScroll)
centerPanel.add(Box.createVerticalStrut(10))
centerPanel.add(msgPanel)
val rightPanel = new JPanel()
rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS))
rightPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10))
val rightScroll = new JScrollPane(rightPanel)
rightScroll.setPreferredSize(new Dimension(380, 600))
def updateOnlineUsers(users: Set[String]): Unit = {
onlineUsersSet = users
refreshOnlineUsersUI()
}
def appendChatMessage(from: String, to: String, msg: String): Unit = {
val chatKey = if (from < to) (from, to) else (to, from)
localChatHistories.getOrElseUpdate(chatKey, mutable.ListBuffer()) += ((from, msg))
if (selectedTargetUser.nonEmpty && (selectedTargetUser == from || selectedTargetUser == to)) {
reloadChatArea()
}
}
def reloadChatArea(): Unit = {
if (selectedTargetUser.nonEmpty) {
val chatKey = if (currentUserId < selectedTargetUser) (currentUserId, selectedTargetUser) else (selectedTargetUser, currentUserId)
val history = localChatHistories.getOrElse(chatKey, mutable.ListBuffer())
chatArea.setText(history.map { case (sender, m) => sender + ": " + m }.mkString("\n"))
}
}
def refreshOnlineUsersUI(): Unit = {
usersListPanel.removeAll()
val titleLbl = new JLabel("Active Users (Online):")
titleLbl.setFont(new Font("Arial", Font.BOLD, 13))
usersListPanel.add(titleLbl)
usersListPanel.add(Box.createVerticalStrut(10))
for (user <- onlineUsersSet) {
val displayName = if (user == currentUserId) user + " (You)" else user
val btn = new JButton(displayName)
btn.setHorizontalAlignment(SwingConstants.LEFT)
btn.setMaximumSize(new Dimension(220, 30))
btn.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
if (user != currentUserId) {
selectedTargetUser = user
chatTitleLbl.setText("Live Chat with: " + user)
msgPanel.setVisible(true)
reloadChatArea()
} else {
JOptionPane.showMessageDialog(usersListPanel, "This is your own profile.", "Info", JOptionPane.INFORMATION_MESSAGE)
}
}
})
usersListPanel.add(btn)
usersListPanel.add(Box.createVerticalStrut(5))
}
usersListPanel.revalidate()
usersListPanel.repaint()
}
def setupRightPanel(): Unit = {
rightPanel.removeAll()
// 0. Profile Photo & Circle Display
val profilePanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
val avatarLabel = new JLabel()
avatarLabel.setPreferredSize(new Dimension(60, 60))
KojoServer.profileImages.get(currentUserId) match {
case Some(b64) if b64.nonEmpty =>
try {
val bytes = Base64.getDecoder.decode(b64)
val bimg = ImageIO.read(new ByteArrayInputStream(bytes))
if (bimg != null) {
val dimg = bimg.getScaledInstance(60, 60, Image.SCALE_SMOOTH)
avatarLabel.setIcon(new ImageIcon(dimg))
}
} catch { case _: Throwable => }
case _ =>
avatarLabel.setText("[No Photo]")
}
profilePanel.add(avatarLabel)
val uploadPhotoBtn = new JButton("Upload Photo")
uploadPhotoBtn.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
val fileChooser = new JFileChooser()
val result = fileChooser.showOpenDialog(rightPanel)
if (result == JFileChooser.APPROVE_OPTION) {
val file = fileChooser.getSelectedFile
try {
val img = ImageIO.read(file)
if (img != null) {
val baos = new ByteArrayOutputStream()
ImageIO.write(img, "jpg", baos)
val b64Str = Base64.getEncoder.encodeToString(baos.toByteArray)
netClient.sendPhoto(currentUserId, b64Str)
JOptionPane.showMessageDialog(rightPanel, "Photo uploaded successfully!")
} else {
JOptionPane.showMessageDialog(rightPanel, "Invalid image!", "Error", JOptionPane.ERROR_MESSAGE)
}
} catch {
case _: Throwable => JOptionPane.showMessageDialog(rightPanel, "Failed to load image!", "Error", JOptionPane.ERROR_MESSAGE)
}
}
}
})
profilePanel.add(uploadPhotoBtn)
rightPanel.add(profilePanel)
rightPanel.add(Box.createVerticalStrut(10))
// 1. Friend Requests Section
val frTitle = new JLabel("Friend Requests:")
frTitle.setFont(new Font("Arial", Font.BOLD, 13))
rightPanel.add(frTitle)
rightPanel.add(Box.createVerticalStrut(5))
val reqButton = new JButton("Send Friend Request to Selected User")
reqButton.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
if (selectedTargetUser.nonEmpty && selectedTargetUser != currentUserId) {
netClient.sendFriendReq(currentUserId, selectedTargetUser)
reqButton.setText("Request Sent")
reqButton.setEnabled(false)
JOptionPane.showMessageDialog(rightPanel, "Friend request sent to " + selectedTargetUser + "!")
} else {
JOptionPane.showMessageDialog(rightPanel, "Please select another user from the left column first!")
}
}
})
rightPanel.add(reqButton)
// Incoming Requests
rightPanel.add(Box.createVerticalStrut(10))
val incomingLbl = new JLabel("Incoming Requests:")
incomingLbl.setFont(new Font("Arial", Font.BOLD, 11))
rightPanel.add(incomingLbl)
val myRecvReqs = KojoServer.friendRequests.getOrElse(currentUserId, mutable.Set())
if (myRecvReqs.isEmpty) {
rightPanel.add(new JLabel("No incoming requests."))
} else {
for (requester <- myRecvReqs) {
val reqPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
reqPanel.add(new JLabel(requester))
val acceptBtn = new JButton("Accept")
val rejectBtn = new JButton("Reject")
acceptBtn.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
netClient.acceptReq(currentUserId, requester)
JOptionPane.showMessageDialog(rightPanel, "You accepted " + requester + "'s request!")
}
})
rejectBtn.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
netClient.rejectReq(currentUserId, requester)
setupRightPanel()
}
})
reqPanel.add(acceptBtn)
reqPanel.add(rejectBtn)
rightPanel.add(reqPanel)
}
}
// Section 2: Personal Posts
rightPanel.add(Box.createVerticalStrut(15))
val myPostTitle = new JLabel("My Personal Posts:")
myPostTitle.setFont(new Font("Arial", Font.BOLD, 12))
rightPanel.add(myPostTitle)
rightPanel.add(Box.createVerticalStrut(5))
val postInput = new JTextField(18)
val addPostBtn = new JButton("Add Post")
val postInputPanel = new JPanel(new FlowLayout(FlowLayout.LEFT))
postInputPanel.add(postInput)
postInputPanel.add(addPostBtn)
rightPanel.add(postInputPanel)
val myPostsArea = new JTextArea(6, 25)
myPostsArea.setEditable(false)
val pList = KojoServer.userPersonalPosts.getOrElse(currentUserId, mutable.ListBuffer())
myPostsArea.setText(pList.mkString("\n"))
addPostBtn.addActionListener(new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
val postTxt = postInput.getText.trim
if (postTxt.nonEmpty) {
netClient.sendPost(currentUserId, postTxt)
postInput.setText("")
}
}
})
rightPanel.add(Box.createVerticalStrut(5))
rightPanel.add(new JScrollPane(myPostsArea))
rightPanel.revalidate()
rightPanel.repaint()
}
setupRightPanel()
val sendAction = new ActionListener {
override def actionPerformed(e: ActionEvent): Unit = {
val text = msgField.getText.trim
if (text.nonEmpty && selectedTargetUser.nonEmpty) {
netClient.sendChat(currentUserId, selectedTargetUser, text)
msgField.setText("")
}
}
}
sendMsgBtn.addActionListener(sendAction)
msgField.addActionListener(sendAction)
add(usersScroll, BorderLayout.WEST)
add(centerPanel, BorderLayout.CENTER)
add(rightScroll, BorderLayout.EAST)
}
// Start Login Window
SwingUtilities.invokeLater(new Runnable {
def run(): Unit = {
new LoginFrame().setVisible(true)
}
})