-
-
Save scan/1562731 to your computer and use it in GitHub Desktop.
Multi-room Chat server / client with Lift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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