Skip to content

Instantly share code, notes, and snippets.

@tjweir
Forked from everson/ChatIn.scala
Created December 12, 2011 21:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tjweir/1469275 to your computer and use it in GitHub Desktop.
Save tjweir/1469275 to your computer and use it in GitHub Desktop.
Multi-room Chat server / client with Lift
<div class="pods">
<h3><a href="#">Contact List</a></h3>
<div id="contactList">
<div class="lift:comet?type=ContactList;metaname=room">
<div id="jsBlock" style="display: none"></div>
<div>
<ul id="ul-users">
<li><strong>Jhonny</strong> MyCompany</li>
</ul>
</div>
</div>
</div>
<h3><a href="#">Chat</a></h3>
<div id="chat">
<div class="lift:comet?type=Chat;metaname=room">
<div id="chat-messages">
<ul id="ul-messages">
<li><strong>User</strong>A message</li>
</ul>
</div>
<div id="chat-input-row">
<form class="lift:form.ajax">
<input class="large lift:ChatIn" id="chat_in"/>
<input type="hidden" id="room-name" name="room" value=""/>
<button id="chat-send" type="submit" class="btn small">Send</button>
</form>
</div>
</div>
</div>
</div>
package myproject.comet
import net.liftweb._
import actor.LiftActor
import common.{Full, Empty, Box}
import http._
import js.JE._
import js.JE.JsArray._
import js.JE.JsRaw._
import js.JsCmds._
import util._
import Helpers._
import util.AnyVar._
import xml.NodeSeq
import com.foursquare.rogue.Rogue._
import net.liftweb.mongodb._
import net.liftweb.mongodb.record._
import net.liftweb.util._
import net.liftweb.common._
import net.liftweb.json.JsonAST.JString
import net.liftweb.json.JsonDSL._
import org.bson.types.ObjectId
import net.liftweb.record.LifecycleCallbacks
import net.liftweb.record._
import scala.xml._
import net.liftweb.record.field._
import net.liftweb.mongodb.record.field._
import net.liftweb.mongodb.record._
import com.foursquare.rogue.Rogue._
import net.liftweb.mongodb._
import com.mongodb.{BasicDBObject, BasicDBObjectBuilder, DBObject}
import br.com.netsar.model.{User, Room}
import scala.PartialFunction
/*
Chat server. Keep and proxy the messages to the comet actors.
*/
class ChatServer(val room: String) extends LiftActor with ListenerManager with Logger {
//class ChatServer(val room: Room) extends LiftActor with ListenerManager {
private var messages = List(("System Bot", "Welcome!"))
private var users: List[User] = Nil
def createUpdate = ChatServerUpdate(messages, users)
// processar as mensagens que chegam
override def lowPriority = {
case ChatServerMsg(user, msg) =>
info("mensagem recebida de %s" format user)
messages = (user, msg) :: messages
updateListeners(NewMsg((user, msg)))
case NewUser(user) =>
info("server novo usuário %s" format user.firstName)
users = user :: users
updateListeners(NewServerUser(user))
case UserLeft(user) => {
info("server - usuario saiu %s" format user.firstName)
info("nova lista de usuários %s" format users.map(_.firstName).toString())
users = users filterNot (u => u._id.is == user._id.is)
updateListeners(ChatServerUsers(users))
}
case _ => info("Mensagem desconhecida")
}
}
object ChatManager {
//private var rooms = Map(Room where(_.active eqs true).fetch.map(rToPair) :_*)
private var rooms = Map("default" :: Nil map (rToPair): _*)
private def rToPair(r: String) = r -> new ChatServer(r)
def default = synchronized {
rooms("default")
}
def find(room: String) = synchronized {
rooms.get(room) orElse {
val c = new ChatServer(room)
rooms += (room -> c)
//rooms += rToPair room
rooms.get(room)
}
}
}
/*
* Cliente do chat. Cada sessão do cliente tem uma instancia dele.
*/
class Chat extends CometActor with CometListener with Logger {
private var messages: List[(String, String)] = Nil
override def lifespan = Full(10 seconds)
private val user = User.currentUser.get
override def localSetup() = {
super.localSetup()
info("localSetup(%s)" format user.firstName)
//sendMessage(NewUser(user))
}
def sendMessage(msg: Any) {
ChatManager.find(name getOrElse "default").get ! msg
}
override protected def localShutdown() {
// remove from user lists if still there.
//sendMessage(UserLeft(user))
super.localShutdown
}
def registerWith = ChatManager.find(name getOrElse "default").get
override def lowPriority = {
case ChatServerUpdate(m, u) =>
info("Iniciando chat local com %s e %s" format(u.map(_.getDisplayName), m))
messages = m
reRender(false)
case NewMsg(t) =>
messages = t :: messages
partialUpdate(Call("addChatMessage", t._1, t._2))
}
def createDisplay(v: List[(String, String)]): NodeSeq = {
{
for {item <- v} yield
<li>
<strong>
{item._1}
</strong>{item._2}
</li>
}
}
def render = {
info("chat render para %s " format user.firstName.is)
"#ul-messages *" #> createDisplay(messages) &
"#room-name [value]" #> name.getOrElse("default") &
"#jsBlock *" #> Script(JsRaw("$(function() {initPods();});")
)
}
}
/*
* Contact List client. Each client session has one instance of this.
*/
class ContactList extends CometActor with CometListener with Logger {
private var users: List[User] = Nil
override def lifespan = Full(10 seconds)
private val user = User.currentUser.get
override def localSetup() = {
super.localSetup()
info("localSetup(%s)" format user.firstName)
sendMessage(NewUser(user))
}
def sendMessage(msg: Any) {
ChatManager.find(name getOrElse "default").get ! msg
}
override protected def localShutdown() {
// remove from user lists if still there.
sendMessage(UserLeft(user))
super.localShutdown
}
def registerWith = ChatManager.find(name getOrElse "default").get
override def lowPriority = {
case ChatServerUpdate(m, u) =>
info("iniciando chat local com %s e %s" format(u.map(_.firstName), m))
users = u
reRender(false)
case NewServerUser(u) =>
info("recebeu novo usuario %s" format u.firstName.is)
//if (user.id != u.id) {
users = u :: users
info("adicionou novo usuario %s" format u.firstName.is)
//partialUpdate(Call("addUser", u.firstName.is))
reRender(false)
//}
case ChatServerUsers(u) =>
info("recebeu nova lista de usuários %s" format u.map(_.firstName).toString())
users = u
reRender(false)
}
def createDisplayUsers(v: List[User]): NodeSeq = {
{
for {item <- v} yield
<li>
<strong>
{item.firstName.is}
</strong>
</li>
}
}
def render = {
val userIds = users.map(c => Str(c.id.toString)).toList
info("render para %s com %s" format(user.firstName.is, users.map(_.getDisplayName)))
"#ul-users *" #> createDisplayUsers(users) &
"#room-name [value]" #> name.getOrElse("default") &
"#jsBlock *" #> Script(JsCrVar("connectedUsers", JsArray(userIds)) &
JsRaw("$(function() {updateChatRoomUsers();});")
)
}
}
case class ChatServerUpdate(msgs: List[(String, String)], usrs: List[User])
case class ChatServerUsers(users: List[User])
case class ChatServerMsg(user: String, msg: String)
case class NewUser(user: User)
case class NewServerUser(user: User)
case class NewMsg(msg: (String, String))
case class UserLeft(user: User)
case class ChatInitialUpdate(msgs: List[(String, String)], users: List[User])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment