Skip to content

Commit

Permalink
notify tournament players anywhere on the site WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Oct 25, 2012
1 parent adcc776 commit fd85b54
Show file tree
Hide file tree
Showing 21 changed files with 151 additions and 44 deletions.
2 changes: 1 addition & 1 deletion app/controllers/Notification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ object Notification extends LilaController {

def remove(id: String) = Auth { implicit ctx
me
Ok(api.remove(me, id))
Ok(api.remove(me.id, id))
}
}
2 changes: 2 additions & 0 deletions app/core/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ final class Settings(config: Config, val IsDev: Boolean) {
val ActorMonitorHub = "monitor_hub"
val ActorTournamentHubMaster = "tournament_hub_master"
val ActorTournamentOrganizer = "tournament_organizer"
val ActorTournamentReminder = "tournament_reminder"
val ActorTournamentRegister = "tournament_register"

val ModlogCollectionModlog = getString("modlog.collection.modlog")

Expand Down
18 changes: 10 additions & 8 deletions app/notification/Api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,23 @@ import user.User
import socket.MetaHub

import collection.mutable
import play.api.templates.Html

final class Api(metaHub: MetaHub) {

private val repo = mutable.Map[String, List[Notification]]()

def add(user: User, html: String, from: Option[User] = None) {
val notification = Notification(user, html, from)
repo.update(user.id, notification :: get(user))
metaHub.addNotification(user.id, views.html.notification.view(notification).toString)
def add(userId: String, html: String, from: Option[String] = None) {
val notif = Notification(userId, html, from)
repo.update(userId, notif :: get(userId))
val rendered = views.html.notification.view(notif.id, notif.from)(Html(notif.html))
metaHub.addNotification(userId, rendered.toString)
}

def get(user: User): List[Notification] = ~(repo get user.id)
def get(userId: String): List[Notification] = ~(repo get userId)

def remove(user: User, id: String) {
repo.update(user.id, get(user) filter (_.id != id))
metaHub.removeNotification(user.id, id)
def remove(userId: String, id: String) {
repo.update(userId, get(userId) filter (_.id != id))
metaHub.removeNotification(userId, id)
}
}
8 changes: 4 additions & 4 deletions app/notification/Notification.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import ornicar.scalalib.Random.nextString

case class Notification(
id: String,
user: User,
user: String,
html: String,
from: Option[User])
from: Option[String])

object Notification {

def apply(
user: User,
user: String,
html: String,
from: Option[User]): Notification = new Notification(
from: Option[String]): Notification = new Notification(
id = nextString(8),
user = user,
html = html,
Expand Down
4 changes: 3 additions & 1 deletion app/notification/NotificationHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ trait NotificationHelper {
private def api = env.notificationApi

def notifications(user: User): Html = {
val notifs = api get user take 2 map { views.html.notification.view(_) }
val notifs = api get user.id take 2 map { notif =>
views.html.notification.view(notif.id, notif.from)(Html(notif.html))
}
notifs.foldLeft(Html(""))(_ + _)
}
}
4 changes: 3 additions & 1 deletion app/round/HubMaster.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package lila
package round

import socket.{ Broom, Close, GetNbMembers, GetUsernames, NbMembers, SendTo }
import socket.{ Broom, Close, GetNbMembers, GetUsernames, NbMembers, SendTo, SendTos }

import akka.actor._
import akka.actor.ReceiveTimeout
Expand Down Expand Up @@ -30,6 +30,8 @@ final class HubMaster(

case msg @ SendTo(_, _) hubs.values foreach (_ ! msg)

case msg @ SendTos(_, _) hubs.values foreach (_ ! msg)

case msg @ GameEvents(gameId, events) hubs get gameId foreach (_ forward msg)

case GetHub(gameId: String) sender ! {
Expand Down
10 changes: 10 additions & 0 deletions app/socket/HubActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ abstract class HubActor[M <: SocketMember](uidTimeout: Int) extends Actor {

case SendTo(userId, msg) sendTo(userId, msg)

case SendTos(userIds, msg) sendTos(userIds, msg)

case Resync(uid) resync(uid)
}

Expand Down Expand Up @@ -65,6 +67,10 @@ abstract class HubActor[M <: SocketMember](uidTimeout: Int) extends Actor {
memberByUserId(userId) foreach (_.channel push msg)
}

def sendTos(userIds: Set[String], msg: JsObject) {
membersByUserIds(userIds) foreach (_.channel push msg)
}

def broom() {
members.keys filterNot aliveUids.get foreach eject
}
Expand Down Expand Up @@ -105,6 +111,10 @@ abstract class HubActor[M <: SocketMember](uidTimeout: Int) extends Actor {
members.values find (_.userId == someId)
}

def membersByUserIds(userIds: Set[String]): Iterable[M] = {
members.values filter (_.userId.fold(userIds.contains, false))
}

def usernames: Iterable[String] = members.values.map(_.username).flatten

def notifyFen(gameId: String, fen: String, lastMove: Option[String]) {
Expand Down
1 change: 1 addition & 0 deletions app/socket/actorApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ case object Broom
case class Quit(uid: String)

case class SendTo(userId: String, message: JsObject)
case class SendTos(userIds: Set[String], message: JsObject)
case class Fen(gameId: String, fen: String, lastMove: Option[String])
case class LiveGames(uid: String, gameIds: List[String])
case class ChangeFeatured(html: String)
Expand Down
10 changes: 6 additions & 4 deletions app/tournament/HubMaster.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package lila
package tournament

import socket.{ History, Broom, Close, GetNbMembers, GetUsernames, NbMembers, SendTo, Fen }
import socket.{ History, Broom, Close, GetNbMembers, GetUsernames, NbMembers, SendTo, SendTos, Fen }

import akka.actor._
import akka.actor.ReceiveTimeout
Expand All @@ -26,11 +26,13 @@ final class HubMaster(

def receive = {

case Broom hubs.values foreach (_ ! Broom)
case Broom hubs.values foreach (_ ! Broom)

case msg @ SendTo(_, _) hubs.values foreach (_ ! msg)
case msg @ SendTo(_, _) hubs.values foreach (_ ! msg)

case Forward(id, msg) hubs get id foreach (_ ! msg)
case msg @ SendTos(_, _) hubs.values foreach (_ ! msg)

case Forward(id, msg) hubs get id foreach (_ ! msg)

case GetHub(id: String) sender ! {
(hubs get id) | {
Expand Down
13 changes: 8 additions & 5 deletions app/tournament/Organizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import game.DbGame
import round.FinishGame

import akka.actor._
import akka.actor.ReceiveTimeout
import akka.util.duration._
import akka.util.Timeout
import akka.pattern.{ ask, pipe }
import akka.dispatch.{ Future, Promise }
import play.api.libs.concurrent._
import play.api.Play.current

final class Organizer(
private[tournament] final class Organizer(
api: TournamentApi,
repo: TournamentRepo,
reminder: ActorRef,
register: ActorRef,
hubMaster: ActorRef) extends Actor {

implicit val timeout = Timeout(1 second)
implicit val executor = Akka.system.dispatcher

def receive = {

Expand Down Expand Up @@ -47,7 +47,7 @@ final class Organizer(

def createdTournament(tour: Created) {
if (tour.isEmpty) (api wipeEmpty tour).unsafePerformIO
else if (tour.readyToStart) (api start tour).unsafePerformIO
else if (tour.readyToStart) api start tour map (_.unsafePerformIO) foreach { reminder ! _ }
else (hubMaster ? GetTournamentUsernames(tour.id)).mapTo[Iterable[String]] onSuccess {
case usernames (tour.userIds diff usernames.toList.map(_.toLowerCase)) |> { leavers
leavers.map(u api.withdraw(tour, u)).sequence.unsafePerformIO
Expand All @@ -56,7 +56,10 @@ final class Organizer(
}

def startedTournaments {
repo.started.unsafePerformIO foreach startedTournament
repo.started.unsafePerformIO ~ { tours
tours foreach startedTournament
register ! SetTournaments(tours)
}
}

def startedTournament(tour: Started) {
Expand Down
23 changes: 23 additions & 0 deletions app/tournament/Register.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package lila
package tournament

import akka.actor._
import play.api.libs.concurrent._
import play.api.Play.current

private[tournament] final class Register extends Actor {

var userTournaments = Map[String, Tournament]()

def receive = {

case SetTournaments(tours) {
userTournaments = tours.foldLeft(Map[String, Tournament]()) {
case (ts, tour) ts ++ tour.userIds.map(x x -> tour)
}
print(userTournaments)
}

case GetUserTournament(userId) sender ! (userTournaments get userId)
}
}
31 changes: 31 additions & 0 deletions app/tournament/Reminder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package lila
package tournament

import akka.actor._
import akka.actor.ReceiveTimeout
import akka.util.duration._
import akka.util.Timeout
import akka.pattern.{ ask, pipe }
import akka.dispatch.{ Future, Promise }
import play.api.libs.concurrent._
import play.api.Play.current
import play.api.libs.json._

import socket.SendTos

private[tournament] final class Reminder(hubNames: List[String]) extends Actor {

lazy val hubRefs = hubNames map { name Akka.system.actorFor("/user/" + name) }

implicit val timeout = Timeout(1 second)

def receive = {
case tour: Started {
val msg = SendTos(tour.userIds.toSet, JsObject(Seq(
"t" -> JsString("tournamentReminder"),
"d" -> JsString(views.html.tournament.reminder(tour).toString)
)))
hubRefs foreach { _ ! msg }
}
}
}
2 changes: 1 addition & 1 deletion app/tournament/Tournament.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ case class Created(

private def withPlayers(s: Players) = copy(players = s)

def start = Started(id, data, DateTime.now, players, Nil)
def start = readyToStart option Started(id, data, DateTime.now, players, Nil)
}

case class Started(
Expand Down
16 changes: 9 additions & 7 deletions app/tournament/TournamentApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import play.api.libs.json._
import game.DbGame
import user.User

final class TournamentApi(
private[tournament] final class TournamentApi(
repo: TournamentRepo,
joiner: GameJoiner,
socket: Socket,
Expand Down Expand Up @@ -41,12 +41,14 @@ final class TournamentApi(
_ lobbyReload
} yield created

def start(created: Created): IO[Unit] = (for {
_ repo saveIO created.start
_ socket reload created.id
_ reloadSiteSocket
_ lobbyReload
} yield ()) doIf created.readyToStart
def start(created: Created): Option[IO[Started]] = created.start map { started
for {
_ repo saveIO started
_ socket reload started.id
_ reloadSiteSocket
_ lobbyReload
} yield started
}

def wipeEmpty(created: Created): IO[Unit] = (for {
_ repo removeIO created
Expand Down
8 changes: 8 additions & 0 deletions app/tournament/TournamentEnv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ final class TournamentEnv(
lazy val organizer = Akka.system.actorOf(Props(new Organizer(
repo = repo,
api = api,
reminder = reminder,
register = register,
hubMaster = hubMaster
)), name = ActorTournamentOrganizer)

lazy val reminder = Akka.system.actorOf(Props(new Reminder(List(
ActorLobbyHub, ActorSiteHub, ActorRoundHubMaster, ActorTournamentHubMaster
))), name = ActorTournamentReminder)

lazy val register = Akka.system.actorOf(Props(new Register), name = ActorTournamentRegister)

private lazy val joiner = new GameJoiner(
gameRepo = gameRepo,
Expand Down
2 changes: 2 additions & 0 deletions app/tournament/actorApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,5 @@ case class StartedTournament(tour: Started)
case object StartPairings
case class StartPairing(tour: Started)
case class GetTournamentUsernames(tournamentId: String)
case class GetUserTournament(userId: String)
case class SetTournaments(tours: List[Tournament])
2 changes: 2 additions & 0 deletions app/views/base/layout.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
</a>
</div>
<div class="notifications">
<div id="tournament_reminder">
</div>
@ctx.me.map(notifications(_))
</div>
<div class="content">
Expand Down
14 changes: 6 additions & 8 deletions app/views/notification/view.scala.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
@(notif: lila.notification.Notification)
@(id: String, from: Option[String] = None)(html: Html)

<div id="@notif.id" class="notification">
<a class="close" href="@routes.Notification.remove(notif.id)">X</a>
@notif.from.map { user =>
@userLink(user, none)
<div id="@id" class="notification">
<a class="close" href="@routes.Notification.remove(id)">X</a>
@from.map { user =>
@userIdLink(user, none)
}
<div class="inner">
@Html(notif.html)
</div>
<div class="inner">@html</div>
</div>
11 changes: 11 additions & 0 deletions app/views/tournament/reminder.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@(tour: lila.tournament.Tournament)

@notification.view("tournament_reminder") {
@linkTo(tour) in progress!
<form action="@routes.Tournament.join(tour.id)" method="POST">
<input type="submit" class="submit button" value="Join" />
</form>
<form action="@routes.Tournament.withdraw(tour.id)" method="POST">
<input type="submit" class="submit button strong" value="Withdraw" />
</form>
}
5 changes: 4 additions & 1 deletion public/javascripts/big.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,14 @@ var lichess = {
$('#nb_messages').text(e || "0").toggleClass("unread", e > 0);
},
notificationAdd: function(html) {
$('div.notifications ').prepend(html);
$('div.notifications').prepend(html);
},
notificationRemove: function(id) {
$('#' + id).remove();
},
tournamentReminder: function(html) {
$('#tournament_reminder').html(html).show();
},
analysisAvailable: function() {
$("div.game_analysis.status").remove();
$("div.game_analysis").show();
Expand Down
Loading

0 comments on commit fd85b54

Please sign in to comment.