Code Sketch
sniper_0718
Category: Art
import scala.swing._
import scala.swing.event._
import java.awt.Color
import java.awt.Graphics2D
import java.awt.Font
import java.awt.event.MouseEvent
import java.awt.datatransfer.DataFlavor
import javax.swing.ImageIcon
import scala.collection.mutable
object KojoSocialApp {
// Global databases
private val registeredUsers = mutable.Map[String, String]()
private val userPosts = mutable.Map[String, mutable.ListBuffer[PostData]]()
// Follow requests: Map[TargetUserId, mutable.Set[RequesterId]]
private val followRequests = mutable.Map[String, mutable.Set[String]]()
// Friends / Accepted connections: Map[UserId, mutable.Set[FriendId]] (Mutual relationship helper)
private val friendsMap = mutable.Map[String, mutable.Set[String]]()
// Chat messages storage: Map[ChatPairKey, mutable.ListBuffer[ChatMessage]]
// ChatPairKey ko alphabetically sort karke banayenge taaki dono users ke liye ek hi chat history rahe.
private val chatHistories = mutable.Map[String, mutable.ListBuffer[ChatMessage]]()
case class PostData(text: String, image: ImageIcon)
case class ChatMessage(sender: String, text: String)
def main(args: Array[String]): Unit = {
// Dummy users testing ke liye
registeredUsers("user1") = "Rahul123"
registeredUsers("user2") = "Amit99"
registeredUsers("user3") = "Priya2026"
javax.swing.SwingUtilities.invokeLater(new Runnable {
def run(): Unit = {
val startFrame = new StartupSearchLoginFrame()
startFrame.visible = true
}
})
}
// 1. STARTUP SEARCH & LOGIN FRAME
class StartupSearchLoginFrame extends MainFrame {
title = "Kojo Social App - Login & Register"
resizable = false
minimumSize = new Dimension(440, 330)
centerOnScreen()
val searchIdField = new TextField(10)
val searchPasswordField = new PasswordField(10)
val searchLoginButton = new Button("Login")
val newIdField = new TextField(10)
val newPasswordField = new PasswordField(10)
val createAccountButton = new Button("Register New Account")
val messageLabel = new Label("Enter your ID & Password to login or register.") {
foreground = new Color(0, 100, 0)
font = new Font("Arial", Font.BOLD, 12)
}
contents = new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(20, 20, 20, 20)
contents += new Label("=== Welcome to Kojo Social ===") { font = new Font("Arial", Font.BOLD, 15) }
contents += Swing.VStrut(15)
contents += new Label("Already have an ID? Login here:")
contents += Swing.VStrut(5)
contents += new BoxPanel(Orientation.Horizontal) {
contents += new Label("ID: ")
contents += searchIdField
contents += Swing.HStrut(5)
contents += new Label("Pass: ")
contents += searchPasswordField
contents += Swing.HStrut(10)
contents += searchLoginButton
}
contents += Swing.VStrut(15)
contents += new Separator()
contents += Swing.VStrut(15)
contents += new Label("New User? Register with ID & Password:")
contents += Swing.VStrut(8)
contents += new BoxPanel(Orientation.Horizontal) {
contents += new Label("ID: ")
contents += newIdField
}
contents += Swing.VStrut(5)
contents += new BoxPanel(Orientation.Horizontal) {
contents += new Label("Pass: ")
contents += newPasswordField
}
contents += Swing.VStrut(12)
contents += createAccountButton
contents += Swing.VStrut(10)
contents += messageLabel
}
listenTo(searchLoginButton)
listenTo(createAccountButton)
reactions += {
case ButtonClicked(`searchLoginButton`) =>
val idToSearch = searchIdField.text.trim
val enteredPass = new String(searchPasswordField.peer.getPassword).trim
if (idToSearch.isEmpty || enteredPass.isEmpty) {
messageLabel.text = "Please enter both ID and Password!"
messageLabel.foreground = Color.RED
} else if (registeredUsers.contains(idToSearch)) {
val correctPass = registeredUsers(idToSearch)
if (correctPass == enteredPass) {
val dashboard = new FullScreenDashboard(idToSearch)
dashboard.maximize()
dashboard.visible = true
this.dispose()
} else {
messageLabel.text = "Incorrect Password!"
messageLabel.foreground = Color.RED
}
} else {
messageLabel.text = "ID not found! Please register below."
messageLabel.foreground = Color.RED
}
case ButtonClicked(`createAccountButton`) =>
val id = newIdField.text.trim
val pass = new String(newPasswordField.peer.getPassword).trim
if (id.isEmpty || pass.isEmpty) {
messageLabel.text = "Enter both ID and Password to register!"
messageLabel.foreground = Color.RED
} else if (registeredUsers.contains(id)) {
messageLabel.text = "This ID already exists! Try logging in above."
messageLabel.foreground = Color.RED
} else {
registeredUsers(id) = pass
val dashboard = new FullScreenDashboard(id)
dashboard.maximize()
dashboard.visible = true
this.dispose()
}
}
}
// 2. FULL SCREEN DASHBOARD
class FullScreenDashboard(currentUserId: String) extends MainFrame {
title = "Kojo Application - Welcome ID: " + currentUserId
minimumSize = new Dimension(1024, 728)
val backButton = new Button("<-- Back")
val searchField = new TextField(20)
val searchButton = new Button("Search ID")
val searchResultLabel = new Label("Search registered users here...") {
foreground = Color.BLUE
font = new Font("Arial", Font.BOLD, 14)
}
val topPanel = new BoxPanel(Orientation.Horizontal) {
border = Swing.EmptyBorder(15, 20, 15, 20)
contents += backButton
contents += Swing.HStrut(15)
contents += new Label("Search User ID: ") { font = new Font("Arial", Font.BOLD, 14) }
contents += Swing.HStrut(10)
contents += searchField
contents += Swing.HStrut(10)
contents += searchButton
contents += Swing.HStrut(20)
contents += searchResultLabel
}
val drawingCanvas = new Panel {
background = new Color(245, 247, 250)
var circleCenterX = 0
var circleCenterY = 0
var circleRadius = 0
override def paintComponent(g: Graphics2D): Unit = {
super.paintComponent(g)
g.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON)
val w = size.width
val h = size.height
val diameter = (Math.min(w, h) * 0.70).toInt
circleRadius = diameter / 2
circleCenterX = w / 2
circleCenterY = h / 2
val circleX = circleCenterX - circleRadius
val circleY = circleCenterY - circleRadius
// Circle Shadow
g.setColor(new Color(200, 200, 200, 120))
g.fillOval(circleX + 10, circleY + 10, diameter, diameter)
// Main Circle
g.setColor(new Color(41, 128, 185))
g.fillOval(circleX, circleY, diameter, diameter)
// Circle Border
g.setColor(new Color(21, 67, 96))
g.setStroke(new java.awt.BasicStroke(5))
g.drawOval(circleX, circleY, diameter, diameter)
// Text inside Circle
g.setColor(Color.WHITE)
g.setFont(new Font("Arial", Font.BOLD, (diameter * 0.11).toInt))
val idText = "ID: " + currentUserId
val metrics = g.getFontMetrics
val idX = circleCenterX - (metrics.stringWidth(idText) / 2)
val idY = circleCenterY + 10
g.drawString(idText, idX, idY)
// Hint text
g.setColor(Color.DARK_GRAY)
g.setFont(new Font("Arial", Font.BOLD, 16))
val hintText = "( Click anywhere on this big circle to open your profile & posts )"
val hintMetrics = g.getFontMetrics
g.drawString(hintText, circleCenterX - (hintMetrics.stringWidth(hintText) / 2), circleY + diameter + 30)
}
peer.addMouseListener(new java.awt.event.MouseAdapter {
override def mouseClicked(e: MouseEvent): Unit = {
val dx = e.getX - circleCenterX
val dy = e.getY - circleCenterY
if (dx * dx + dy * dy <= circleRadius * circleRadius) {
val profileWin = new ProfileFrame(currentUserId, currentUserId)
profileWin.visible = true
}
}
})
}
contents = new BorderPanel {
layout(topPanel) = BorderPanel.Position.North
layout(drawingCanvas) = BorderPanel.Position.Center
}
listenTo(backButton)
listenTo(searchButton)
listenTo(searchField.keys)
reactions += {
case ButtonClicked(`backButton`) =>
val loginScreen = new StartupSearchLoginFrame()
loginScreen.visible = true
this.dispose()
case ButtonClicked(`searchButton`) =>
performSearch()
case KeyPressed(_, Key.Enter, _, _) =>
performSearch()
}
def performSearch(): Unit = {
val queryId = searchField.text.trim
if (queryId.nonEmpty) {
if (registeredUsers.contains(queryId)) {
searchResultLabel.text = s"Found ID: $queryId. Opening profile..."
searchResultLabel.foreground = new Color(39, 174, 96)
val externalProfile = new ProfileFrame(queryId, currentUserId)
externalProfile.visible = true
} else {
searchResultLabel.text = "No registered user found with this ID"
searchResultLabel.foreground = Color.RED
}
} else {
searchResultLabel.text = "Please enter an ID to search!"
searchResultLabel.foreground = Color.RED
}
}
}
// 3. PROFILE WINDOW
class ProfileFrame(profileUserId: String, viewerUserId: String) extends MainFrame {
title = s"Profile Options - ID: $profileUserId"
minimumSize = new Dimension(620, 550)
centerOnScreen()
val profileBackBtn = new Button("<-- Back")
val captionLabel = new Label(s"Profile Details | ID: $profileUserId") {
font = new Font("Arial", Font.BOLD, 14)
}
val postInputField = new TextArea(3, 30) {
lineWrap = true
wordWrap = true
font = new Font("Arial", Font.PLAIN, 14)
}
var pastedImageIcon: ImageIcon = null
val imageStatusLabel = new Label("No image attached. (Copy image from Google & Paste via Ctrl+V)") {
font = new Font("Arial", Font.ITALIC, 12)
foreground = Color.GRAY
}
postInputField.peer.addKeyListener(new java.awt.event.KeyAdapter {
override def keyPressed(e: java.awt.event.KeyEvent): Unit = {
if (e.isControlDown && e.getKeyCode == java.awt.event.KeyEvent.VK_V) {
try {
val clipboard = java.awt.Toolkit.getDefaultToolkit.getSystemClipboard
if (clipboard.isDataFlavorAvailable(DataFlavor.imageFlavor)) {
val img = clipboard.getData(DataFlavor.imageFlavor).asInstanceOf[java.awt.Image]
pastedImageIcon = new ImageIcon(img.getScaledInstance(150, 150, java.awt.Image.SCALE_SMOOTH))
imageStatusLabel.text = "Image attached successfully!"
imageStatusLabel.foreground = new Color(39, 174, 96)
e.consume()
}
} catch {
case ex: Exception =>
imageStatusLabel.text = "Failed to paste image!"
imageStatusLabel.foreground = Color.RED
}
}
}
})
val postButton = new Button("Post")
// Dynamic buttons depending on whether it's own profile or someone else's profile
val actionButton = new Button()
val chatButton = new Button("? Open Chat") {
// Chat button tabhi dikhega jab dono friends honge (ya khud ki profile pe ho)
visible = false
}
def updateButtonStates(): Unit = {
if (profileUserId == viewerUserId) {
actionButton.text = "View Follow Requests"
chatButton.visible = false
} else {
val areFriends = friendsMap.getOrElse(viewerUserId, mutable.Set()).contains(profileUserId)
if (areFriends) {
actionButton.text = "Connected / Friends"
actionButton.enabled = false
chatButton.visible = true
} else {
val reqs = followRequests.getOrElse(profileUserId, mutable.Set())
if (reqs.contains(viewerUserId)) {
actionButton.text = "Request Sent"
actionButton.enabled = false
} else {
actionButton.text = "Send Follow Request"
actionButton.enabled = true
}
chatButton.visible = false
}
}
}
updateButtonStates()
val statusMessage = new Label("") {
font = new Font("Arial", Font.BOLD, 13)
foreground = new Color(0, 128, 0)
}
val postsContainer = new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(10, 10, 10, 10)
}
val scrollPane = new ScrollPane(postsContainer) {
preferredSize = new Dimension(570, 180)
}
def refreshPosts(): Unit = {
postsContainer.contents.clear()
val list = userPosts.getOrElse(profileUserId, mutable.ListBuffer())
if (list.isEmpty) {
postsContainer.contents += new Label("No posts yet.")
} else {
for (p <- list) {
val panel = new BoxPanel(Orientation.Vertical) {
border = Swing.CompoundBorder(Swing.LineBorder(Color.LIGHT_GRAY), Swing.EmptyBorder(8, 8, 8, 8))
if (p.text.nonEmpty) {
contents += new Label(p.text) { font = new Font("Arial", Font.PLAIN, 13) }
contents += Swing.VStrut(5)
}
if (p.image != null) {
contents += new Label() { icon = p.image }
}
}
postsContainer.contents += panel
postsContainer.contents += Swing.VStrut(10)
}
}
postsContainer.revalidate()
postsContainer.repaint()
}
refreshPosts()
if (profileUserId != viewerUserId) {
postInputField.editable = false
postButton.enabled = false
imageStatusLabel.text = "Viewing external profile (Posting disabled)"
}
contents = new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(15, 20, 20, 20)
contents += new BoxPanel(Orientation.Horizontal) {
contents += profileBackBtn
contents += Swing.HStrut(15)
contents += captionLabel
}
contents += Swing.VStrut(10)
contents += new Label("Write caption / Paste Google Image (Ctrl+V) below:")
contents += Swing.VStrut(5)
contents += new ScrollPane(postInputField)
contents += Swing.VStrut(3)
contents += imageStatusLabel
contents += Swing.VStrut(10)
contents += new BoxPanel(Orientation.Horizontal) {
if (profileUserId == viewerUserId) {
contents += postButton
contents += Swing.HStrut(15)
}
contents += actionButton
contents += Swing.HStrut(10)
contents += chatButton
}
contents += Swing.VStrut(10)
contents += statusMessage
contents += Swing.VStrut(5)
contents += new Label("User Posts Feed:") { font = new Font("Arial", Font.BOLD, 13) }
contents += Swing.VStrut(5)
contents += scrollPane
}
listenTo(profileBackBtn)
listenTo(postButton)
listenTo(actionButton)
listenTo(chatButton)
reactions += {
case ButtonClicked(`profileBackBtn`) =>
this.close()
case ButtonClicked(`postButton`) =>
val content = postInputField.text.trim
if (content.nonEmpty || pastedImageIcon != null) {
val newPost = PostData(content, pastedImageIcon)
userPosts.getOrElseUpdate(profileUserId, mutable.ListBuffer()) += newPost
statusMessage.text = "Successfully Posted!"
statusMessage.foreground = new Color(39, 174, 96)
postInputField.text = ""
pastedImageIcon = null
imageStatusLabel.text = "No image attached."
refreshPosts()
} else {
statusMessage.text = "Cannot post empty content!"
statusMessage.foreground = Color.RED
}
case ButtonClicked(`actionButton`) =>
if (profileUserId == viewerUserId) {
// Open Follow Requests Management Dialog/Window
val reqWindow = new FollowRequestsDialog(viewerUserId)
reqWindow.visible = true
} else {
val reqs = followRequests.getOrElseUpdate(profileUserId, mutable.Set())
if (!reqs.contains(viewerUserId)) {
reqs += viewerUserId
statusMessage.text = "Follow request sent successfully!"
statusMessage.foreground = new Color(39, 174, 96)
updateButtonStates()
}
}
case ButtonClicked(`chatButton`) =>
val chatWin = new LiveChatFrame(viewerUserId, profileUserId)
chatWin.visible = true
}
}
// 4. FOLLOW REQUESTS MANAGEMENT DIALOG (Accept / Reject)
class FollowRequestsDialog(currentUserId: String) extends MainFrame {
title = "Manage Follow Requests"
minimumSize = new Dimension(400, 300)
centerOnScreen()
val requestsPanel = new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(10, 10, 10, 10)
}
val scroll = new ScrollPane(requestsPanel)
def loadRequests(): Unit = {
requestsPanel.contents.clear()
val reqs = followRequests.getOrElse(currentUserId, mutable.Set())
if (reqs.isEmpty) {
requestsPanel.contents += new Label("No pending follow requests.") {
font = new Font("Arial", Font.BOLD, 13)
}
} else {
for (requester <- reqs) {
val row = new BoxPanel(Orientation.Horizontal) {
border = Swing.EmptyBorder(5, 5, 5, 5)
contents += new Label(s"User ID: $requester") { font = new Font("Arial", Font.BOLD, 13) }
contents += Swing.HStrut(20)
val acceptBtn = new Button("Accept")
val rejectBtn = new Button("Reject")
listenTo(acceptBtn)
listenTo(rejectBtn)
reactions += {
case ButtonClicked(`acceptBtn`) =>
// Remove from requests
reqs -= requester
// Add mutual friends
friendsMap.getOrElseUpdate(currentUserId, mutable.Set()) += requester
friendsMap.getOrElseUpdate(requester, mutable.Set()) += currentUserId
loadRequests()
case ButtonClicked(`rejectBtn`) =>
reqs -= requester
loadRequests()
}
contents += acceptBtn
contents += Swing.HStrut(5)
contents += rejectBtn
}
requestsPanel.contents += row
requestsPanel.contents += Swing.VStrut(5)
}
}
requestsPanel.revalidate()
requestsPanel.repaint()
}
loadRequests()
val closeBtn = new Button("Close")
contents = new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(15, 15, 15, 15)
contents += new Label("Pending Requests:") { font = new Font("Arial", Font.BOLD, 15) }
contents += Swing.VStrut(10)
contents += scroll
contents += Swing.VStrut(15)
contents += closeBtn
}
listenTo(closeBtn)
reactions += {
case ButtonClicked(`closeBtn`) => this.close()
}
}
// 5. LIVE CHAT WINDOW
class LiveChatFrame(userA: String, userB: String) extends MainFrame {
// Unique key generate karna taaki dono users ek hi chat room share karein
private val chatKey = if (userA < userB) s"$userA-$userB" else s"$userB-$userA"
title = s"Live Chat: $userA & $userB"
minimumSize = new Dimension(450, 450)
centerOnScreen()
val chatArea = new TextArea(15, 35) {
editable = false
lineWrap = true
wordWrap = true
font = new Font("Arial", Font.PLAIN, 13)
}
val messageInput = new TextField(25)
val sendButton = new Button("Send")
def refreshChat(): Unit = {
val messages = chatHistories.getOrElse(chatKey, mutable.ListBuffer())
val sb = new StringBuilder
for (msg <- messages) {
sb.append(s"[${msg.sender}]:${msg.text}\n")
}
chatArea.text = sb.toString()
// Scroll to bottom
chatArea.peer.setCaretPosition(chatArea.text.length)
}
refreshChat()
contents = new BoxPanel(Orientation.Vertical) {
border = Swing.EmptyBorder(15, 15, 15, 15)
contents += new Label(s"Chatting with: ${if (userA == currentUserId(userA, userB)) userB else userA}") {
font = new Font("Arial", Font.BOLD, 14)
}
contents += Swing.VStrut(10)
contents += new ScrollPane(chatArea)
contents += Swing.VStrut(10)
contents += new BoxPanel(Orientation.Horizontal) {
contents += messageInput
contents += Swing.HStrut(10)
contents += sendButton
}
}
def currentUserId(u1: String, u2: String): String = userA // Helper
listenTo(sendButton)
listenTo(messageInput.keys)
reactions += {
case ButtonClicked(`sendButton`) =>
sendMessage()
case KeyPressed(_, Key.Enter, _, _) =>
sendMessage()
}
def sendMessage(): Unit = {
val text = messageInput.text.trim
if (text.nonEmpty) {
val list = chatHistories.getOrElseUpdate(chatKey, mutable.ListBuffer())
list += ChatMessage(userA, text)
messageInput.text = ""
refreshChat()
}
}
}
}
KojoSocialApp.main(Array())