Code Sketch


sniper_0718
By: Mhalsakant School
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 scala.collection.mutable

object KojoSocialApp {

  // Global databases
  private val registeredUsers = mutable.Map[String, String]()
  
  // Follow system databases
  private val followersMap = mutable.Map[String, mutable.Set[String]]()
  private val followingMap = mutable.Map[String, mutable.Set[String]]()
  private val followRequests = mutable.Map[String, mutable.Set[String]]()

  // Chat messages storage
  private val chatHistories = mutable.Map[String, mutable.ListBuffer[ChatMessage]]()

  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.peer.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH)
            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.peer.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH)
          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 limButton = new Button("LIM") {
      font = new Font("Arial", Font.BOLD, 14)
      foreground = new Color(41, 128, 185)
      tooltip = "Click to view logged-in user name/ID"
    }

    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
      contents += Swing.HGlue
      contents += limButton
    }

    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 )"
        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
          }
        }
      })
    }

    val logoutButton = new Button("Logout") {
      foreground = Color.RED
      font = new Font("Arial", Font.BOLD, 12)
    }

    val bottomPanel = new BoxPanel(Orientation.Horizontal) {
      border = Swing.EmptyBorder(10, 20, 15, 20)
      contents += Swing.HGlue
      contents += logoutButton
    }

    contents = new BorderPanel {
      layout(topPanel) = BorderPanel.Position.North
      layout(drawingCanvas) = BorderPanel.Position.Center
      layout(bottomPanel) = BorderPanel.Position.South
    }

    listenTo(backButton)
    listenTo(searchButton)
    listenTo(limButton)
    listenTo(logoutButton)
    listenTo(searchField.keys)

    reactions += {
      case ButtonClicked(`backButton`) =>
        val loginScreen = new StartupSearchLoginFrame()
        loginScreen.visible = true
        this.dispose()

      case ButtonClicked(`searchButton`) =>
        performSearch()

      case ButtonClicked(`limButton`) =>
        Dialog.showMessage(this, s"Currently Logged-in User ID: $currentUserId", "Logged-In Member (LIM)", Dialog.Message.Info)

      case ButtonClicked(`logoutButton`) =>
        val confirm = Dialog.showConfirmation(this, "Are you sure you want to logout?", "Confirm Logout", Dialog.Options.YesNo, Dialog.Message.Question)
        if (confirm == Dialog.Result.Yes) {
          val loginScreen = new StartupSearchLoginFrame()
          loginScreen.visible = true
          this.dispose()
        }

      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 - ID: $profileUserId"
    minimumSize = new Dimension(450, 350)
    centerOnScreen()

    val profileBackBtn = new Button("<-- Back")
    
    val captionLabel = new Label(s"User Profile | ID: $profileUserId") {
      font = new Font("Arial", Font.BOLD, 15)
    }

    val followerButton = new Button("Follower")
    val followingButton = new Button("Following")
    val requestButton = new Button("Request")
    val chatButton = new Button("Chat")

    val statusMessage = new Label("") {
      font = new Font("Arial", Font.BOLD, 13)
      foreground = new Color(0, 128, 0)
    }

    def updateButtonStates(): Unit = {
      if (profileUserId == viewerUserId) {
        requestButton.text = "View Requests"
        chatButton.enabled = false
      } else {
        val isFollowing = followingMap.getOrElse(viewerUserId, mutable.Set()).contains(profileUserId)
        val reqs = followRequests.getOrElse(profileUserId, mutable.Set())
        
        if (isFollowing) {
          requestButton.text = "Connected"
          requestButton.enabled = false
          chatButton.enabled = true
        } else if (reqs.contains(viewerUserId)) {
          requestButton.text = "Request Sent"
          requestButton.enabled = false
          chatButton.enabled = false
        } else {
          requestButton.text = "Send Request"
          requestButton.enabled = true
          chatButton.enabled = false
        }
      }
    }
    
    updateButtonStates()

    contents = new BoxPanel(Orientation.Vertical) {
      border = Swing.EmptyBorder(20, 20, 20, 20)
      
      contents += new BoxPanel(Orientation.Horizontal) {
        contents += profileBackBtn
        contents += Swing.HStrut(15)
        contents += captionLabel
      }
      
      contents += Swing.VStrut(25)
      
      contents += new BoxPanel(Orientation.Horizontal) {
        contents += followerButton
        contents += Swing.HStrut(10)
        contents += followingButton
        contents += Swing.HStrut(10)
        contents += requestButton
        contents += Swing.HStrut(10)
        contents += chatButton
      }
      
      contents += Swing.VStrut(25)
      contents += statusMessage
    }

    listenTo(profileBackBtn)
    listenTo(followerButton)
    listenTo(followingButton)
    listenTo(requestButton)
    listenTo(chatButton)

    reactions += {
      case ButtonClicked(`profileBackBtn`) =>
        this.close()

      case ButtonClicked(`followerButton`) =>
        val fWin = new ListViewerFrame(s"Followers of $profileUserId", followersMap.getOrElse(profileUserId, mutable.Set()).toSet)
        fWin.visible = true

      case ButtonClicked(`followingButton`) =>
        val fWin = new ListViewerFrame(s"Following of $profileUserId", followingMap.getOrElse(profileUserId, mutable.Set()).toSet)
        fWin.visible = true

      case ButtonClicked(`requestButton`) =>
        if (profileUserId == viewerUserId) {
          val reqWin = new FollowRequestsDialog(viewerUserId)
          reqWin.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
    }
  }

  class ListViewerFrame(titleText: String, items: Set[String]) extends MainFrame {
    title = titleText
    minimumSize = new Dimension(300, 250)
    centerOnScreen()

    val listPanel = new BoxPanel(Orientation.Vertical) {
      border = Swing.EmptyBorder(10, 10, 10, 10)
      if (items.isEmpty) {
        contents += new Label("No users found.")
      } else {
        for (item <- items) {
          contents += new Label(s"- $item") { font = new Font("Arial", Font.PLAIN, 14) }
          contents += Swing.VStrut(5)
        }
      }
    }

    contents = new ScrollPane(listPanel)
  }

  // 4. FOLLOW REQUESTS MANAGEMENT DIALOG
  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`) =>
                reqs -= requester
                followersMap.getOrElseUpdate(currentUserId, mutable.Set()) += requester
                followingMap.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 {
    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()
      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: $userB") {
        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
      }
    }

    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()
      }
    }
  }
}

// Ye line Kojo environment mein run karne ke liye zaroori hai:
KojoSocialApp.main(Array())